summaryrefslogtreecommitdiff
path: root/libgnome-desktop/gnome-rr.c
diff options
context:
space:
mode:
Diffstat (limited to 'libgnome-desktop/gnome-rr.c')
-rw-r--r--libgnome-desktop/gnome-rr.c1215
1 files changed, 1215 insertions, 0 deletions
diff --git a/libgnome-desktop/gnome-rr.c b/libgnome-desktop/gnome-rr.c
index e69de29b..8e6ff0e9 100644
--- a/libgnome-desktop/gnome-rr.c
+++ b/libgnome-desktop/gnome-rr.c
@@ -0,0 +1,1215 @@
+/* gnome-rr.c
+ *
+ * Copyright 2007, 2008, Red Hat, Inc.
+ *
+ * This file is part of the Gnome Library.
+ *
+ * The Gnome 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 Gnome 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 Gnome 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 <sandmann@redhat.com>
+ */
+
+#define GNOME_DESKTOP_USE_UNSTABLE_API
+
+#include "libgnomeui/gnome-rr.h"
+#include <string.h>
+#include <X11/Xlib.h>
+#include <X11/extensions/Xrandr.h>
+#include <gtk/gtk.h>
+#include <gdk/gdkx.h>
+#include <X11/Xatom.h>
+
+#define DISPLAY(o) ((o)->info->screen->xdisplay)
+
+typedef struct ScreenInfo ScreenInfo;
+
+struct ScreenInfo
+{
+ int min_width;
+ int max_width;
+ int min_height;
+ int max_height;
+
+ XRRScreenResources *resources;
+
+ GnomeRROutput ** outputs;
+ GnomeRRCrtc ** crtcs;
+ GnomeRRMode ** modes;
+
+ GnomeRRScreen * screen;
+};
+
+struct GnomeRRScreen
+{
+ GdkScreen * gdk_screen;
+ GdkWindow * gdk_root;
+ Display * xdisplay;
+ Screen * xscreen;
+ Window xroot;
+ ScreenInfo * info;
+
+ int randr_event_base;
+
+ GnomeRRScreenChanged callback;
+ gpointer data;
+};
+
+struct GnomeRROutput
+{
+ ScreenInfo * info;
+ RROutput id;
+
+ char * name;
+ GnomeRRCrtc * current_crtc;
+ gboolean connected;
+ gulong width_mm;
+ gulong height_mm;
+ GnomeRRCrtc ** possible_crtcs;
+ GnomeRROutput ** clones;
+ GnomeRRMode ** modes;
+ int n_preferred;
+ guint8 * edid_data;
+};
+
+struct GnomeRROutputWrap
+{
+ RROutput id;
+};
+
+struct GnomeRRCrtc
+{
+ ScreenInfo * info;
+ RRCrtc id;
+
+ GnomeRRMode * current_mode;
+ GnomeRROutput ** current_outputs;
+ GnomeRROutput ** possible_outputs;
+ int x;
+ int y;
+
+ GnomeRRRotation current_rotation;
+ GnomeRRRotation rotations;
+};
+
+struct GnomeRRMode
+{
+ ScreenInfo * info;
+ RRMode id;
+ char * name;
+ int width;
+ int height;
+ int freq; /* in mHz */
+};
+
+/* GnomeRRCrtc */
+static GnomeRRCrtc * crtc_new (ScreenInfo *info,
+ RRCrtc id);
+static void crtc_free (GnomeRRCrtc *crtc);
+static void crtc_initialize (GnomeRRCrtc *crtc,
+ XRRScreenResources *res);
+
+/* GnomeRROutput */
+static GnomeRROutput *output_new (ScreenInfo *info,
+ RROutput id);
+static void output_initialize (GnomeRROutput *output,
+ XRRScreenResources *res);
+static void output_free (GnomeRROutput *output);
+
+/* GnomeRRMode */
+static GnomeRRMode * mode_new (ScreenInfo *info,
+ RRMode id);
+static void mode_initialize (GnomeRRMode *mode,
+ XRRModeInfo *info);
+static void mode_free (GnomeRRMode *mode);
+
+
+/* Screen */
+static GnomeRROutput *
+gnome_rr_output_by_id (ScreenInfo *info, RROutput id)
+{
+ GnomeRROutput **output;
+
+ g_assert (info != NULL);
+
+ for (output = info->outputs; *output; ++output)
+ {
+ if ((*output)->id == id)
+ return *output;
+ }
+
+ return NULL;
+}
+
+static GnomeRRCrtc *
+crtc_by_id (ScreenInfo *info, RRCrtc id)
+{
+ GnomeRRCrtc **crtc;
+
+ if (!info)
+ return NULL;
+
+ for (crtc = info->crtcs; *crtc; ++crtc)
+ {
+ if ((*crtc)->id == id)
+ return *crtc;
+ }
+
+ return NULL;
+}
+
+static GnomeRRMode *
+mode_by_id (ScreenInfo *info, RRMode id)
+{
+ GnomeRRMode **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)
+{
+ GnomeRROutput **output;
+ GnomeRRCrtc **crtc;
+ GnomeRRMode **mode;
+
+ g_assert (info != NULL);
+
+ if (info->resources)
+ {
+ XRRFreeScreenResources (info->resources);
+
+ info->resources = NULL;
+ }
+
+ 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);
+ }
+
+ g_free (info);
+}
+
+static gboolean
+fill_out_screen_info (Display *xdisplay,
+ Window xroot,
+ ScreenInfo *info)
+{
+ XRRScreenResources *resources;
+
+ g_assert (xdisplay != NULL);
+ g_assert (info != NULL);
+
+ gdk_error_trap_push ();
+
+ if (!XRRGetScreenSizeRange (xdisplay, xroot,
+ &(info->min_width),
+ &(info->min_height),
+ &(info->max_width),
+ &(info->max_height))) {
+ /* XRR caught an error */
+ return False;
+ }
+
+ gdk_flush ();
+ if (gdk_error_trap_pop ())
+ {
+ /* Unhandled X Error was generated */
+ return False;
+ }
+
+#if 0
+ g_print ("ranges: %d - %d; %d - %d\n",
+ screen->min_width, screen->max_width,
+ screen->min_height, screen->max_height);
+#endif
+
+ resources = XRRGetScreenResources (xdisplay, xroot);
+
+ if (resources)
+ {
+ int i;
+ GPtrArray *a;
+ GnomeRRCrtc **crtc;
+ GnomeRROutput **output;
+
+#if 0
+ g_print ("Resource Timestamp: %u\n", (guint32)resources->timestamp);
+ g_print ("Resource Configuration Timestamp: %u\n", (guint32)resources->configTimestamp);
+#endif
+
+ 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)
+ {
+ GnomeRRCrtc *crtc = crtc_new (info, resources->crtcs[i]);
+
+ g_ptr_array_add (a, crtc);
+ }
+ g_ptr_array_add (a, NULL);
+ info->crtcs = (GnomeRRCrtc **)g_ptr_array_free (a, FALSE);
+
+ a = g_ptr_array_new ();
+ for (i = 0; i < resources->noutput; ++i)
+ {
+ GnomeRROutput *output = output_new (info, resources->outputs[i]);
+
+ g_ptr_array_add (a, output);
+ }
+ g_ptr_array_add (a, NULL);
+ info->outputs = (GnomeRROutput **)g_ptr_array_free (a, FALSE);
+
+ a = g_ptr_array_new ();
+ for (i = 0; i < resources->nmode; ++i)
+ {
+ GnomeRRMode *mode = mode_new (info, resources->modes[i].id);
+
+ g_ptr_array_add (a, mode);
+ }
+ g_ptr_array_add (a, NULL);
+ info->modes = (GnomeRRMode **)g_ptr_array_free (a, FALSE);
+
+ /* Initialize */
+ for (crtc = info->crtcs; *crtc; ++crtc)
+ crtc_initialize (*crtc, resources);
+
+ for (output = info->outputs; *output; ++output)
+ output_initialize (*output, resources);
+
+ for (i = 0; i < resources->nmode; ++i)
+ {
+ GnomeRRMode *mode = mode_by_id (info, resources->modes[i].id);
+
+ mode_initialize (mode, &(resources->modes[i]));
+ }
+
+ return TRUE;
+ }
+ else
+ {
+ g_print ("Couldn't get screen resources\n");
+
+ return FALSE;
+ }
+}
+
+static ScreenInfo *
+screen_info_new (GnomeRRScreen *screen)
+{
+ 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))
+ {
+ return info;
+ }
+ else
+ {
+ g_free (info);
+ return NULL;
+ }
+}
+
+static gboolean
+screen_update (GnomeRRScreen *screen, gboolean force_callback)
+{
+ ScreenInfo *info;
+ gboolean changed = FALSE;
+
+ g_assert (screen != NULL);
+
+ info = screen_info_new (screen);
+ if (info)
+ {
+ if (info->resources->configTimestamp != screen->info->resources->configTimestamp)
+ changed = TRUE;
+
+ 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)
+{
+ GnomeRRScreen *screen = data;
+ XEvent *e = xevent;
+
+ if (e && e->type - screen->randr_event_base == RRNotify)
+ {
+ XRRNotifyEvent *event = (XRRNotifyEvent *)e;
+
+ switch (event->subtype)
+ {
+ default:
+ break;
+ }
+
+ /* FIXME: we may need to be more discriminating in
+ * what causes 'changed' events
+ */
+ screen_update (screen, TRUE);
+ }
+
+ /* 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.
+ */
+GnomeRRScreen *
+gnome_rr_screen_new (GdkScreen *gdk_screen,
+ GnomeRRScreenChanged callback,
+ gpointer data)
+{
+ Display *dpy = GDK_SCREEN_XDISPLAY (gdk_screen);
+ int event_base;
+ int ignore;
+
+ if (XRRQueryExtension (dpy, &event_base, &ignore))
+ {
+ GnomeRRScreen *screen = g_new0 (GnomeRRScreen, 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->callback = callback;
+ screen->data = data;
+
+ screen->randr_event_base = event_base;
+
+ screen->info = screen_info_new (screen);
+
+ if (!screen->info)
+ return NULL;
+
+ XRRSelectInput (screen->xdisplay,
+ screen->xroot,
+ RRScreenChangeNotifyMask |
+ RRCrtcChangeNotifyMask |
+ RROutputPropertyNotifyMask);
+
+ 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;
+ }
+
+ return NULL;
+}
+
+void
+gnome_rr_screen_set_size (GnomeRRScreen *screen,
+ int width,
+ int height,
+ int mm_width,
+ int mm_height)
+{
+ g_return_if_fail (screen != NULL);
+
+ XRRSetScreenSize (screen->xdisplay, screen->xroot,
+ width, height, mm_width, mm_height);
+}
+
+void
+gnome_rr_screen_get_ranges (GnomeRRScreen *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;
+}
+
+gboolean
+gnome_rr_screen_refresh (GnomeRRScreen *screen)
+{
+ return screen_update (screen, FALSE);
+}
+
+GnomeRRMode **
+gnome_rr_screen_list_modes (GnomeRRScreen *screen)
+{
+ g_return_val_if_fail (screen != NULL, NULL);
+ g_return_val_if_fail (screen->info != NULL, NULL);
+
+ return screen->info->modes;
+}
+
+GnomeRRCrtc **
+gnome_rr_screen_list_crtcs (GnomeRRScreen *screen)
+{
+ g_return_val_if_fail (screen != NULL, NULL);
+ g_return_val_if_fail (screen->info != NULL, NULL);
+
+ return screen->info->crtcs;
+}
+
+GnomeRROutput **
+gnome_rr_screen_list_outputs (GnomeRRScreen *screen)
+{
+ g_return_val_if_fail (screen != NULL, NULL);
+ g_return_val_if_fail (screen->info != NULL, NULL);
+
+ return screen->info->outputs;
+}
+
+GnomeRRCrtc *
+gnome_rr_screen_get_crtc_by_id (GnomeRRScreen *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;
+}
+
+GnomeRROutput *
+gnome_rr_screen_get_output_by_id (GnomeRRScreen *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;
+}
+
+/* GnomeRROutput */
+static GnomeRROutput *
+output_new (ScreenInfo *info, RROutput id)
+{
+ GnomeRROutput *output = g_new0 (GnomeRROutput, 1);
+
+ output->id = id;
+ output->info = info;
+
+ return output;
+}
+
+static guint8 *
+get_property (Display *dpy,
+ RROutput output,
+ Atom atom,
+ int *len)
+{
+ 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;
+}
+
+static guint8 *
+read_edid_data (GnomeRROutput *output)
+{
+ Atom edid_atom = XInternAtom (DISPLAY (output), "EDID_DATA", FALSE);
+ guint8 *result;
+ int len;
+
+ result = get_property (DISPLAY (output),
+ output->id, edid_atom, &len);
+
+ if (result)
+ {
+ if (len == 128)
+ return result;
+ else
+ g_free (result);
+ }
+
+ return NULL;
+}
+
+static void
+output_initialize (GnomeRROutput *output, XRRScreenResources *res)
+{
+ XRROutputInfo *info = XRRGetOutputInfo (
+ DISPLAY (output), res, output->id);
+ GPtrArray *a;
+ int i;
+
+ g_print ("Output %lx Timestamp: %u\n", output->id, (guint32)info->timestamp);
+
+ if (!info || !output->info)
+ {
+ /* FIXME */
+ return;
+ }
+
+ 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);
+
+ /* Possible crtcs */
+ a = g_ptr_array_new ();
+
+ for (i = 0; i < info->ncrtc; ++i)
+ {
+ GnomeRRCrtc *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 = (GnomeRRCrtc **)g_ptr_array_free (a, FALSE);
+
+ /* Clones */
+ a = g_ptr_array_new ();
+ for (i = 0; i < info->nclone; ++i)
+ {
+ GnomeRROutput *gnome_rr_output = gnome_rr_output_by_id (output->info, info->clones[i]);
+
+ if (gnome_rr_output)
+ g_ptr_array_add (a, gnome_rr_output);
+ }
+ g_ptr_array_add (a, NULL);
+ output->clones = (GnomeRROutput **)g_ptr_array_free (a, FALSE);
+
+ /* Modes */
+ a = g_ptr_array_new ();
+ for (i = 0; i < info->nmode; ++i)
+ {
+ GnomeRRMode *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 = (GnomeRRMode **)g_ptr_array_free (a, FALSE);
+
+ output->n_preferred = info->npreferred;
+
+ /* Edid data */
+ output->edid_data = read_edid_data (output);
+
+ XRRFreeOutputInfo (info);
+}
+
+static void
+output_free (GnomeRROutput *output)
+{
+ g_free (output);
+}
+
+guint32
+gnome_rr_output_get_id (GnomeRROutput *output)
+{
+ g_assert(output != NULL);
+
+ return output->id;
+}
+
+const guint8 *
+gnome_rr_output_get_edid_data (GnomeRROutput *output)
+{
+ g_return_val_if_fail (output != NULL, NULL);
+
+ return output->edid_data;
+}
+
+GnomeRROutput *
+gnome_rr_screen_get_output_by_name (GnomeRRScreen *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)
+ {
+ GnomeRROutput *output = screen->info->outputs[i];
+
+ if (strcmp (output->name, name) == 0)
+ return output;
+ }
+
+ return NULL;
+}
+
+GnomeRRCrtc *
+gnome_rr_output_get_crtc (GnomeRROutput *output)
+{
+ g_return_val_if_fail (output != NULL, NULL);
+
+ return output->current_crtc;
+}
+
+GnomeRRMode *
+gnome_rr_output_get_current_mode (GnomeRROutput *output)
+{
+ GnomeRRCrtc *crtc;
+
+ g_return_val_if_fail (output != NULL, NULL);
+
+ if ((crtc = gnome_rr_output_get_crtc (output)))
+ return gnome_rr_crtc_get_current_mode (crtc);
+
+ return NULL;
+}
+
+void
+gnome_rr_output_get_position (GnomeRROutput *output,
+ int *x,
+ int *y)
+{
+ GnomeRRCrtc *crtc;
+
+ g_return_if_fail (output != NULL);
+
+ if ((crtc = gnome_rr_output_get_crtc (output)))
+ gnome_rr_crtc_get_position (crtc, x, y);
+}
+
+const char *
+gnome_rr_output_get_name (GnomeRROutput *output)
+{
+ g_assert (output != NULL);
+ return output->name;
+}
+
+int
+gnome_rr_output_get_width_mm (GnomeRROutput *output)
+{
+ g_assert (output != NULL);
+ return output->width_mm;
+}
+
+int
+gnome_rr_output_get_height_mm (GnomeRROutput *output)
+{
+ g_assert (output != NULL);
+ return output->height_mm;
+}
+
+GnomeRRMode *
+gnome_rr_output_get_preferred_mode (GnomeRROutput *output)
+{
+ g_return_val_if_fail (output != NULL, NULL);
+ if (output->n_preferred)
+ return output->modes[0];
+
+ return NULL;
+}
+
+GnomeRRMode **
+gnome_rr_output_list_modes (GnomeRROutput *output)
+{
+ g_return_val_if_fail (output != NULL, NULL);
+ return output->modes;
+}
+
+gboolean
+gnome_rr_output_is_connected (GnomeRROutput *output)
+{
+ g_return_val_if_fail (output != NULL, FALSE);
+ return output->connected;
+}
+
+gboolean
+gnome_rr_output_supports_mode (GnomeRROutput *output,
+ GnomeRRMode *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
+gnome_rr_output_can_clone (GnomeRROutput *output,
+ GnomeRROutput *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;
+}
+
+/* GnomeRRCrtc */
+typedef struct
+{
+ Rotation xrot;
+ GnomeRRRotation rot;
+} RotationMap;
+
+static const RotationMap rotation_map[] =
+{
+ { RR_Rotate_0, GNOME_RR_ROTATION_0 },
+ { RR_Rotate_90, GNOME_RR_ROTATION_90 },
+ { RR_Rotate_180, GNOME_RR_ROTATION_180 },
+ { RR_Rotate_270, GNOME_RR_ROTATION_270 },
+ { RR_Reflect_X, GNOME_RR_REFLECT_X },
+ { RR_Reflect_Y, GNOME_RR_REFLECT_Y },
+};
+
+static GnomeRRRotation
+gnome_rr_rotation_from_xrotation (Rotation r)
+{
+ int i;
+ GnomeRRRotation 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 (GnomeRRRotation 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;
+}
+
+gboolean
+gnome_rr_crtc_set_config (GnomeRRCrtc *crtc,
+ int x,
+ int y,
+ GnomeRRMode *mode,
+ GnomeRRRotation rotation,
+ GnomeRROutput **outputs,
+ int n_outputs)
+{
+ ScreenInfo *info;
+ GArray *output_ids;
+ int i;
+
+ g_return_val_if_fail (crtc != NULL, FALSE);
+ g_return_val_if_fail (mode != NULL || outputs == NULL || n_outputs == 0, FALSE);
+
+ info = crtc->info;
+
+ if (mode)
+ {
+ g_return_val_if_fail (x + mode->width <= info->max_width, FALSE);
+ g_return_val_if_fail (y + mode->height <= info->max_height, 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);
+ }
+
+ XRRSetCrtcConfig (DISPLAY (crtc), info->resources, crtc->id,
+ CurrentTime,
+ x, y,
+ mode? mode->id : None,
+ xrotation_from_rotation (rotation),
+ (RROutput *)output_ids->data,
+ output_ids->len);
+
+ g_array_free (output_ids, TRUE);
+
+ return TRUE;
+}
+
+GnomeRRMode *
+gnome_rr_crtc_get_current_mode (GnomeRRCrtc *crtc)
+{
+ g_return_val_if_fail (crtc != NULL, NULL);
+
+ return crtc->current_mode;
+}
+
+guint32
+gnome_rr_crtc_get_id (GnomeRRCrtc *crtc)
+{
+ g_return_val_if_fail (crtc != NULL, 0);
+
+ return crtc->id;
+}
+
+gboolean
+gnome_rr_crtc_can_drive_output (GnomeRRCrtc *crtc,
+ GnomeRROutput *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
+gnome_rr_crtc_get_position (GnomeRRCrtc *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()? */
+GnomeRRRotation
+gnome_rr_crtc_get_current_rotation (GnomeRRCrtc *crtc)
+{
+ g_assert(crtc != NULL);
+ return crtc->current_rotation;
+}
+
+GnomeRRRotation
+gnome_rr_crtc_get_rotations (GnomeRRCrtc *crtc)
+{
+ g_assert(crtc != NULL);
+ return crtc->rotations;
+}
+
+gboolean
+gnome_rr_crtc_supports_rotation (GnomeRRCrtc * crtc,
+ GnomeRRRotation rotation)
+{
+ g_return_val_if_fail (crtc != NULL, FALSE);
+ return (crtc->rotations & rotation);
+}
+
+static GnomeRRCrtc *
+crtc_new (ScreenInfo *info, RROutput id)
+{
+ GnomeRRCrtc *crtc = g_new0 (GnomeRRCrtc, 1);
+
+ crtc->id = id;
+ crtc->info = info;
+
+ return crtc;
+}
+
+static void
+crtc_initialize (GnomeRRCrtc *crtc,
+ XRRScreenResources *res)
+{
+ XRRCrtcInfo *info = XRRGetCrtcInfo (DISPLAY (crtc), res, crtc->id);
+ GPtrArray *a;
+ int i;
+
+ g_print ("CRTC %lx Timestamp: %u\n", crtc->id, (guint32)info->timestamp);
+
+ if (!info)
+ {
+ /* FIXME: We need to reaquire the screen resources */
+ return;
+ }
+
+ /* GnomeRRMode */
+ 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)
+ {
+ GnomeRROutput *output = gnome_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 = (GnomeRROutput **)g_ptr_array_free (a, FALSE);
+
+ /* Possible outputs */
+ a = g_ptr_array_new ();
+ for (i = 0; i < info->npossible; ++i)
+ {
+ GnomeRROutput *output = gnome_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 = (GnomeRROutput **)g_ptr_array_free (a, FALSE);
+
+ /* Rotations */
+ crtc->current_rotation = gnome_rr_rotation_from_xrotation (info->rotation);
+ crtc->rotations = gnome_rr_rotation_from_xrotation (info->rotations);
+
+ XRRFreeCrtcInfo (info);
+}
+
+static void
+crtc_free (GnomeRRCrtc *crtc)
+{
+ g_free (crtc->current_outputs);
+ g_free (crtc->possible_outputs);
+ g_free (crtc);
+}
+
+/* GnomeRRMode */
+static GnomeRRMode *
+mode_new (ScreenInfo *info, RRMode id)
+{
+ GnomeRRMode *mode = g_new0 (GnomeRRMode, 1);
+
+ mode->id = id;
+ mode->info = info;
+
+ return mode;
+}
+
+guint32
+gnome_rr_mode_get_id (GnomeRRMode *mode)
+{
+ g_return_val_if_fail (mode != NULL, 0);
+ return mode->id;
+}
+
+guint
+gnome_rr_mode_get_width (GnomeRRMode *mode)
+{
+ g_return_val_if_fail (mode != NULL, 0);
+ return mode->width;
+}
+
+int
+gnome_rr_mode_get_freq (GnomeRRMode *mode)
+{
+ g_return_val_if_fail (mode != NULL, 0);
+ return (mode->freq) / 1000;
+}
+
+guint
+gnome_rr_mode_get_height (GnomeRRMode *mode)
+{
+ g_return_val_if_fail (mode != NULL, 0);
+ return mode->height;
+}
+
+static void
+mode_initialize (GnomeRRMode *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;
+}
+
+static void
+mode_free (GnomeRRMode *mode)
+{
+ g_free (mode->name);
+ g_free (mode);
+}
+
+
+#ifdef INCLUDE_MAIN
+static void
+on_screen_changed (GnomeRRScreen *screen, gpointer data)
+{
+ g_print ("Changed\n");
+}
+
+static gboolean
+do_refresh (gpointer data)
+{
+ GnomeRRScreen *screen = data;
+
+ gnome_rr_screen_refresh (screen);
+
+ return TRUE;
+}
+
+int
+main (int argc, char **argv)
+{
+ int i;
+
+ gtk_init (&argc, &argv);
+
+ GnomeRRScreen *screen = gnome_rr_screen_new (gdk_screen_get_default(),
+ on_screen_changed,
+ NULL);
+
+ for (i = 0; screen->info->crtcs[i]; ++i)
+ {
+ GnomeRRCrtc *crtc = screen->info->crtcs[i];
+
+ if (crtc->current_mode)
+ {
+ g_print ("CRTC %p: (%d %d %d %d)\n",
+ crtc, crtc->x, crtc->y,
+ crtc->current_mode->width, crtc->current_mode->height);
+ }
+ else
+ {
+ g_print ("CRTC %p: turned off\n", crtc);
+ }
+ }
+
+ for (i = 0; screen->info->outputs[i]; ++i)
+ {
+ GnomeRROutput *output = screen->info->outputs[i];
+
+ g_print ("Output %s currently", output->name);
+
+ if (!output->current_crtc)
+ g_print (" turned off\n");
+ else
+ g_print (" driven by CRTC %p\n", output->current_crtc);
+ }
+
+ g_timeout_add (500, do_refresh, screen);
+
+ gtk_main ();
+
+ return 0;
+}
+#endif