summaryrefslogtreecommitdiff
path: root/libmate-desktop/mate-rr.c
diff options
context:
space:
mode:
Diffstat (limited to 'libmate-desktop/mate-rr.c')
-rw-r--r--libmate-desktop/mate-rr.c1824
1 files changed, 1824 insertions, 0 deletions
diff --git a/libmate-desktop/mate-rr.c b/libmate-desktop/mate-rr.c
new file mode 100644
index 0000000..5c73ce9
--- /dev/null
+++ b/libmate-desktop/mate-rr.c
@@ -0,0 +1,1824 @@
+/* mate-rr.c
+ *
+ * Copyright 2007, 2008, Red Hat, Inc.
+ *
+ * This file is part of the Mate Library.
+ *
+ * The Mate Library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or (at your option) any later version.
+ *
+ * The Mate Library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with the Mate Library; see the file COPYING.LIB. If not,
+ * write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ *
+ * Author: Soren Sandmann <[email protected]>
+ */
+
+#define MATE_DESKTOP_USE_UNSTABLE_API
+
+#include <config.h>
+#include <glib/gi18n-lib.h>
+#include <string.h>
+#include <X11/Xlib.h>
+
+#ifdef HAVE_RANDR
+#include <X11/extensions/Xrandr.h>
+#endif
+
+#include <gtk/gtk.h>
+#include <gdk/gdkx.h>
+#include <X11/Xatom.h>
+
+#undef MATE_DISABLE_DEPRECATED
+#include "libmateui/mate-rr.h"
+
+#include "private.h"
+#include "mate-rr-private.h"
+
+#define DISPLAY(o) ((o)->info->screen->xdisplay)
+
+#ifndef HAVE_RANDR
+/* This is to avoid a ton of ifdefs wherever we use a type from libXrandr */
+typedef int RROutput;
+typedef int RRCrtc;
+typedef int RRMode;
+typedef int Rotation;
+#define RR_Rotate_0 1
+#define RR_Rotate_90 2
+#define RR_Rotate_180 4
+#define RR_Rotate_270 8
+#define RR_Reflect_X 16
+#define RR_Reflect_Y 32
+#endif
+
+struct MateRROutput
+{
+ ScreenInfo * info;
+ RROutput id;
+
+ char * name;
+ MateRRCrtc * current_crtc;
+ gboolean connected;
+ gulong width_mm;
+ gulong height_mm;
+ MateRRCrtc ** possible_crtcs;
+ MateRROutput ** clones;
+ MateRRMode ** modes;
+ int n_preferred;
+ guint8 * edid_data;
+ char * connector_type;
+};
+
+struct MateRROutputWrap
+{
+ RROutput id;
+};
+
+struct MateRRCrtc
+{
+ ScreenInfo * info;
+ RRCrtc id;
+
+ MateRRMode * current_mode;
+ MateRROutput ** current_outputs;
+ MateRROutput ** possible_outputs;
+ int x;
+ int y;
+
+ MateRRRotation current_rotation;
+ MateRRRotation rotations;
+ int gamma_size;
+};
+
+struct MateRRMode
+{
+ ScreenInfo * info;
+ RRMode id;
+ char * name;
+ int width;
+ int height;
+ int freq; /* in mHz */
+};
+
+/* MateRRCrtc */
+static MateRRCrtc * crtc_new (ScreenInfo *info,
+ RRCrtc id);
+static void crtc_free (MateRRCrtc *crtc);
+
+#ifdef HAVE_RANDR
+static gboolean crtc_initialize (MateRRCrtc *crtc,
+ XRRScreenResources *res,
+ GError **error);
+#endif
+
+/* MateRROutput */
+static MateRROutput *output_new (ScreenInfo *info,
+ RROutput id);
+
+#ifdef HAVE_RANDR
+static gboolean output_initialize (MateRROutput *output,
+ XRRScreenResources *res,
+ GError **error);
+#endif
+
+static void output_free (MateRROutput *output);
+
+/* MateRRMode */
+static MateRRMode * mode_new (ScreenInfo *info,
+ RRMode id);
+
+#ifdef HAVE_RANDR
+static void mode_initialize (MateRRMode *mode,
+ XRRModeInfo *info);
+#endif
+
+static void mode_free (MateRRMode *mode);
+
+
+/* Errors */
+
+/**
+ * mate_rr_error_quark:
+ *
+ * Returns the #GQuark that will be used for #GError values returned by the
+ * MateRR API.
+ *
+ * Return value: a #GQuark used to identify errors coming from the MateRR API.
+ */
+GQuark
+mate_rr_error_quark (void)
+{
+ return g_quark_from_static_string ("mate-rr-error-quark");
+}
+
+/* Screen */
+static MateRROutput *
+mate_rr_output_by_id (ScreenInfo *info, RROutput id)
+{
+ MateRROutput **output;
+
+ g_assert (info != NULL);
+
+ for (output = info->outputs; *output; ++output)
+ {
+ if ((*output)->id == id)
+ return *output;
+ }
+
+ return NULL;
+}
+
+static MateRRCrtc *
+crtc_by_id (ScreenInfo *info, RRCrtc id)
+{
+ MateRRCrtc **crtc;
+
+ if (!info)
+ return NULL;
+
+ for (crtc = info->crtcs; *crtc; ++crtc)
+ {
+ if ((*crtc)->id == id)
+ return *crtc;
+ }
+
+ return NULL;
+}
+
+static MateRRMode *
+mode_by_id (ScreenInfo *info, RRMode id)
+{
+ MateRRMode **mode;
+
+ g_assert (info != NULL);
+
+ for (mode = info->modes; *mode; ++mode)
+ {
+ if ((*mode)->id == id)
+ return *mode;
+ }
+
+ return NULL;
+}
+
+static void
+screen_info_free (ScreenInfo *info)
+{
+ MateRROutput **output;
+ MateRRCrtc **crtc;
+ MateRRMode **mode;
+
+ g_assert (info != NULL);
+
+#ifdef HAVE_RANDR
+ if (info->resources)
+ {
+ XRRFreeScreenResources (info->resources);
+
+ info->resources = NULL;
+ }
+#endif
+
+ if (info->outputs)
+ {
+ for (output = info->outputs; *output; ++output)
+ output_free (*output);
+ g_free (info->outputs);
+ }
+
+ if (info->crtcs)
+ {
+ for (crtc = info->crtcs; *crtc; ++crtc)
+ crtc_free (*crtc);
+ g_free (info->crtcs);
+ }
+
+ if (info->modes)
+ {
+ for (mode = info->modes; *mode; ++mode)
+ mode_free (*mode);
+ g_free (info->modes);
+ }
+
+ if (info->clone_modes)
+ {
+ /* The modes themselves were freed above */
+ g_free (info->clone_modes);
+ }
+
+ g_free (info);
+}
+
+static gboolean
+has_similar_mode (MateRROutput *output, MateRRMode *mode)
+{
+ int i;
+ MateRRMode **modes = mate_rr_output_list_modes (output);
+ int width = mate_rr_mode_get_width (mode);
+ int height = mate_rr_mode_get_height (mode);
+
+ for (i = 0; modes[i] != NULL; ++i)
+ {
+ MateRRMode *m = modes[i];
+
+ if (mate_rr_mode_get_width (m) == width &&
+ mate_rr_mode_get_height (m) == height)
+ {
+ return TRUE;
+ }
+ }
+
+ return FALSE;
+}
+
+static void
+gather_clone_modes (ScreenInfo *info)
+{
+ int i;
+ GPtrArray *result = g_ptr_array_new ();
+
+ for (i = 0; info->outputs[i] != NULL; ++i)
+ {
+ int j;
+ MateRROutput *output1, *output2;
+
+ output1 = info->outputs[i];
+
+ if (!output1->connected)
+ continue;
+
+ for (j = 0; output1->modes[j] != NULL; ++j)
+ {
+ MateRRMode *mode = output1->modes[j];
+ gboolean valid;
+ int k;
+
+ valid = TRUE;
+ for (k = 0; info->outputs[k] != NULL; ++k)
+ {
+ output2 = info->outputs[k];
+
+ if (!output2->connected)
+ continue;
+
+ if (!has_similar_mode (output2, mode))
+ {
+ valid = FALSE;
+ break;
+ }
+ }
+
+ if (valid)
+ g_ptr_array_add (result, mode);
+ }
+ }
+
+ g_ptr_array_add (result, NULL);
+
+ info->clone_modes = (MateRRMode **)g_ptr_array_free (result, FALSE);
+}
+
+#ifdef HAVE_RANDR
+static gboolean
+fill_screen_info_from_resources (ScreenInfo *info,
+ XRRScreenResources *resources,
+ GError **error)
+{
+ int i;
+ GPtrArray *a;
+ MateRRCrtc **crtc;
+ MateRROutput **output;
+
+ info->resources = resources;
+
+ /* We create all the structures before initializing them, so
+ * that they can refer to each other.
+ */
+ a = g_ptr_array_new ();
+ for (i = 0; i < resources->ncrtc; ++i)
+ {
+ MateRRCrtc *crtc = crtc_new (info, resources->crtcs[i]);
+
+ g_ptr_array_add (a, crtc);
+ }
+ g_ptr_array_add (a, NULL);
+ info->crtcs = (MateRRCrtc **)g_ptr_array_free (a, FALSE);
+
+ a = g_ptr_array_new ();
+ for (i = 0; i < resources->noutput; ++i)
+ {
+ MateRROutput *output = output_new (info, resources->outputs[i]);
+
+ g_ptr_array_add (a, output);
+ }
+ g_ptr_array_add (a, NULL);
+ info->outputs = (MateRROutput **)g_ptr_array_free (a, FALSE);
+
+ a = g_ptr_array_new ();
+ for (i = 0; i < resources->nmode; ++i)
+ {
+ MateRRMode *mode = mode_new (info, resources->modes[i].id);
+
+ g_ptr_array_add (a, mode);
+ }
+ g_ptr_array_add (a, NULL);
+ info->modes = (MateRRMode **)g_ptr_array_free (a, FALSE);
+
+ /* Initialize */
+ for (crtc = info->crtcs; *crtc; ++crtc)
+ {
+ if (!crtc_initialize (*crtc, resources, error))
+ return FALSE;
+ }
+
+ for (output = info->outputs; *output; ++output)
+ {
+ if (!output_initialize (*output, resources, error))
+ return FALSE;
+ }
+
+ for (i = 0; i < resources->nmode; ++i)
+ {
+ MateRRMode *mode = mode_by_id (info, resources->modes[i].id);
+
+ mode_initialize (mode, &(resources->modes[i]));
+ }
+
+ gather_clone_modes (info);
+
+ return TRUE;
+}
+#endif /* HAVE_RANDR */
+
+static gboolean
+fill_out_screen_info (Display *xdisplay,
+ Window xroot,
+ ScreenInfo *info,
+ gboolean needs_reprobe,
+ GError **error)
+{
+#ifdef HAVE_RANDR
+ XRRScreenResources *resources;
+
+ g_assert (xdisplay != NULL);
+ g_assert (info != NULL);
+
+ /* First update the screen resources */
+
+ if (needs_reprobe)
+ resources = XRRGetScreenResources (xdisplay, xroot);
+ else
+ {
+ /* XRRGetScreenResourcesCurrent is less expensive than
+ * XRRGetScreenResources, however it is available only
+ * in RandR 1.3 or higher
+ */
+#if (RANDR_MAJOR > 1 || (RANDR_MAJOR == 1 && RANDR_MINOR >= 3))
+ /* Runtime check for RandR 1.3 or higher */
+ if (info->screen->rr_major_version == 1 && info->screen->rr_minor_version >= 3)
+ resources = XRRGetScreenResourcesCurrent (xdisplay, xroot);
+ else
+ resources = XRRGetScreenResources (xdisplay, xroot);
+#else
+ resources = XRRGetScreenResources (xdisplay, xroot);
+#endif
+ }
+
+ if (resources)
+ {
+ if (!fill_screen_info_from_resources (info, resources, error))
+ return FALSE;
+ }
+ else
+ {
+ g_set_error (error, MATE_RR_ERROR, MATE_RR_ERROR_RANDR_ERROR,
+ /* Translators: a CRTC is a CRT Controller (this is X terminology). */
+ _("could not get the screen resources (CRTCs, outputs, modes)"));
+ return FALSE;
+ }
+
+ /* Then update the screen size range. We do this after XRRGetScreenResources() so that
+ * the X server will already have an updated view of the outputs.
+ */
+
+ if (needs_reprobe) {
+ gboolean success;
+
+ gdk_error_trap_push ();
+ success = XRRGetScreenSizeRange (xdisplay, xroot,
+ &(info->min_width),
+ &(info->min_height),
+ &(info->max_width),
+ &(info->max_height));
+ gdk_flush ();
+ if (gdk_error_trap_pop ()) {
+ g_set_error (error, MATE_RR_ERROR, MATE_RR_ERROR_UNKNOWN,
+ _("unhandled X error while getting the range of screen sizes"));
+ return FALSE;
+ }
+
+ if (!success) {
+ g_set_error (error, MATE_RR_ERROR, MATE_RR_ERROR_RANDR_ERROR,
+ _("could not get the range of screen sizes"));
+ return FALSE;
+ }
+ }
+ else
+ {
+ mate_rr_screen_get_ranges (info->screen,
+ &(info->min_width),
+ &(info->max_width),
+ &(info->min_height),
+ &(info->max_height));
+ }
+
+ info->primary = None;
+#if (RANDR_MAJOR > 1 || (RANDR_MAJOR == 1 && RANDR_MINOR >= 3))
+ /* Runtime check for RandR 1.3 or higher */
+ if (info->screen->rr_major_version == 1 && info->screen->rr_minor_version >= 3) {
+ gdk_error_trap_push ();
+ info->primary = XRRGetOutputPrimary (xdisplay, xroot);
+ gdk_flush ();
+ gdk_error_trap_pop (); /* ignore error */
+ }
+#endif
+
+ return TRUE;
+#else
+ return FALSE;
+#endif /* HAVE_RANDR */
+}
+
+static ScreenInfo *
+screen_info_new (MateRRScreen *screen, gboolean needs_reprobe, GError **error)
+{
+ ScreenInfo *info = g_new0 (ScreenInfo, 1);
+
+ g_assert (screen != NULL);
+
+ info->outputs = NULL;
+ info->crtcs = NULL;
+ info->modes = NULL;
+ info->screen = screen;
+
+ if (fill_out_screen_info (screen->xdisplay, screen->xroot, info, needs_reprobe, error))
+ {
+ return info;
+ }
+ else
+ {
+ screen_info_free (info);
+ return NULL;
+ }
+}
+
+static gboolean
+screen_update (MateRRScreen *screen, gboolean force_callback, gboolean needs_reprobe, GError **error)
+{
+ ScreenInfo *info;
+ gboolean changed = FALSE;
+
+ g_assert (screen != NULL);
+
+ info = screen_info_new (screen, needs_reprobe, error);
+ if (!info)
+ return FALSE;
+
+#ifdef HAVE_RANDR
+ if (info->resources->configTimestamp != screen->info->resources->configTimestamp)
+ changed = TRUE;
+#endif
+
+ screen_info_free (screen->info);
+
+ screen->info = info;
+
+ if ((changed || force_callback) && screen->callback)
+ screen->callback (screen, screen->data);
+
+ return changed;
+}
+
+static GdkFilterReturn
+screen_on_event (GdkXEvent *xevent,
+ GdkEvent *event,
+ gpointer data)
+{
+#ifdef HAVE_RANDR
+ MateRRScreen *screen = data;
+ XEvent *e = xevent;
+ int event_num;
+
+ if (!e)
+ return GDK_FILTER_CONTINUE;
+
+ event_num = e->type - screen->randr_event_base;
+
+ if (event_num == RRScreenChangeNotify) {
+ /* We don't reprobe the hardware; we just fetch the X server's latest
+ * state. The server already knows the new state of the outputs; that's
+ * why it sent us an event!
+ */
+ screen_update (screen, TRUE, FALSE, NULL); /* NULL-GError */
+#if 0
+ /* Enable this code to get a dialog showing the RANDR timestamps, for debugging purposes */
+ {
+ GtkWidget *dialog;
+ XRRScreenChangeNotifyEvent *rr_event;
+ static int dialog_num;
+
+ rr_event = (XRRScreenChangeNotifyEvent *) e;
+
+ dialog = gtk_message_dialog_new (NULL,
+ 0,
+ GTK_MESSAGE_INFO,
+ GTK_BUTTONS_CLOSE,
+ "RRScreenChangeNotify timestamps (%d):\n"
+ "event change: %u\n"
+ "event config: %u\n"
+ "event serial: %lu\n"
+ "----------------------"
+ "screen change: %u\n"
+ "screen config: %u\n",
+ dialog_num++,
+ (guint32) rr_event->timestamp,
+ (guint32) rr_event->config_timestamp,
+ rr_event->serial,
+ (guint32) screen->info->resources->timestamp,
+ (guint32) screen->info->resources->configTimestamp);
+ g_signal_connect (dialog, "response",
+ G_CALLBACK (gtk_widget_destroy), NULL);
+ gtk_widget_show (dialog);
+ }
+#endif
+ }
+#if 0
+ /* WHY THIS CODE IS DISABLED:
+ *
+ * Note that in mate_rr_screen_new(), we only select for
+ * RRScreenChangeNotifyMask. We used to select for other values in
+ * RR*NotifyMask, but we weren't really doing anything useful with those
+ * events. We only care about "the screens changed in some way or another"
+ * for now.
+ *
+ * If we ever run into a situtation that could benefit from processing more
+ * detailed events, we can enable this code again.
+ *
+ * Note that the X server sends RRScreenChangeNotify in conjunction with the
+ * more detailed events from RANDR 1.2 - see xserver/randr/randr.c:TellChanged().
+ */
+ else if (event_num == RRNotify)
+ {
+ /* Other RandR events */
+
+ XRRNotifyEvent *event = (XRRNotifyEvent *)e;
+
+ /* Here we can distinguish between RRNotify events supported
+ * since RandR 1.2 such as RRNotify_OutputProperty. For now, we
+ * don't have anything special to do for particular subevent types, so
+ * we leave this as an empty switch().
+ */
+ switch (event->subtype)
+ {
+ default:
+ break;
+ }
+
+ /* No need to reprobe hardware here */
+ screen_update (screen, TRUE, FALSE, NULL); /* NULL-GError */
+ }
+#endif
+
+#endif /* HAVE_RANDR */
+
+ /* Pass the event on to GTK+ */
+ return GDK_FILTER_CONTINUE;
+}
+
+/* Returns NULL if screen could not be created. For instance, if
+ * the driver does not support Xrandr 1.2.
+ */
+MateRRScreen *
+mate_rr_screen_new (GdkScreen *gdk_screen,
+ MateRRScreenChanged callback,
+ gpointer data,
+ GError **error)
+{
+#ifdef HAVE_RANDR
+ Display *dpy = GDK_SCREEN_XDISPLAY (gdk_screen);
+ int event_base;
+ int ignore;
+#endif
+
+ g_return_val_if_fail (error == NULL || *error == NULL, NULL);
+
+ _mate_desktop_init_i18n ();
+
+#ifdef HAVE_RANDR
+ if (XRRQueryExtension (dpy, &event_base, &ignore))
+ {
+ MateRRScreen *screen = g_new0 (MateRRScreen, 1);
+
+ screen->gdk_screen = gdk_screen;
+ screen->gdk_root = gdk_screen_get_root_window (gdk_screen);
+ screen->xroot = gdk_x11_drawable_get_xid (screen->gdk_root);
+ screen->xdisplay = dpy;
+ screen->xscreen = gdk_x11_screen_get_xscreen (screen->gdk_screen);
+ screen->connector_type_atom = XInternAtom (dpy, "ConnectorType", FALSE);
+
+ screen->callback = callback;
+ screen->data = data;
+
+ screen->randr_event_base = event_base;
+
+ XRRQueryVersion (dpy, &screen->rr_major_version, &screen->rr_minor_version);
+ if (screen->rr_major_version > 1 || (screen->rr_major_version == 1 && screen->rr_minor_version < 2)) {
+ g_set_error (error, MATE_RR_ERROR, MATE_RR_ERROR_NO_RANDR_EXTENSION,
+ "RANDR extension is too old (must be at least 1.2)");
+ g_free (screen);
+ return NULL;
+ }
+
+ screen->info = screen_info_new (screen, TRUE, error);
+
+ if (!screen->info) {
+ g_free (screen);
+ return NULL;
+ }
+
+ if (screen->callback) {
+ XRRSelectInput (screen->xdisplay,
+ screen->xroot,
+ RRScreenChangeNotifyMask);
+
+ gdk_x11_register_standard_event_type (gdk_screen_get_display (gdk_screen),
+ event_base,
+ RRNotify + 1);
+
+ gdk_window_add_filter (screen->gdk_root, screen_on_event, screen);
+ }
+
+ return screen;
+ }
+ else
+ {
+#endif /* HAVE_RANDR */
+ g_set_error (error, MATE_RR_ERROR, MATE_RR_ERROR_NO_RANDR_EXTENSION,
+ _("RANDR extension is not present"));
+
+ return NULL;
+#ifdef HAVE_RANDR
+ }
+#endif
+}
+
+void
+mate_rr_screen_destroy (MateRRScreen *screen)
+{
+ g_return_if_fail (screen != NULL);
+
+ gdk_window_remove_filter (screen->gdk_root, screen_on_event, screen);
+
+ screen_info_free (screen->info);
+ screen->info = NULL;
+
+ g_free (screen);
+}
+
+void
+mate_rr_screen_set_size (MateRRScreen *screen,
+ int width,
+ int height,
+ int mm_width,
+ int mm_height)
+{
+ g_return_if_fail (screen != NULL);
+
+#ifdef HAVE_RANDR
+ gdk_error_trap_push ();
+ XRRSetScreenSize (screen->xdisplay, screen->xroot,
+ width, height, mm_width, mm_height);
+ gdk_flush ();
+ gdk_error_trap_pop (); /* ignore error */
+#endif
+}
+
+void
+mate_rr_screen_get_ranges (MateRRScreen *screen,
+ int *min_width,
+ int *max_width,
+ int *min_height,
+ int *max_height)
+{
+ g_return_if_fail (screen != NULL);
+
+ if (min_width)
+ *min_width = screen->info->min_width;
+
+ if (max_width)
+ *max_width = screen->info->max_width;
+
+ if (min_height)
+ *min_height = screen->info->min_height;
+
+ if (max_height)
+ *max_height = screen->info->max_height;
+}
+
+/**
+ * mate_rr_screen_get_timestamps
+ * @screen: a #MateRRScreen
+ * @change_timestamp_ret: Location in which to store the timestamp at which the RANDR configuration was last changed
+ * @config_timestamp_ret: Location in which to store the timestamp at which the RANDR configuration was last obtained
+ *
+ * Queries the two timestamps that the X RANDR extension maintains. The X
+ * server will prevent change requests for stale configurations, those whose
+ * timestamp is not equal to that of the latest request for configuration. The
+ * X server will also prevent change requests that have an older timestamp to
+ * the latest change request.
+ */
+void
+mate_rr_screen_get_timestamps (MateRRScreen *screen,
+ guint32 *change_timestamp_ret,
+ guint32 *config_timestamp_ret)
+{
+ g_return_if_fail (screen != NULL);
+
+#ifdef HAVE_RANDR
+ if (change_timestamp_ret)
+ *change_timestamp_ret = screen->info->resources->timestamp;
+
+ if (config_timestamp_ret)
+ *config_timestamp_ret = screen->info->resources->configTimestamp;
+#endif
+}
+
+static gboolean
+force_timestamp_update (MateRRScreen *screen)
+{
+ MateRRCrtc *crtc;
+ XRRCrtcInfo *current_info;
+ Status status;
+ gboolean timestamp_updated;
+
+ timestamp_updated = FALSE;
+
+ crtc = screen->info->crtcs[0];
+
+ if (crtc == NULL)
+ goto out;
+
+ current_info = XRRGetCrtcInfo (screen->xdisplay,
+ screen->info->resources,
+ crtc->id);
+
+ if (current_info == NULL)
+ goto out;
+
+ gdk_error_trap_push ();
+ status = XRRSetCrtcConfig (screen->xdisplay,
+ screen->info->resources,
+ crtc->id,
+ current_info->timestamp,
+ current_info->x,
+ current_info->y,
+ current_info->mode,
+ current_info->rotation,
+ current_info->outputs,
+ current_info->noutput);
+
+ XRRFreeCrtcInfo (current_info);
+
+ gdk_flush ();
+ if (gdk_error_trap_pop ())
+ goto out;
+
+ if (status == RRSetConfigSuccess)
+ timestamp_updated = TRUE;
+out:
+ return timestamp_updated;
+}
+
+/**
+ * mate_rr_screen_refresh
+ * @screen: a #MateRRScreen
+ * @error: location to store error, or %NULL
+ *
+ * Refreshes the screen configuration, and calls the screen's callback if it
+ * exists and if the screen's configuration changed.
+ *
+ * Return value: TRUE if the screen's configuration changed; otherwise, the
+ * function returns FALSE and a NULL error if the configuration didn't change,
+ * or FALSE and a non-NULL error if there was an error while refreshing the
+ * configuration.
+ */
+gboolean
+mate_rr_screen_refresh (MateRRScreen *screen,
+ GError **error)
+{
+ gboolean refreshed;
+
+ g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
+
+ gdk_x11_display_grab (gdk_screen_get_display (screen->gdk_screen));
+
+ refreshed = screen_update (screen, FALSE, TRUE, error);
+ force_timestamp_update (screen); /* this is to keep other clients from thinking that the X server re-detected things by itself - bgo#621046 */
+
+ gdk_x11_display_ungrab (gdk_screen_get_display (screen->gdk_screen));
+
+ return refreshed;
+}
+
+MateRRMode **
+mate_rr_screen_list_modes (MateRRScreen *screen)
+{
+ g_return_val_if_fail (screen != NULL, NULL);
+ g_return_val_if_fail (screen->info != NULL, NULL);
+
+ return screen->info->modes;
+}
+
+MateRRMode **
+mate_rr_screen_list_clone_modes (MateRRScreen *screen)
+{
+ g_return_val_if_fail (screen != NULL, NULL);
+ g_return_val_if_fail (screen->info != NULL, NULL);
+
+ return screen->info->clone_modes;
+}
+
+MateRRCrtc **
+mate_rr_screen_list_crtcs (MateRRScreen *screen)
+{
+ g_return_val_if_fail (screen != NULL, NULL);
+ g_return_val_if_fail (screen->info != NULL, NULL);
+
+ return screen->info->crtcs;
+}
+
+MateRROutput **
+mate_rr_screen_list_outputs (MateRRScreen *screen)
+{
+ g_return_val_if_fail (screen != NULL, NULL);
+ g_return_val_if_fail (screen->info != NULL, NULL);
+
+ return screen->info->outputs;
+}
+
+MateRRCrtc *
+mate_rr_screen_get_crtc_by_id (MateRRScreen *screen,
+ guint32 id)
+{
+ int i;
+
+ g_return_val_if_fail (screen != NULL, NULL);
+ g_return_val_if_fail (screen->info != NULL, NULL);
+
+ for (i = 0; screen->info->crtcs[i] != NULL; ++i)
+ {
+ if (screen->info->crtcs[i]->id == id)
+ return screen->info->crtcs[i];
+ }
+
+ return NULL;
+}
+
+MateRROutput *
+mate_rr_screen_get_output_by_id (MateRRScreen *screen,
+ guint32 id)
+{
+ int i;
+
+ g_return_val_if_fail (screen != NULL, NULL);
+ g_return_val_if_fail (screen->info != NULL, NULL);
+
+ for (i = 0; screen->info->outputs[i] != NULL; ++i)
+ {
+ if (screen->info->outputs[i]->id == id)
+ return screen->info->outputs[i];
+ }
+
+ return NULL;
+}
+
+/* MateRROutput */
+static MateRROutput *
+output_new (ScreenInfo *info, RROutput id)
+{
+ MateRROutput *output = g_new0 (MateRROutput, 1);
+
+ output->id = id;
+ output->info = info;
+
+ return output;
+}
+
+static guint8 *
+get_property (Display *dpy,
+ RROutput output,
+ Atom atom,
+ int *len)
+{
+#ifdef HAVE_RANDR
+ unsigned char *prop;
+ int actual_format;
+ unsigned long nitems, bytes_after;
+ Atom actual_type;
+ guint8 *result;
+
+ XRRGetOutputProperty (dpy, output, atom,
+ 0, 100, False, False,
+ AnyPropertyType,
+ &actual_type, &actual_format,
+ &nitems, &bytes_after, &prop);
+
+ if (actual_type == XA_INTEGER && actual_format == 8)
+ {
+ result = g_memdup (prop, nitems);
+ if (len)
+ *len = nitems;
+ }
+ else
+ {
+ result = NULL;
+ }
+
+ XFree (prop);
+
+ return result;
+#else
+ return NULL;
+#endif /* HAVE_RANDR */
+}
+
+static guint8 *
+read_edid_data (MateRROutput *output)
+{
+ Atom edid_atom;
+ guint8 *result;
+ int len;
+
+ edid_atom = XInternAtom (DISPLAY (output), "EDID", FALSE);
+ result = get_property (DISPLAY (output),
+ output->id, edid_atom, &len);
+
+ if (!result)
+ {
+ edid_atom = XInternAtom (DISPLAY (output), "EDID_DATA", FALSE);
+ result = get_property (DISPLAY (output),
+ output->id, edid_atom, &len);
+ }
+
+ if (result)
+ {
+ if (len % 128 == 0)
+ return result;
+ else
+ g_free (result);
+ }
+
+ return NULL;
+}
+
+static char *
+get_connector_type_string (MateRROutput *output)
+{
+#ifdef HAVE_RANDR
+ char *result;
+ unsigned char *prop;
+ int actual_format;
+ unsigned long nitems, bytes_after;
+ Atom actual_type;
+ Atom connector_type;
+ char *connector_type_str;
+
+ result = NULL;
+
+ if (XRRGetOutputProperty (DISPLAY (output), output->id, output->info->screen->connector_type_atom,
+ 0, 100, False, False,
+ AnyPropertyType,
+ &actual_type, &actual_format,
+ &nitems, &bytes_after, &prop) != Success)
+ return NULL;
+
+ if (!(actual_type == XA_ATOM && actual_format == 32 && nitems == 1))
+ goto out;
+
+ connector_type = *((Atom *) prop);
+
+ connector_type_str = XGetAtomName (DISPLAY (output), connector_type);
+ if (connector_type_str) {
+ result = g_strdup (connector_type_str); /* so the caller can g_free() it */
+ XFree (connector_type_str);
+ }
+
+out:
+
+ XFree (prop);
+
+ return result;
+#else
+ return NULL;
+#endif
+}
+
+#ifdef HAVE_RANDR
+static gboolean
+output_initialize (MateRROutput *output, XRRScreenResources *res, GError **error)
+{
+ XRROutputInfo *info = XRRGetOutputInfo (
+ DISPLAY (output), res, output->id);
+ GPtrArray *a;
+ int i;
+
+#if 0
+ g_print ("Output %lx Timestamp: %u\n", output->id, (guint32)info->timestamp);
+#endif
+
+ if (!info || !output->info)
+ {
+ /* FIXME: see the comment in crtc_initialize() */
+ /* Translators: here, an "output" is a video output */
+ g_set_error (error, MATE_RR_ERROR, MATE_RR_ERROR_RANDR_ERROR,
+ _("could not get information about output %d"),
+ (int) output->id);
+ return FALSE;
+ }
+
+ output->name = g_strdup (info->name); /* FIXME: what is nameLen used for? */
+ output->current_crtc = crtc_by_id (output->info, info->crtc);
+ output->width_mm = info->mm_width;
+ output->height_mm = info->mm_height;
+ output->connected = (info->connection == RR_Connected);
+ output->connector_type = get_connector_type_string (output);
+
+ /* Possible crtcs */
+ a = g_ptr_array_new ();
+
+ for (i = 0; i < info->ncrtc; ++i)
+ {
+ MateRRCrtc *crtc = crtc_by_id (output->info, info->crtcs[i]);
+
+ if (crtc)
+ g_ptr_array_add (a, crtc);
+ }
+ g_ptr_array_add (a, NULL);
+ output->possible_crtcs = (MateRRCrtc **)g_ptr_array_free (a, FALSE);
+
+ /* Clones */
+ a = g_ptr_array_new ();
+ for (i = 0; i < info->nclone; ++i)
+ {
+ MateRROutput *mate_rr_output = mate_rr_output_by_id (output->info, info->clones[i]);
+
+ if (mate_rr_output)
+ g_ptr_array_add (a, mate_rr_output);
+ }
+ g_ptr_array_add (a, NULL);
+ output->clones = (MateRROutput **)g_ptr_array_free (a, FALSE);
+
+ /* Modes */
+ a = g_ptr_array_new ();
+ for (i = 0; i < info->nmode; ++i)
+ {
+ MateRRMode *mode = mode_by_id (output->info, info->modes[i]);
+
+ if (mode)
+ g_ptr_array_add (a, mode);
+ }
+ g_ptr_array_add (a, NULL);
+ output->modes = (MateRRMode **)g_ptr_array_free (a, FALSE);
+
+ output->n_preferred = info->npreferred;
+
+ /* Edid data */
+ output->edid_data = read_edid_data (output);
+
+ XRRFreeOutputInfo (info);
+
+ return TRUE;
+}
+#endif /* HAVE_RANDR */
+
+static void
+output_free (MateRROutput *output)
+{
+ g_free (output->clones);
+ g_free (output->modes);
+ g_free (output->possible_crtcs);
+ g_free (output->edid_data);
+ g_free (output->name);
+ g_free (output->connector_type);
+ g_free (output);
+}
+
+guint32
+mate_rr_output_get_id (MateRROutput *output)
+{
+ g_assert(output != NULL);
+
+ return output->id;
+}
+
+const guint8 *
+mate_rr_output_get_edid_data (MateRROutput *output)
+{
+ g_return_val_if_fail (output != NULL, NULL);
+
+ return output->edid_data;
+}
+
+MateRROutput *
+mate_rr_screen_get_output_by_name (MateRRScreen *screen,
+ const char *name)
+{
+ int i;
+
+ g_return_val_if_fail (screen != NULL, NULL);
+ g_return_val_if_fail (screen->info != NULL, NULL);
+
+ for (i = 0; screen->info->outputs[i] != NULL; ++i)
+ {
+ MateRROutput *output = screen->info->outputs[i];
+
+ if (strcmp (output->name, name) == 0)
+ return output;
+ }
+
+ return NULL;
+}
+
+MateRRCrtc *
+mate_rr_output_get_crtc (MateRROutput *output)
+{
+ g_return_val_if_fail (output != NULL, NULL);
+
+ return output->current_crtc;
+}
+
+/* Returns NULL if the ConnectorType property is not available */
+const char *
+mate_rr_output_get_connector_type (MateRROutput *output)
+{
+ g_return_val_if_fail (output != NULL, NULL);
+
+ return output->connector_type;
+}
+
+gboolean
+mate_rr_output_is_laptop (MateRROutput *output)
+{
+ const char *connector_type;
+
+ g_return_val_if_fail (output != NULL, FALSE);
+
+ if (!output->connected)
+ return FALSE;
+
+ /* The ConnectorType property is present in RANDR 1.3 and greater */
+
+ connector_type = mate_rr_output_get_connector_type (output);
+ if (connector_type && strcmp (connector_type, MATE_RR_CONNECTOR_TYPE_PANEL) == 0)
+ return TRUE;
+
+ /* Older versions of RANDR - this is a best guess, as @#$% RANDR doesn't have standard output names,
+ * so drivers can use whatever they like.
+ */
+
+ if (output->name
+ && (strstr (output->name, "lvds") || /* Most drivers use an "LVDS" prefix... */
+ strstr (output->name, "LVDS") ||
+ strstr (output->name, "Lvds") ||
+ strstr (output->name, "LCD"))) /* ... but fglrx uses "LCD" in some versions. Shoot me now, kthxbye. */
+ return TRUE;
+
+ return FALSE;
+}
+
+MateRRMode *
+mate_rr_output_get_current_mode (MateRROutput *output)
+{
+ MateRRCrtc *crtc;
+
+ g_return_val_if_fail (output != NULL, NULL);
+
+ if ((crtc = mate_rr_output_get_crtc (output)))
+ return mate_rr_crtc_get_current_mode (crtc);
+
+ return NULL;
+}
+
+void
+mate_rr_output_get_position (MateRROutput *output,
+ int *x,
+ int *y)
+{
+ MateRRCrtc *crtc;
+
+ g_return_if_fail (output != NULL);
+
+ if ((crtc = mate_rr_output_get_crtc (output)))
+ mate_rr_crtc_get_position (crtc, x, y);
+}
+
+const char *
+mate_rr_output_get_name (MateRROutput *output)
+{
+ g_assert (output != NULL);
+ return output->name;
+}
+
+int
+mate_rr_output_get_width_mm (MateRROutput *output)
+{
+ g_assert (output != NULL);
+ return output->width_mm;
+}
+
+int
+mate_rr_output_get_height_mm (MateRROutput *output)
+{
+ g_assert (output != NULL);
+ return output->height_mm;
+}
+
+MateRRMode *
+mate_rr_output_get_preferred_mode (MateRROutput *output)
+{
+ g_return_val_if_fail (output != NULL, NULL);
+ if (output->n_preferred)
+ return output->modes[0];
+
+ return NULL;
+}
+
+MateRRMode **
+mate_rr_output_list_modes (MateRROutput *output)
+{
+ g_return_val_if_fail (output != NULL, NULL);
+ return output->modes;
+}
+
+gboolean
+mate_rr_output_is_connected (MateRROutput *output)
+{
+ g_return_val_if_fail (output != NULL, FALSE);
+ return output->connected;
+}
+
+gboolean
+mate_rr_output_supports_mode (MateRROutput *output,
+ MateRRMode *mode)
+{
+ int i;
+
+ g_return_val_if_fail (output != NULL, FALSE);
+ g_return_val_if_fail (mode != NULL, FALSE);
+
+ for (i = 0; output->modes[i] != NULL; ++i)
+ {
+ if (output->modes[i] == mode)
+ return TRUE;
+ }
+
+ return FALSE;
+}
+
+gboolean
+mate_rr_output_can_clone (MateRROutput *output,
+ MateRROutput *clone)
+{
+ int i;
+
+ g_return_val_if_fail (output != NULL, FALSE);
+ g_return_val_if_fail (clone != NULL, FALSE);
+
+ for (i = 0; output->clones[i] != NULL; ++i)
+ {
+ if (output->clones[i] == clone)
+ return TRUE;
+ }
+
+ return FALSE;
+}
+
+gboolean
+mate_rr_output_get_is_primary (MateRROutput *output)
+{
+#ifdef HAVE_RANDR
+ return output->info->primary == output->id;
+#else
+ return FALSE;
+#endif
+}
+
+void
+mate_rr_screen_set_primary_output (MateRRScreen *screen,
+ MateRROutput *output)
+{
+#ifdef HAVE_RANDR
+#if (RANDR_MAJOR > 1 || (RANDR_MAJOR == 1 && RANDR_MINOR >= 3))
+ RROutput id;
+
+ if (output)
+ id = output->id;
+ else
+ id = None;
+
+ /* Runtime check for RandR 1.3 or higher */
+ if (screen->rr_major_version == 1 && screen->rr_minor_version >= 3)
+ XRRSetOutputPrimary (screen->xdisplay, screen->xroot, id);
+#endif
+#endif /* HAVE_RANDR */
+}
+
+/* MateRRCrtc */
+typedef struct
+{
+ Rotation xrot;
+ MateRRRotation rot;
+} RotationMap;
+
+static const RotationMap rotation_map[] =
+{
+ { RR_Rotate_0, MATE_RR_ROTATION_0 },
+ { RR_Rotate_90, MATE_RR_ROTATION_90 },
+ { RR_Rotate_180, MATE_RR_ROTATION_180 },
+ { RR_Rotate_270, MATE_RR_ROTATION_270 },
+ { RR_Reflect_X, MATE_RR_REFLECT_X },
+ { RR_Reflect_Y, MATE_RR_REFLECT_Y },
+};
+
+static MateRRRotation
+mate_rr_rotation_from_xrotation (Rotation r)
+{
+ int i;
+ MateRRRotation result = 0;
+
+ for (i = 0; i < G_N_ELEMENTS (rotation_map); ++i)
+ {
+ if (r & rotation_map[i].xrot)
+ result |= rotation_map[i].rot;
+ }
+
+ return result;
+}
+
+static Rotation
+xrotation_from_rotation (MateRRRotation r)
+{
+ int i;
+ Rotation result = 0;
+
+ for (i = 0; i < G_N_ELEMENTS (rotation_map); ++i)
+ {
+ if (r & rotation_map[i].rot)
+ result |= rotation_map[i].xrot;
+ }
+
+ return result;
+}
+
+#ifndef MATE_DISABLE_DEPRECATED_SOURCE
+gboolean
+mate_rr_crtc_set_config (MateRRCrtc *crtc,
+ int x,
+ int y,
+ MateRRMode *mode,
+ MateRRRotation rotation,
+ MateRROutput **outputs,
+ int n_outputs,
+ GError **error)
+{
+ return mate_rr_crtc_set_config_with_time (crtc, GDK_CURRENT_TIME, x, y, mode, rotation, outputs, n_outputs, error);
+}
+#endif
+
+gboolean
+mate_rr_crtc_set_config_with_time (MateRRCrtc *crtc,
+ guint32 timestamp,
+ int x,
+ int y,
+ MateRRMode *mode,
+ MateRRRotation rotation,
+ MateRROutput **outputs,
+ int n_outputs,
+ GError **error)
+{
+#ifdef HAVE_RANDR
+ ScreenInfo *info;
+ GArray *output_ids;
+ Status status;
+ gboolean result;
+ int i;
+
+ g_return_val_if_fail (crtc != NULL, FALSE);
+ g_return_val_if_fail (mode != NULL || outputs == NULL || n_outputs == 0, FALSE);
+ g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
+
+ info = crtc->info;
+
+ if (mode)
+ {
+ if (x + mode->width > info->max_width
+ || y + mode->height > info->max_height)
+ {
+ g_set_error (error, MATE_RR_ERROR, MATE_RR_ERROR_BOUNDS_ERROR,
+ /* Translators: the "position", "size", and "maximum"
+ * words here are not keywords; please translate them
+ * as usual. A CRTC is a CRT Controller (this is X terminology) */
+ _("requested position/size for CRTC %d is outside the allowed limit: "
+ "position=(%d, %d), size=(%d, %d), maximum=(%d, %d)"),
+ (int) crtc->id,
+ x, y,
+ mode->width, mode->height,
+ info->max_width, info->max_height);
+ return FALSE;
+ }
+ }
+
+ output_ids = g_array_new (FALSE, FALSE, sizeof (RROutput));
+
+ if (outputs)
+ {
+ for (i = 0; i < n_outputs; ++i)
+ g_array_append_val (output_ids, outputs[i]->id);
+ }
+
+ status = XRRSetCrtcConfig (DISPLAY (crtc), info->resources, crtc->id,
+ timestamp,
+ x, y,
+ mode ? mode->id : None,
+ xrotation_from_rotation (rotation),
+ (RROutput *)output_ids->data,
+ output_ids->len);
+
+ g_array_free (output_ids, TRUE);
+
+ if (status == RRSetConfigSuccess)
+ result = TRUE;
+ else {
+ result = FALSE;
+ /* Translators: CRTC is a CRT Controller (this is X terminology).
+ * It is *very* unlikely that you'll ever get this error, so it is
+ * only listed for completeness. */
+ g_set_error (error, MATE_RR_ERROR, MATE_RR_ERROR_RANDR_ERROR,
+ _("could not set the configuration for CRTC %d"),
+ (int) crtc->id);
+ }
+
+ return result;
+#else
+ return FALSE;
+#endif /* HAVE_RANDR */
+}
+
+MateRRMode *
+mate_rr_crtc_get_current_mode (MateRRCrtc *crtc)
+{
+ g_return_val_if_fail (crtc != NULL, NULL);
+
+ return crtc->current_mode;
+}
+
+guint32
+mate_rr_crtc_get_id (MateRRCrtc *crtc)
+{
+ g_return_val_if_fail (crtc != NULL, 0);
+
+ return crtc->id;
+}
+
+gboolean
+mate_rr_crtc_can_drive_output (MateRRCrtc *crtc,
+ MateRROutput *output)
+{
+ int i;
+
+ g_return_val_if_fail (crtc != NULL, FALSE);
+ g_return_val_if_fail (output != NULL, FALSE);
+
+ for (i = 0; crtc->possible_outputs[i] != NULL; ++i)
+ {
+ if (crtc->possible_outputs[i] == output)
+ return TRUE;
+ }
+
+ return FALSE;
+}
+
+/* FIXME: merge with get_mode()? */
+void
+mate_rr_crtc_get_position (MateRRCrtc *crtc,
+ int *x,
+ int *y)
+{
+ g_return_if_fail (crtc != NULL);
+
+ if (x)
+ *x = crtc->x;
+
+ if (y)
+ *y = crtc->y;
+}
+
+/* FIXME: merge with get_mode()? */
+MateRRRotation
+mate_rr_crtc_get_current_rotation (MateRRCrtc *crtc)
+{
+ g_assert(crtc != NULL);
+ return crtc->current_rotation;
+}
+
+MateRRRotation
+mate_rr_crtc_get_rotations (MateRRCrtc *crtc)
+{
+ g_assert(crtc != NULL);
+ return crtc->rotations;
+}
+
+gboolean
+mate_rr_crtc_supports_rotation (MateRRCrtc * crtc,
+ MateRRRotation rotation)
+{
+ g_return_val_if_fail (crtc != NULL, FALSE);
+ return (crtc->rotations & rotation);
+}
+
+static MateRRCrtc *
+crtc_new (ScreenInfo *info, RROutput id)
+{
+ MateRRCrtc *crtc = g_new0 (MateRRCrtc, 1);
+
+ crtc->id = id;
+ crtc->info = info;
+
+ return crtc;
+}
+
+#ifdef HAVE_RANDR
+static gboolean
+crtc_initialize (MateRRCrtc *crtc,
+ XRRScreenResources *res,
+ GError **error)
+{
+ XRRCrtcInfo *info = XRRGetCrtcInfo (DISPLAY (crtc), res, crtc->id);
+ GPtrArray *a;
+ int i;
+
+#if 0
+ g_print ("CRTC %lx Timestamp: %u\n", crtc->id, (guint32)info->timestamp);
+#endif
+
+ if (!info)
+ {
+ /* FIXME: We need to reaquire the screen resources */
+ /* FIXME: can we actually catch BadRRCrtc, and does it make sense to emit that? */
+
+ /* Translators: CRTC is a CRT Controller (this is X terminology).
+ * It is *very* unlikely that you'll ever get this error, so it is
+ * only listed for completeness. */
+ g_set_error (error, MATE_RR_ERROR, MATE_RR_ERROR_RANDR_ERROR,
+ _("could not get information about CRTC %d"),
+ (int) crtc->id);
+ return FALSE;
+ }
+
+ /* MateRRMode */
+ crtc->current_mode = mode_by_id (crtc->info, info->mode);
+
+ crtc->x = info->x;
+ crtc->y = info->y;
+
+ /* Current outputs */
+ a = g_ptr_array_new ();
+ for (i = 0; i < info->noutput; ++i)
+ {
+ MateRROutput *output = mate_rr_output_by_id (crtc->info, info->outputs[i]);
+
+ if (output)
+ g_ptr_array_add (a, output);
+ }
+ g_ptr_array_add (a, NULL);
+ crtc->current_outputs = (MateRROutput **)g_ptr_array_free (a, FALSE);
+
+ /* Possible outputs */
+ a = g_ptr_array_new ();
+ for (i = 0; i < info->npossible; ++i)
+ {
+ MateRROutput *output = mate_rr_output_by_id (crtc->info, info->possible[i]);
+
+ if (output)
+ g_ptr_array_add (a, output);
+ }
+ g_ptr_array_add (a, NULL);
+ crtc->possible_outputs = (MateRROutput **)g_ptr_array_free (a, FALSE);
+
+ /* Rotations */
+ crtc->current_rotation = mate_rr_rotation_from_xrotation (info->rotation);
+ crtc->rotations = mate_rr_rotation_from_xrotation (info->rotations);
+
+ XRRFreeCrtcInfo (info);
+
+ /* get an store gamma size */
+ crtc->gamma_size = XRRGetCrtcGammaSize (DISPLAY (crtc), crtc->id);
+
+ return TRUE;
+}
+#endif
+
+static void
+crtc_free (MateRRCrtc *crtc)
+{
+ g_free (crtc->current_outputs);
+ g_free (crtc->possible_outputs);
+ g_free (crtc);
+}
+
+/* MateRRMode */
+static MateRRMode *
+mode_new (ScreenInfo *info, RRMode id)
+{
+ MateRRMode *mode = g_new0 (MateRRMode, 1);
+
+ mode->id = id;
+ mode->info = info;
+
+ return mode;
+}
+
+guint32
+mate_rr_mode_get_id (MateRRMode *mode)
+{
+ g_return_val_if_fail (mode != NULL, 0);
+ return mode->id;
+}
+
+guint
+mate_rr_mode_get_width (MateRRMode *mode)
+{
+ g_return_val_if_fail (mode != NULL, 0);
+ return mode->width;
+}
+
+int
+mate_rr_mode_get_freq (MateRRMode *mode)
+{
+ g_return_val_if_fail (mode != NULL, 0);
+ return (mode->freq) / 1000;
+}
+
+guint
+mate_rr_mode_get_height (MateRRMode *mode)
+{
+ g_return_val_if_fail (mode != NULL, 0);
+ return mode->height;
+}
+
+#ifdef HAVE_RANDR
+static void
+mode_initialize (MateRRMode *mode, XRRModeInfo *info)
+{
+ g_assert (mode != NULL);
+ g_assert (info != NULL);
+
+ mode->name = g_strdup (info->name);
+ mode->width = info->width;
+ mode->height = info->height;
+ mode->freq = ((info->dotClock / (double)info->hTotal) / info->vTotal + 0.5) * 1000;
+}
+#endif /* HAVE_RANDR */
+
+static void
+mode_free (MateRRMode *mode)
+{
+ g_free (mode->name);
+ g_free (mode);
+}
+
+void
+mate_rr_crtc_set_gamma (MateRRCrtc *crtc, int size,
+ unsigned short *red,
+ unsigned short *green,
+ unsigned short *blue)
+{
+#ifdef HAVE_RANDR
+ int copy_size;
+ XRRCrtcGamma *gamma;
+
+ g_return_if_fail (crtc != NULL);
+ g_return_if_fail (red != NULL);
+ g_return_if_fail (green != NULL);
+ g_return_if_fail (blue != NULL);
+
+ if (size != crtc->gamma_size)
+ return;
+
+ gamma = XRRAllocGamma (crtc->gamma_size);
+
+ copy_size = crtc->gamma_size * sizeof (unsigned short);
+ memcpy (gamma->red, red, copy_size);
+ memcpy (gamma->green, green, copy_size);
+ memcpy (gamma->blue, blue, copy_size);
+
+ XRRSetCrtcGamma (DISPLAY (crtc), crtc->id, gamma);
+ XRRFreeGamma (gamma);
+#endif /* HAVE_RANDR */
+}
+
+gboolean
+mate_rr_crtc_get_gamma (MateRRCrtc *crtc, int *size,
+ unsigned short **red, unsigned short **green,
+ unsigned short **blue)
+{
+#ifdef HAVE_RANDR
+ int copy_size;
+ unsigned short *r, *g, *b;
+ XRRCrtcGamma *gamma;
+
+ g_return_val_if_fail (crtc != NULL, FALSE);
+
+ gamma = XRRGetCrtcGamma (DISPLAY (crtc), crtc->id);
+ if (!gamma)
+ return FALSE;
+
+ copy_size = crtc->gamma_size * sizeof (unsigned short);
+
+ if (red) {
+ r = g_new0 (unsigned short, crtc->gamma_size);
+ memcpy (r, gamma->red, copy_size);
+ *red = r;
+ }
+
+ if (green) {
+ g = g_new0 (unsigned short, crtc->gamma_size);
+ memcpy (g, gamma->green, copy_size);
+ *green = g;
+ }
+
+ if (blue) {
+ b = g_new0 (unsigned short, crtc->gamma_size);
+ memcpy (b, gamma->blue, copy_size);
+ *blue = b;
+ }
+
+ XRRFreeGamma (gamma);
+
+ if (size)
+ *size = crtc->gamma_size;
+
+ return TRUE;
+#else
+ return FALSE;
+#endif /* HAVE_RANDR */
+}
+