diff -up /dev/null gnome-control-center-2.21.5/capplets/display/edid.h
--- /dev/null 2008-01-26 11:28:38.229690233 -0500
+++ gnome-control-center-2.21.5/capplets/display/edid.h 2008-01-29 16:47:04.000000000 -0500
@@ -0,0 +1,169 @@
+typedef unsigned char uchar;
+typedef struct MonitorInfo MonitorInfo;
+typedef struct Timing Timing;
+typedef struct DetailedTiming DetailedTiming;
+
+typedef enum
+{
+ UNDEFINED,
+ DVI,
+ HDMI_A,
+ HDMI_B,
+ MDDI,
+ DISPLAY_PORT
+} Interface;
+
+typedef enum
+{
+ UNDEFINED_COLOR,
+ MONOCHROME,
+ RGB,
+ OTHER_COLOR
+} ColorType;
+
+typedef enum
+{
+ NO_STEREO,
+ FIELD_RIGHT,
+ FIELD_LEFT,
+ TWO_WAY_RIGHT_ON_EVEN,
+ TWO_WAY_LEFT_ON_EVEN,
+ FOUR_WAY_INTERLEAVED,
+ SIDE_BY_SIDE
+} StereoType;
+
+struct Timing
+{
+ int width;
+ int height;
+ int frequency;
+};
+
+struct DisplayDescriptor
+{
+};
+
+struct DetailedTiming
+{
+ int pixel_clock;
+ int h_addr;
+ int h_blank;
+ int h_sync;
+ int h_front_porch;
+ int v_addr;
+ int v_blank;
+ int v_sync;
+ int v_front_porch;
+ int width_mm;
+ int height_mm;
+ int right_border;
+ int top_border;
+ int interlaced;
+ StereoType stereo;
+
+ int digital_sync;
+ union
+ {
+ struct
+ {
+ int bipolar;
+ int serrations;
+ int sync_on_green;
+ } analog;
+
+ struct
+ {
+ int composite;
+ int serrations;
+ int negative_vsync;
+ int negative_hsync;
+ } digital;
+ };
+};
+
+struct MonitorInfo
+{
+ int checksum;
+ char manufacturer_code[4];
+ int product_code;
+ unsigned int serial_number;
+
+ int production_week; /* -1 if not specified */
+ int production_year; /* -1 if not specified */
+ int model_year; /* -1 if not specified */
+
+ int major_version;
+ int minor_version;
+
+ int is_digital;
+
+ union
+ {
+ struct
+ {
+ int bits_per_primary;
+ Interface interface;
+ int rgb444;
+ int ycrcb444;
+ int ycrcb422;
+ } digital;
+
+ struct
+ {
+ double video_signal_level;
+ double sync_signal_level;
+ double total_signal_level;
+
+ int blank_to_black;
+
+ int separate_hv_sync;
+ int composite_sync_on_h;
+ int composite_sync_on_green;
+ int serration_on_vsync;
+ ColorType color_type;
+ } analog;
+ };
+
+ int width_mm; /* -1 if not specified */
+ int height_mm; /* -1 if not specified */
+ double aspect_ratio; /* -1.0 if not specififed */
+
+ double gamma; /* -1.0 if not specified */
+
+ int standby;
+ int suspend;
+ int active_off;
+
+ int srgb_is_standard;
+ int preferred_timing_includes_native;
+ int continuous_frequency;
+
+ double red_x;
+ double red_y;
+ double green_x;
+ double green_y;
+ double blue_x;
+ double blue_y;
+ double white_x;
+ double white_y;
+
+ Timing established[24]; /* Terminated by 0x0x0 */
+ Timing standard[8];
+
+ int n_detailed_timings;
+ DetailedTiming detailed_timings[4]; /* If monitor has a preferred
+ * mode, it is the first one
+ * (whether it has, is
+ * determined by the
+ * preferred_timing_includes
+ * bit.
+ */
+
+ /* Optional product description */
+ char dsc_serial_number[14];
+ char dsc_product_name[14];
+ char dsc_string[14]; /* Unspecified ASCII data */
+};
+
+MonitorInfo *decode_edid (const uchar *data);
+char * make_display_name (const MonitorInfo *info);
diff -up /dev/null gnome-control-center-2.21.5/capplets/display/foo-marshal.c
--- /dev/null 2008-01-26 11:28:38.229690233 -0500
+++ gnome-control-center-2.21.5/capplets/display/foo-marshal.c 2008-01-29 16:47:04.000000000 -0500
@@ -0,0 +1,279 @@
+
+#include <glib-object.h>
+
+
+#ifdef G_ENABLE_DEBUG
+#define g_marshal_value_peek_boolean(v) g_value_get_boolean (v)
+#define g_marshal_value_peek_char(v) g_value_get_char (v)
+#define g_marshal_value_peek_uchar(v) g_value_get_uchar (v)
+#define g_marshal_value_peek_int(v) g_value_get_int (v)
+#define g_marshal_value_peek_uint(v) g_value_get_uint (v)
+#define g_marshal_value_peek_long(v) g_value_get_long (v)
+#define g_marshal_value_peek_ulong(v) g_value_get_ulong (v)
+#define g_marshal_value_peek_int64(v) g_value_get_int64 (v)
+#define g_marshal_value_peek_uint64(v) g_value_get_uint64 (v)
+#define g_marshal_value_peek_enum(v) g_value_get_enum (v)
+#define g_marshal_value_peek_flags(v) g_value_get_flags (v)
+#define g_marshal_value_peek_float(v) g_value_get_float (v)
+#define g_marshal_value_peek_double(v) g_value_get_double (v)
+#define g_marshal_value_peek_string(v) (char*) g_value_get_string (v)
+#define g_marshal_value_peek_param(v) g_value_get_param (v)
+#define g_marshal_value_peek_boxed(v) g_value_get_boxed (v)
+#define g_marshal_value_peek_pointer(v) g_value_get_pointer (v)
+#define g_marshal_value_peek_object(v) g_value_get_object (v)
+#else /* !G_ENABLE_DEBUG */
+/* WARNING: This code accesses GValues directly, which is UNSUPPORTED API.
+ * Do not access GValues directly in your code. Instead, use the
+ * g_value_get_*() functions
+ */
+#define g_marshal_value_peek_boolean(v) (v)->data[0].v_int
+#define g_marshal_value_peek_char(v) (v)->data[0].v_int
+#define g_marshal_value_peek_uchar(v) (v)->data[0].v_uint
+#define g_marshal_value_peek_int(v) (v)->data[0].v_int
+#define g_marshal_value_peek_uint(v) (v)->data[0].v_uint
+#define g_marshal_value_peek_long(v) (v)->data[0].v_long
+#define g_marshal_value_peek_ulong(v) (v)->data[0].v_ulong
+#define g_marshal_value_peek_int64(v) (v)->data[0].v_int64
+#define g_marshal_value_peek_uint64(v) (v)->data[0].v_uint64
+#define g_marshal_value_peek_enum(v) (v)->data[0].v_long
+#define g_marshal_value_peek_flags(v) (v)->data[0].v_ulong
+#define g_marshal_value_peek_float(v) (v)->data[0].v_float
+#define g_marshal_value_peek_double(v) (v)->data[0].v_double
+#define g_marshal_value_peek_string(v) (v)->data[0].v_pointer
+#define g_marshal_value_peek_param(v) (v)->data[0].v_pointer
+#define g_marshal_value_peek_boxed(v) (v)->data[0].v_pointer
+#define g_marshal_value_peek_pointer(v) (v)->data[0].v_pointer
+#define g_marshal_value_peek_object(v) (v)->data[0].v_pointer
+#endif /* !G_ENABLE_DEBUG */
+
+
+/* VOID:OBJECT,OBJECT (marshal.list:1) */
+void
+foo_marshal_VOID__OBJECT_OBJECT (GClosure *closure,
+ GValue *return_value G_GNUC_UNUSED,
+ guint n_param_values,
+ const GValue *param_values,
+ gpointer invocation_hint G_GNUC_UNUSED,
+ gpointer marshal_data)
+{
+ typedef void (*GMarshalFunc_VOID__OBJECT_OBJECT) (gpointer data1,
+ gpointer arg_1,
+ gpointer arg_2,
+ gpointer data2);
+ register GMarshalFunc_VOID__OBJECT_OBJECT callback;
+ register GCClosure *cc = (GCClosure*) closure;
+ register gpointer data1, data2;
+
+ g_return_if_fail (n_param_values == 3);
+
+ if (G_CCLOSURE_SWAP_DATA (closure))
+ {
+ data1 = closure->data;
+ data2 = g_value_peek_pointer (param_values + 0);
+ }
+ else
+ {
+ data1 = g_value_peek_pointer (param_values + 0);
+ data2 = closure->data;
+ }
+ callback = (GMarshalFunc_VOID__OBJECT_OBJECT) (marshal_data ? marshal_data : cc->callback);
+
+ callback (data1,
+ g_marshal_value_peek_object (param_values + 1),
+ g_marshal_value_peek_object (param_values + 2),
+ data2);
+}
+
+/* VOID:UINT,UINT,UINT,UINT (marshal.list:2) */
+void
+foo_marshal_VOID__UINT_UINT_UINT_UINT (GClosure *closure,
+ GValue *return_value G_GNUC_UNUSED,
+ guint n_param_values,
+ const GValue *param_values,
+ gpointer invocation_hint G_GNUC_UNUSED,
+ gpointer marshal_data)
+{
+ typedef void (*GMarshalFunc_VOID__UINT_UINT_UINT_UINT) (gpointer data1,
+ guint arg_1,
+ guint arg_2,
+ guint arg_3,
+ guint arg_4,
+ gpointer data2);
+ register GMarshalFunc_VOID__UINT_UINT_UINT_UINT callback;
+ register GCClosure *cc = (GCClosure*) closure;
+ register gpointer data1, data2;
+
+ g_return_if_fail (n_param_values == 5);
+
+ if (G_CCLOSURE_SWAP_DATA (closure))
+ {
+ data1 = closure->data;
+ data2 = g_value_peek_pointer (param_values + 0);
+ }
+ else
+ {
+ data1 = g_value_peek_pointer (param_values + 0);
+ data2 = closure->data;
+ }
+ callback = (GMarshalFunc_VOID__UINT_UINT_UINT_UINT) (marshal_data ? marshal_data : cc->callback);
+
+ callback (data1,
+ g_marshal_value_peek_uint (param_values + 1),
+ g_marshal_value_peek_uint (param_values + 2),
+ g_marshal_value_peek_uint (param_values + 3),
+ g_marshal_value_peek_uint (param_values + 4),
+ data2);
+}
+
+/* VOID:UINT,UINT (marshal.list:3) */
+void
+foo_marshal_VOID__UINT_UINT (GClosure *closure,
+ GValue *return_value G_GNUC_UNUSED,
+ guint n_param_values,
+ const GValue *param_values,
+ gpointer invocation_hint G_GNUC_UNUSED,
+ gpointer marshal_data)
+{
+ typedef void (*GMarshalFunc_VOID__UINT_UINT) (gpointer data1,
+ guint arg_1,
+ guint arg_2,
+ gpointer data2);
+ register GMarshalFunc_VOID__UINT_UINT callback;
+ register GCClosure *cc = (GCClosure*) closure;
+ register gpointer data1, data2;
+
+ g_return_if_fail (n_param_values == 3);
+
+ if (G_CCLOSURE_SWAP_DATA (closure))
+ {
+ data1 = closure->data;
+ data2 = g_value_peek_pointer (param_values + 0);
+ }
+ else
+ {
+ data1 = g_value_peek_pointer (param_values + 0);
+ data2 = closure->data;
+ }
+ callback = (GMarshalFunc_VOID__UINT_UINT) (marshal_data ? marshal_data : cc->callback);
+
+ callback (data1,
+ g_marshal_value_peek_uint (param_values + 1),
+ g_marshal_value_peek_uint (param_values + 2),
+ data2);
+}
+
+/* VOID:BOXED (marshal.list:4) */
+
+/* VOID:BOXED,BOXED (marshal.list:5) */
+void
+foo_marshal_VOID__BOXED_BOXED (GClosure *closure,
+ GValue *return_value G_GNUC_UNUSED,
+ guint n_param_values,
+ const GValue *param_values,
+ gpointer invocation_hint G_GNUC_UNUSED,
+ gpointer marshal_data)
+{
+ typedef void (*GMarshalFunc_VOID__BOXED_BOXED) (gpointer data1,
+ gpointer arg_1,
+ gpointer arg_2,
+ gpointer data2);
+ register GMarshalFunc_VOID__BOXED_BOXED callback;
+ register GCClosure *cc = (GCClosure*) closure;
+ register gpointer data1, data2;
+
+ g_return_if_fail (n_param_values == 3);
+
+ if (G_CCLOSURE_SWAP_DATA (closure))
+ {
+ data1 = closure->data;
+ data2 = g_value_peek_pointer (param_values + 0);
+ }
+ else
+ {
+ data1 = g_value_peek_pointer (param_values + 0);
+ data2 = closure->data;
+ }
+ callback = (GMarshalFunc_VOID__BOXED_BOXED) (marshal_data ? marshal_data : cc->callback);
+
+ callback (data1,
+ g_marshal_value_peek_boxed (param_values + 1),
+ g_marshal_value_peek_boxed (param_values + 2),
+ data2);
+}
+
+/* VOID:POINTER,BOXED,POINTER (marshal.list:6) */
+void
+foo_marshal_VOID__POINTER_BOXED_POINTER (GClosure *closure,
+ GValue *return_value G_GNUC_UNUSED,
+ guint n_param_values,
+ const GValue *param_values,
+ gpointer invocation_hint G_GNUC_UNUSED,
+ gpointer marshal_data)
+{
+ typedef void (*GMarshalFunc_VOID__POINTER_BOXED_POINTER) (gpointer data1,
+ gpointer arg_1,
+ gpointer arg_2,
+ gpointer arg_3,
+ gpointer data2);
+ register GMarshalFunc_VOID__POINTER_BOXED_POINTER callback;
+ register GCClosure *cc = (GCClosure*) closure;
+ register gpointer data1, data2;
+
+ g_return_if_fail (n_param_values == 4);
+
+ if (G_CCLOSURE_SWAP_DATA (closure))
+ {
+ data1 = closure->data;
+ data2 = g_value_peek_pointer (param_values + 0);
+ }
+ else
+ {
+ data1 = g_value_peek_pointer (param_values + 0);
+ data2 = closure->data;
+ }
+ callback = (GMarshalFunc_VOID__POINTER_BOXED_POINTER) (marshal_data ? marshal_data : cc->callback);
+
+ callback (data1,
+ g_marshal_value_peek_pointer (param_values + 1),
+ g_marshal_value_peek_boxed (param_values + 2),
+ g_marshal_value_peek_pointer (param_values + 3),
+ data2);
+}
+
+/* VOID:POINTER,POINTER (marshal.list:7) */
+void
+foo_marshal_VOID__POINTER_POINTER (GClosure *closure,
+ GValue *return_value G_GNUC_UNUSED,
+ guint n_param_values,
+ const GValue *param_values,
+ gpointer invocation_hint G_GNUC_UNUSED,
+ gpointer marshal_data)
+{
+ typedef void (*GMarshalFunc_VOID__POINTER_POINTER) (gpointer data1,
+ gpointer arg_1,
+ gpointer arg_2,
+ gpointer data2);
+ register GMarshalFunc_VOID__POINTER_POINTER callback;
+ register GCClosure *cc = (GCClosure*) closure;
+ register gpointer data1, data2;
+
+ g_return_if_fail (n_param_values == 3);
+
+ if (G_CCLOSURE_SWAP_DATA (closure))
+ {
+ data1 = closure->data;
+ data2 = g_value_peek_pointer (param_values + 0);
+ }
+ else
+ {
+ data1 = g_value_peek_pointer (param_values + 0);
+ data2 = closure->data;
+ }
+ callback = (GMarshalFunc_VOID__POINTER_POINTER) (marshal_data ? marshal_data : cc->callback);
+
+ callback (data1,
+ g_marshal_value_peek_pointer (param_values + 1),
+ g_marshal_value_peek_pointer (param_values + 2),
+ data2);
+}
+
diff -up /dev/null gnome-control-center-2.21.5/capplets/display/edid-parse.c
--- /dev/null 2008-01-26 11:28:38.229690233 -0500
+++ gnome-control-center-2.21.5/capplets/display/edid-parse.c 2008-01-29 16:47:04.000000000 -0500
@@ -0,0 +1,551 @@
+/*
+ * Copyright 2007 Red Hat, Inc.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * on the rights to use, copy, modify, merge, publish, distribute, sub
+ * license, and/or sell copies of the Software, and to permit persons to whom
+ * the Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice (including the next
+ * paragraph) shall be included in all copies or substantial portions of the
+ * Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
+ * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ */
+
+/* Author: Soren Sandmann <sandmann@redhat.com> */
+
+#include "edid.h"
+#include <stdlib.h>
+#include <string.h>
+#include <math.h>
+
+#define TRUE 1
+#define FALSE 0
+
+static int
+get_bit (int in, int bit)
+{
+ return (in & (1 << bit)) >> bit;
+}
+
+static int
+get_bits (int in, int begin, int end)
+{
+ int mask = (1 << (end - begin + 1)) - 1;
+
+ return (in >> begin) & mask;
+}
+
+static int
+decode_header (const uchar *edid)
+{
+ if (memcmp (edid, "\x00\xff\xff\xff\xff\xff\xff\x00", 8) == 0)
+ return TRUE;
+ return FALSE;
+}
+
+static int
+decode_vendor_and_product_identification (const uchar *edid, MonitorInfo *info)
+{
+ int is_model_year;
+
+ /* Manufacturer Code */
+ info->manufacturer_code[0] = get_bits (edid[0x08], 2, 6);
+ info->manufacturer_code[1] = get_bits (edid[0x08], 0, 1) << 3;
+ info->manufacturer_code[1] |= get_bits (edid[0x09], 5, 7);
+ info->manufacturer_code[2] = get_bits (edid[0x09], 0, 4);
+ info->manufacturer_code[3] = '\0';
+
+ info->manufacturer_code[0] += 'A' - 1;
+ info->manufacturer_code[1] += 'A' - 1;
+ info->manufacturer_code[2] += 'A' - 1;
+
+ /* Product Code */
+ info->product_code = edid[0x0b] << 8 | edid[0x0a];
+
+ /* Serial Number */
+ info->serial_number =
+ edid[0x0c] | edid[0x0d] << 8 | edid[0x0e] << 16 | edid[0x0f] << 24;
+
+ /* Week and Year */
+ is_model_year = FALSE;
+ switch (edid[0x10])
+ {
+ case 0x00:
+ info->production_week = -1;
+ break;
+
+ case 0xff:
+ info->production_week = -1;
+ is_model_year = TRUE;
+ break;
+
+ default:
+ info->production_week = edid[0x10];
+ break;
+ }
+
+ if (is_model_year)
+ {
+ info->production_year = -1;
+ info->model_year = 1990 + edid[0x11];
+ }
+ else
+ {
+ info->production_year = 1990 + edid[0x11];
+ info->model_year = -1;
+ }
+
+ return TRUE;
+}
+
+static int
+decode_edid_version (const uchar *edid, MonitorInfo *info)
+{
+ info->major_version = edid[0x12];
+ info->minor_version = edid[0x13];
+
+ return TRUE;
+}
+
+static int
+decode_display_parameters (const uchar *edid, MonitorInfo *info)
+{
+ /* Digital vs Analog */
+ info->is_digital = get_bit (edid[0x14], 7);
+
+ if (info->is_digital)
+ {
+ int bits;
+
+ static const int bit_depth[8] =
+ {
+ -1, 6, 8, 10, 12, 14, 16, -1
+ };
+
+ static const Interface interfaces[6] =
+ {
+ UNDEFINED, DVI, HDMI_A, HDMI_B, MDDI, DISPLAY_PORT
+ };
+
+ bits = get_bits (edid[0x14], 4, 6);
+ info->digital.bits_per_primary = bit_depth[bits];
+
+ bits = get_bits (edid[0x14], 0, 3);
+
+ if (bits <= 5)
+ info->digital.interface = interfaces[bits];
+ else
+ info->digital.interface = UNDEFINED;
+ }
+ else
+ {
+ int bits = get_bits (edid[0x14], 5, 6);
+
+ static const double levels[][3] =
+ {
+ { 0.7, 0.3, 1.0 },
+ { 0.714, 0.286, 1.0 },
+ { 1.0, 0.4, 1.4 },
+ { 0.7, 0.0, 0.7 },
+ };
+
+ info->analog.video_signal_level = levels[bits][0];
+ info->analog.sync_signal_level = levels[bits][1];
+ info->analog.total_signal_level = levels[bits][2];
+
+ info->analog.blank_to_black = get_bit (edid[0x14], 4);
+
+ info->analog.separate_hv_sync = get_bit (edid[0x14], 3);
+ info->analog.composite_sync_on_h = get_bit (edid[0x14], 2);
+ info->analog.composite_sync_on_green = get_bit (edid[0x14], 1);
+
+ info->analog.serration_on_vsync = get_bit (edid[0x14], 0);
+ }
+
+ /* Screen Size / Aspect Ratio */
+ if (edid[0x15] == 0 && edid[0x16] == 0)
+ {
+ info->width_mm = -1;
+ info->height_mm = -1;
+ info->aspect_ratio = -1.0;
+ }
+ else if (edid[0x16] == 0)
+ {
+ info->width_mm = -1;
+ info->height_mm = -1;
+ info->aspect_ratio = 100.0 / (edid[0x15] + 99);
+ }
+ else if (edid[0x15] == 0)
+ {
+ info->width_mm = -1;
+ info->height_mm = -1;
+ info->aspect_ratio = 100.0 / (edid[0x16] + 99);
+ info->aspect_ratio = 1/info->aspect_ratio; /* portrait */
+ }
+ else
+ {
+ info->width_mm = 10 * edid[0x15];
+ info->height_mm = 10 * edid[0x16];
+ }
+
+ /* Gamma */
+ if (edid[0x17] == 0xFF)
+ info->gamma = -1.0;
+ else
+ info->gamma = (edid[0x17] + 100.0) / 100.0;
+
+ /* Features */
+ info->standby = get_bit (edid[0x18], 7);
+ info->suspend = get_bit (edid[0x18], 6);
+ info->active_off = get_bit (edid[0x18], 5);
+
+ if (info->is_digital)
+ {
+ info->digital.rgb444 = TRUE;
+ if (get_bit (edid[0x18], 3))
+ info->digital.ycrcb444 = 1;
+ if (get_bit (edid[0x18], 4))
+ info->digital.ycrcb422 = 1;
+ }
+ else
+ {
+ int bits = get_bits (edid[0x18], 3, 4);
+ ColorType color_type[4] =
+ {
+ MONOCHROME, RGB, OTHER_COLOR, UNDEFINED_COLOR
+ };
+
+ info->analog.color_type = color_type[bits];
+ }
+
+ info->srgb_is_standard = get_bit (edid[0x18], 2);
+
+ /* In 1.3 this is called "has preferred timing" */
+ info->preferred_timing_includes_native = get_bit (edid[0x18], 1);
+
+ /* FIXME: In 1.3 this indicates whether the monitor accepts GTF */
+ info->continuous_frequency = get_bit (edid[0x18], 0);
+ return TRUE;
+}
+
+static double
+decode_fraction (int high, int low)
+{
+ double result = 0.0;
+ int i;
+
+ high = (high << 2) | low;
+
+ for (i = 0; i < 10; ++i)
+ result += get_bit (high, i) * pow (2, i - 10);
+
+ return result;
+}
+
+static int
+decode_color_characteristics (const uchar *edid, MonitorInfo *info)
+{
+ info->red_x = decode_fraction (edid[0x1b], get_bits (edid[0x19], 6, 7));
+ info->red_y = decode_fraction (edid[0x1c], get_bits (edid[0x19], 5, 4));
+ info->green_x = decode_fraction (edid[0x1d], get_bits (edid[0x19], 2, 3));
+ info->green_y = decode_fraction (edid[0x1e], get_bits (edid[0x19], 0, 1));
+ info->blue_x = decode_fraction (edid[0x1f], get_bits (edid[0x1a], 6, 7));
+ info->blue_y = decode_fraction (edid[0x20], get_bits (edid[0x1a], 4, 5));
+ info->white_x = decode_fraction (edid[0x21], get_bits (edid[0x1a], 2, 3));
+ info->white_y = decode_fraction (edid[0x22], get_bits (edid[0x1a], 0, 1));
+
+ return TRUE;
+}
+
+static int
+decode_established_timings (const uchar *edid, MonitorInfo *info)
+{
+ static const Timing established[][8] =
+ {
+ {
+ { 800, 600, 60 },
+ { 800, 600, 56 },
+ { 640, 480, 75 },
+ { 640, 480, 72 },
+ { 640, 480, 67 },
+ { 640, 480, 60 },
+ { 720, 400, 88 },
+ { 720, 400, 70 }
+ },
+ {
+ { 1280, 1024, 75 },
+ { 1024, 768, 75 },
+ { 1024, 768, 70 },
+ { 1024, 768, 60 },
+ { 1024, 768, 87 },
+ { 832, 624, 75 },
+ { 800, 600, 75 },
+ { 800, 600, 72 }
+ },
+ {
+ { 0, 0, 0 },
+ { 0, 0, 0 },
+ { 0, 0, 0 },
+ { 0, 0, 0 },
+ { 0, 0, 0 },
+ { 0, 0, 0 },
+ { 0, 0, 0 },
+ { 1152, 870, 75 }
+ },
+ };
+
+ int i, j, idx;
+
+ idx = 0;
+ for (i = 0; i < 3; ++i)
+ {
+ for (j = 0; j < 8; ++j)
+ {
+ int byte = edid[0x23 + i];
+
+ if (get_bit (byte, j) && established[i][j].frequency != 0)
+ info->established[idx++] = established[i][j];
+ }
+ }
+ return TRUE;
+}
+
+static int
+decode_standard_timings (const uchar *edid, MonitorInfo *info)
+{
+ int i;
+
+ for (i = 0; i < 8; i++)
+ {
+ int first = edid[0x26 + 2 * i];
+ int second = edid[0x27 + 2 * i];
+
+ if (first != 0x01 && second != 0x01)
+ {
+ int w = 8 * (first + 31);
+ int h;
+
+ switch (get_bits (second, 6, 7))
+ {
+ case 0x00: h = (w / 16) * 10; break;
+ case 0x01: h = (w / 4) * 3; break;
+ case 0x02: h = (w / 5) * 4; break;
+ case 0x03: h = (w / 16) * 9; break;
+ }
+
+ info->standard[i].width = w;
+ info->standard[i].height = h;
+ info->standard[i].frequency = get_bits (second, 0, 5) + 60;
+ }
+ }
+
+ return TRUE;
+}
+
+static void
+decode_lf_string (const uchar *s, int n_chars, char *result)
+{
+ int i;
+ for (i = 0; i < n_chars; ++i)
+ {
+ if (s[i] == 0x0a)
+ {
+ *result++ = '\0';
+ break;
+ }
+ else if (s[i] == 0x00)
+ {
+ /* Convert embedded 0's to spaces */
+ *result++ = ' ';
+ }
+ else
+ {
+ *result++ = s[i];
+ }
+ }
+}
+
+static void
+decode_display_descriptor (const uchar *desc,
+ MonitorInfo *info)
+{
+ switch (desc[0x03])
+ {
+ case 0xFC:
+ decode_lf_string (desc + 5, 13, info->dsc_product_name);
+ break;
+ case 0xFF:
+ decode_lf_string (desc + 5, 13, info->dsc_serial_number);
+ break;
+ case 0xFE:
+ decode_lf_string (desc + 5, 13, info->dsc_string);
+ break;
+ case 0xFD:
+ /* Range Limits */
+ break;
+ case 0xFB:
+ /* Color Point */
+ break;
+ case 0xFA:
+ /* Timing Identifications */
+ break;
+ case 0xF9:
+ /* Color Management */
+ break;
+ case 0xF8:
+ /* Timing Codes */
+ break;
+ case 0xF7:
+ /* Established Timings */
+ break;
+ case 0x10:
+ break;
+ }
+}
+
+static void
+decode_detailed_timing (const uchar *timing,
+ DetailedTiming *detailed)
+{
+ int bits;
+ StereoType stereo[] =
+ {
+ NO_STEREO, NO_STEREO, FIELD_RIGHT, FIELD_LEFT,
+ TWO_WAY_RIGHT_ON_EVEN, TWO_WAY_LEFT_ON_EVEN,
+ FOUR_WAY_INTERLEAVED, SIDE_BY_SIDE
+ };
+
+ detailed->pixel_clock = (timing[0x00] | timing[0x01] << 8) * 10000;
+ detailed->h_addr = timing[0x02] | ((timing[0x04] & 0xf0) << 4);
+ detailed->h_blank = timing[0x03] | ((timing[0x04] & 0x0f) << 8);
+ detailed->v_addr = timing[0x05] | ((timing[0x07] & 0xf0) << 4);
+ detailed->v_blank = timing[0x06] | ((timing[0x07] & 0x0f) << 8);
+ detailed->h_front_porch = timing[0x08] | get_bits (timing[0x0b], 6, 7) << 8;
+ detailed->h_sync = timing[0x09] | get_bits (timing[0x0b], 4, 5) << 8;
+ detailed->v_front_porch =
+ get_bits (timing[0x0a], 4, 7) | get_bits (timing[0x0b], 2, 3) << 4;
+ detailed->v_sync =
+ get_bits (timing[0x0a], 0, 3) | get_bits (timing[0x0b], 0, 1) << 4;
+ detailed->width_mm = timing[0x0c] | get_bits (timing[0x0e], 4, 7) << 8;
+ detailed->height_mm = timing[0x0d] | get_bits (timing[0x0e], 0, 3) << 8;
+ detailed->right_border = timing[0x0f];
+ detailed->top_border = timing[0x10];
+
+ detailed->interlaced = get_bit (timing[0x11], 7);
+
+ /* Stereo */
+ bits = get_bits (timing[0x11], 5, 6) << 1 | get_bit (timing[0x11], 0);
+ detailed->stereo = stereo[bits];
+
+ /* Sync */
+ bits = timing[0x11];
+
+ detailed->digital_sync = get_bit (bits, 4);
+ if (detailed->digital_sync)
+ {
+ detailed->digital.composite = !get_bit (bits, 3);
+
+ if (detailed->digital.composite)
+ {
+ detailed->digital.serrations = get_bit (bits, 2);
+ detailed->digital.negative_vsync = FALSE;
+ }
+ else
+ {
+ detailed->digital.serrations = FALSE;
+ detailed->digital.negative_vsync = !get_bit (bits, 2);
+ }
+
+ detailed->digital.negative_hsync = !get_bit (bits, 0);
+ }
+ else
+ {
+ detailed->analog.bipolar = get_bit (bits, 3);
+ detailed->analog.serrations = get_bit (bits, 2);
+ detailed->analog.sync_on_green = !get_bit (bits, 1);
+ }
+}
+
+static int
+decode_descriptors (const uchar *edid, MonitorInfo *info)
+{
+ int i;
+ int timing_idx;
+
+ timing_idx = 0;
+
+ for (i = 0; i < 4; ++i)
+ {
+ int index = 0x36 + i * 18;
+
+ if (edid[index + 0] == 0x00 && edid[index + 1] == 0x00)
+ {
+ decode_display_descriptor (edid + index, info);
+ }
+ else
+ {
+ decode_detailed_timing (
+ edid + index, &(info->detailed_timings[timing_idx++]));
+ }
+ }
+
+ info->n_detailed_timings = timing_idx;
+
+ return TRUE;
+}
+
+static void
+decode_check_sum (const uchar *edid,
+ MonitorInfo *info)
+{
+ int i;
+ uchar check = 0;
+
+ for (i = 0; i < 128; ++i)
+ check += edid[i];
+
+ info->checksum = check;
+}
+
+MonitorInfo *
+decode_edid (const uchar *edid)
+{
+ MonitorInfo *info = calloc (1, sizeof (MonitorInfo));
+
+ decode_check_sum (edid, info);
+
+ if (!decode_header (edid))
+ return NULL;
+
+ if (!decode_vendor_and_product_identification (edid, info))
+ return NULL;
+
+ if (!decode_edid_version (edid, info))
+ return NULL;
+
+ if (!decode_display_parameters (edid, info))
+ return NULL;
+
+ if (!decode_color_characteristics (edid, info))
+ return NULL;
+
+ if (!decode_established_timings (edid, info))
+ return NULL;
+
+ if (!decode_standard_timings (edid, info))
+ return NULL;
+
+ if (!decode_descriptors (edid, info))
+ return NULL;
+
+ return info;
+}
diff -up /dev/null gnome-control-center-2.21.5/capplets/display/foo-marshal.h
--- /dev/null 2008-01-26 11:28:38.229690233 -0500
+++ gnome-control-center-2.21.5/capplets/display/foo-marshal.h 2008-01-29 16:47:04.000000000 -0500
@@ -0,0 +1,63 @@
+
+#ifndef __foo_marshal_MARSHAL_H__
+#define __foo_marshal_MARSHAL_H__
+
+#include <glib-object.h>
+
+G_BEGIN_DECLS
+
+/* VOID:OBJECT,OBJECT (marshal.list:1) */
+extern void foo_marshal_VOID__OBJECT_OBJECT (GClosure *closure,
+ GValue *return_value,
+ guint n_param_values,
+ const GValue *param_values,
+ gpointer invocation_hint,
+ gpointer marshal_data);
+
+/* VOID:UINT,UINT,UINT,UINT (marshal.list:2) */
+extern void foo_marshal_VOID__UINT_UINT_UINT_UINT (GClosure *closure,
+ GValue *return_value,
+ guint n_param_values,
+ const GValue *param_values,
+ gpointer invocation_hint,
+ gpointer marshal_data);
+
+/* VOID:UINT,UINT (marshal.list:3) */
+extern void foo_marshal_VOID__UINT_UINT (GClosure *closure,
+ GValue *return_value,
+ guint n_param_values,
+ const GValue *param_values,
+ gpointer invocation_hint,
+ gpointer marshal_data);
+
+/* VOID:BOXED (marshal.list:4) */
+#define foo_marshal_VOID__BOXED g_cclosure_marshal_VOID__BOXED
+
+/* VOID:BOXED,BOXED (marshal.list:5) */
+extern void foo_marshal_VOID__BOXED_BOXED (GClosure *closure,
+ GValue *return_value,
+ guint n_param_values,
+ const GValue *param_values,
+ gpointer invocation_hint,
+ gpointer marshal_data);
+
+/* VOID:POINTER,BOXED,POINTER (marshal.list:6) */
+extern void foo_marshal_VOID__POINTER_BOXED_POINTER (GClosure *closure,
+ GValue *return_value,
+ guint n_param_values,
+ const GValue *param_values,
+ gpointer invocation_hint,
+ gpointer marshal_data);
+
+/* VOID:POINTER,POINTER (marshal.list:7) */
+extern void foo_marshal_VOID__POINTER_POINTER (GClosure *closure,
+ GValue *return_value,
+ guint n_param_values,
+ const GValue *param_values,
+ gpointer invocation_hint,
+ gpointer marshal_data);
+
+G_END_DECLS
+
+#endif /* __foo_marshal_MARSHAL_H__ */
+
diff -up /dev/null gnome-control-center-2.21.5/capplets/display/scrollarea.c
--- /dev/null 2008-01-26 11:28:38.229690233 -0500
+++ gnome-control-center-2.21.5/capplets/display/scrollarea.c 2008-01-29 16:47:04.000000000 -0500
@@ -0,0 +1,1900 @@
+#include <gdk/gdkprivate.h> /* For GDK_PARENT_RELATIVE_BG */
+#include "scrollarea.h"
+#include "foo-marshal.h"
+
+G_DEFINE_TYPE (FooScrollArea, foo_scroll_area, GTK_TYPE_CONTAINER);
+
+static GtkWidgetClass *parent_class;
+
+typedef struct BackingStore BackingStore;
+
+typedef void (* ExposeFunc) (cairo_t *cr, GdkRegion *region, gpointer data);
+
+#if 0
+static void backing_store_draw (BackingStore *store,
+ GdkDrawable *dest,
+ GdkRegion *clip,
+ int dest_x,
+ int dest_y);
+static void backing_store_scroll (BackingStore *store,
+ int dx, int dy);
+static void backing_store_invalidate_rect (BackingStore *store,
+ GdkRectangle *rect);
+static void backing_store_invalidate_region (BackingStore *store,
+ GdkRegion *region);
+static void backing_store_invalidate_all (BackingStore *store);
+static BackingStore *backing_store_new (GdkWindow *window,
+ int width, int height);
+static void backing_store_resize (BackingStore *store,
+ int width, int height);
+static void backing_store_process_updates (BackingStore *store,
+ ExposeFunc func,
+ gpointer data);
+static void backing_store_free (BackingStore *store);
+#endif
+
+typedef struct InputPath InputPath;
+typedef struct InputRegion InputRegion;
+typedef struct AutoScrollInfo AutoScrollInfo;
+
+struct InputPath
+{
+ gboolean is_stroke;
+ cairo_fill_rule_t fill_rule;
+ double line_width;
+ cairo_path_t *path; /* In canvas coordinates */
+
+ FooScrollAreaEventFunc func;
+ gpointer data;
+
+ InputPath *next;
+};
+
+/* InputRegions are mutually disjoint */
+struct InputRegion
+{
+ GdkRegion *region; /* the boundary of this area in canvas coordinates */
+ InputPath *paths;
+};
+
+struct AutoScrollInfo
+{
+ int dx;
+ int dy;
+ int timeout_id;
+ int begin_x;
+ int begin_y;
+ double res_x;
+ double res_y;
+ GTimer *timer;
+};
+
+struct FooScrollAreaPrivate
+{
+ GdkWindow *input_window;
+
+ int width;
+ int height;
+
+ GtkAdjustment *hadj;
+ GtkAdjustment *vadj;
+ int x_offset;
+ int y_offset;
+
+ int min_width;
+ int min_height;
+
+ GPtrArray *input_regions;
+
+ AutoScrollInfo *auto_scroll_info;
+
+ /* During expose, this region is set to the region
+ * being exposed. At other times, it is NULL
+ *
+ * It is used for clipping of input areas
+ */
+ GdkRegion *expose_region;
+ InputRegion *current_input;
+
+ gboolean grabbed;
+ FooScrollAreaEventFunc grab_func;
+ gpointer grab_data;
+
+ GdkPixmap *pixmap;
+ GdkRegion *update_region; /* In canvas coordinates */
+};
+
+enum
+{
+ VIEWPORT_CHANGED,
+ PAINT,
+ INPUT,
+ LAST_SIGNAL,
+};
+
+static guint signals [LAST_SIGNAL] = { 0 };
+
+static void foo_scroll_area_size_request (GtkWidget *widget,
+ GtkRequisition *requisition);
+static gboolean foo_scroll_area_expose (GtkWidget *widget,
+ GdkEventExpose *expose);
+static void foo_scroll_area_size_allocate (GtkWidget *widget,
+ GtkAllocation *allocation);
+static void foo_scroll_area_set_scroll_adjustments (FooScrollArea *scroll_area,
+ GtkAdjustment *hadjustment,
+ GtkAdjustment *vadjustment);
+static void foo_scroll_area_realize (GtkWidget *widget);
+static void foo_scroll_area_unrealize (GtkWidget *widget);
+static void foo_scroll_area_map (GtkWidget *widget);
+static void foo_scroll_area_unmap (GtkWidget *widget);
+static gboolean foo_scroll_area_button_press (GtkWidget *widget,
+ GdkEventButton *event);
+static gboolean foo_scroll_area_button_release (GtkWidget *widget,
+ GdkEventButton *event);
+static gboolean foo_scroll_area_motion (GtkWidget *widget,
+ GdkEventMotion *event);
+
+static void
+foo_scroll_area_map (GtkWidget *widget)
+{
+ FooScrollArea *area = FOO_SCROLL_AREA (widget);
+
+ GTK_WIDGET_CLASS (parent_class)->map (widget);
+
+ if (area->priv->input_window)
+ gdk_window_show (area->priv->input_window);
+}
+
+static void
+foo_scroll_area_unmap (GtkWidget *widget)
+{
+ FooScrollArea *area = FOO_SCROLL_AREA (widget);
+
+ if (area->priv->input_window)
+ gdk_window_hide (area->priv->input_window);
+
+ GTK_WIDGET_CLASS (parent_class)->unmap (widget);
+}
+
+static void
+foo_scroll_area_finalize (GObject *object)
+{
+ FooScrollArea *scroll_area = FOO_SCROLL_AREA (object);
+
+ g_object_unref (scroll_area->priv->hadj);
+ g_object_unref (scroll_area->priv->vadj);
+
+ g_ptr_array_free (scroll_area->priv->input_regions, TRUE);
+
+ g_free (scroll_area->priv);
+
+ G_OBJECT_CLASS (foo_scroll_area_parent_class)->finalize (object);
+}
+
+static void
+foo_scroll_area_class_init (FooScrollAreaClass *class)
+{
+ GObjectClass *object_class = G_OBJECT_CLASS (class);
+ GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (class);
+
+ object_class->finalize = foo_scroll_area_finalize;
+ widget_class->size_request = foo_scroll_area_size_request;
+ widget_class->expose_event = foo_scroll_area_expose;
+ widget_class->size_allocate = foo_scroll_area_size_allocate;
+ widget_class->realize = foo_scroll_area_realize;
+ widget_class->unrealize = foo_scroll_area_unrealize;
+ widget_class->button_press_event = foo_scroll_area_button_press;
+ widget_class->button_release_event = foo_scroll_area_button_release;
+ widget_class->motion_notify_event = foo_scroll_area_motion;
+ widget_class->map = foo_scroll_area_map;
+ widget_class->unmap = foo_scroll_area_unmap;
+
+ class->set_scroll_adjustments = foo_scroll_area_set_scroll_adjustments;
+
+ parent_class = g_type_class_peek_parent (class);
+
+ signals[VIEWPORT_CHANGED] =
+ g_signal_new ("viewport_changed",
+ G_OBJECT_CLASS_TYPE (object_class),
+ G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
+ G_STRUCT_OFFSET (FooScrollAreaClass,
+ viewport_changed),
+ NULL, NULL,
+ foo_marshal_VOID__BOXED_BOXED,
+ G_TYPE_NONE, 2,
+ GDK_TYPE_RECTANGLE,
+ GDK_TYPE_RECTANGLE);
+
+ signals[PAINT] =
+ g_signal_new ("paint",
+ G_OBJECT_CLASS_TYPE (object_class),
+ G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
+ G_STRUCT_OFFSET (FooScrollAreaClass,
+ paint),
+ NULL, NULL,
+ foo_marshal_VOID__POINTER_BOXED_POINTER,
+ G_TYPE_NONE, 3,
+ G_TYPE_POINTER,
+ GDK_TYPE_RECTANGLE,
+ G_TYPE_POINTER);
+
+ widget_class->set_scroll_adjustments_signal =
+ g_signal_new ("set_scroll_adjustments",
+ G_OBJECT_CLASS_TYPE (object_class),
+ G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
+ G_STRUCT_OFFSET (FooScrollAreaClass,
+ set_scroll_adjustments),
+ NULL, NULL,
+ foo_marshal_VOID__OBJECT_OBJECT,
+ G_TYPE_NONE, 2,
+ GTK_TYPE_ADJUSTMENT,
+ GTK_TYPE_ADJUSTMENT);
+}
+
+static GtkAdjustment *
+new_adjustment (void)
+{
+ return GTK_ADJUSTMENT (gtk_adjustment_new (0.0, 0.0, 0.0, 0.0, 0.0, 0.0));
+}
+
+static void
+foo_scroll_area_init (FooScrollArea *scroll_area)
+{
+ GTK_WIDGET_SET_FLAGS (scroll_area, GTK_NO_WINDOW);
+
+ gtk_widget_set_redraw_on_allocate (GTK_WIDGET (scroll_area), FALSE);
+
+ scroll_area->priv = g_new0 (FooScrollAreaPrivate, 1);
+ scroll_area->priv->width = 0;
+ scroll_area->priv->height = 0;
+ scroll_area->priv->hadj = g_object_ref_sink (new_adjustment());
+ scroll_area->priv->vadj = g_object_ref_sink (new_adjustment());
+ scroll_area->priv->x_offset = 0.0;
+ scroll_area->priv->y_offset = 0.0;
+ scroll_area->priv->min_width = -1;
+ scroll_area->priv->min_height = -1;
+ scroll_area->priv->auto_scroll_info = NULL;
+ scroll_area->priv->input_regions = g_ptr_array_new ();
+ scroll_area->priv->pixmap = NULL;
+ scroll_area->priv->update_region = gdk_region_new ();
+
+ gtk_widget_set_double_buffered (GTK_WIDGET (scroll_area), FALSE);
+}
+
+static void
+translate_cairo_device (cairo_t *cr,
+ int x_offset,
+ int y_offset)
+{
+ cairo_surface_t *surface = cairo_get_target (cr);
+ double dev_x;
+ double dev_y;
+
+ cairo_surface_get_device_offset (surface, &dev_x, &dev_y);
+ dev_x += x_offset;
+ dev_y += y_offset;
+ cairo_surface_set_device_offset (surface, dev_x, dev_y);
+}
+
+#if 0
+static void
+print_region (const char *header, GdkRegion *region)
+{
+ GdkRectangle *rects;
+ int n_rects;
+ int i;
+
+ g_print ("%s\n", header);
+
+ gdk_region_get_rectangles (region, &rects, &n_rects);
+ for (i = 0; i < n_rects; ++i)
+ {
+ GdkRectangle *rect = &(rects[i]);
+ g_print (" %d %d %d %d\n",
+ rect->x, rect->y, rect->width, rect->height);
+ }
+}
+#endif
+
+typedef void (* PathForeachFunc) (double *x,
+ double *y,
+ gpointer data);
+
+static void
+path_foreach_point (cairo_path_t *path,
+ PathForeachFunc func,
+ gpointer user_data)
+{
+ int i;
+
+ for (i = 0; i < path->num_data; i += path->data[i].header.length)
+ {
+ cairo_path_data_t *data = &(path->data[i]);
+
+ switch (data->header.type)
+ {
+ case CAIRO_PATH_MOVE_TO:
+ case CAIRO_PATH_LINE_TO:
+ func (&(data[1].point.x), &(data[1].point.y), user_data);
+ break;
+
+ case CAIRO_PATH_CURVE_TO:
+ func (&(data[1].point.x), &(data[1].point.y), user_data);
+ func (&(data[2].point.x), &(data[2].point.y), user_data);
+ func (&(data[3].point.x), &(data[3].point.y), user_data);
+ break;
+
+ case CAIRO_PATH_CLOSE_PATH:
+ break;
+ }
+ }
+}
+
+typedef struct
+{
+ double x1, y1, x2, y2;
+} Box;
+
+#if 0
+static void
+update_box (double *x, double *y, gpointer data)
+{
+ Box *box = data;
+
+ if (*x < box->x1)
+ box->x1 = *x;
+
+ if (*y < box->y1)
+ box->y1 = *y;
+
+ if (*y > box->y2)
+ box->y2 = *y;
+
+ if (*x > box->x2)
+ box->x2 = *x;
+}
+#endif
+
+#if 0
+static void
+path_compute_extents (cairo_path_t *path,
+ GdkRectangle *rect)
+{
+ if (rect)
+ {
+ Box box = { G_MAXDOUBLE, G_MAXDOUBLE, G_MINDOUBLE, G_MINDOUBLE };
+
+ path_foreach_point (path, update_box, &box);
+
+ rect->x = box.x1;
+ rect->y = box.y1;
+ rect->width = box.x2 - box.x1;
+ rect->height = box.y2 - box.y1;
+ }
+}
+#endif
+
+static void
+input_path_free_list (InputPath *paths)
+{
+ if (!paths)
+ return;
+
+ input_path_free_list (paths->next);
+ cairo_path_destroy (paths->path);
+ g_free (paths);
+}
+
+static void
+input_region_free (InputRegion *region)
+{
+ input_path_free_list (region->paths);
+ gdk_region_destroy (region->region);
+
+ g_free (region);
+}
+
+static void
+get_viewport (FooScrollArea *scroll_area,
+ GdkRectangle *viewport)
+{
+ GtkWidget *widget = GTK_WIDGET (scroll_area);
+
+ viewport->x = scroll_area->priv->x_offset;
+ viewport->y = scroll_area->priv->y_offset;
+ viewport->width = widget->allocation.width;
+ viewport->height = widget->allocation.height;
+}
+
+static void
+allocation_to_canvas (FooScrollArea *area,
+ int *x,
+ int *y)
+{
+ *x += area->priv->x_offset;
+ *y += area->priv->y_offset;
+}
+
+static void
+clear_exposed_input_region (FooScrollArea *area,
+ GdkRegion *exposed) /* in canvas coordinates */
+{
+ int i;
+ GdkRegion *viewport;
+ GdkRectangle allocation;
+
+ allocation = GTK_WIDGET (area)->allocation;
+ allocation.x = 0;
+ allocation.y = 0;
+ allocation_to_canvas (area, &allocation.x, &allocation.y);
+ viewport = gdk_region_rectangle (&allocation);
+ gdk_region_subtract (viewport, exposed);
+
+ for (i = 0; i < area->priv->input_regions->len; ++i)
+ {
+ InputRegion *region = area->priv->input_regions->pdata[i];
+
+ gdk_region_intersect (region->region, viewport);
+
+ if (gdk_region_empty (region->region))
+ {
+ input_region_free (region);
+ g_ptr_array_remove_index_fast (area->priv->input_regions, i--);
+ }
+ }
+
+ gdk_region_destroy (viewport);
+
+#if 0
+ path = region->paths;
+ while (path != NULL)
+ {
+ GdkRectangle rect;
+
+ path_compute_extents (path->path, &rect);
+
+ if (gdk_region_rect_in (area->priv->expose_region, &rect) == GDK_OVERLAP_RECTANGLE_IN)
+ g_print ("we would have deleted it\n");
+#if 0
+ else
+ g_print ("nope (%d %d %d %d)\n", );
+#endif
+
+ path = path->next;
+ }
+
+ /* FIXME: we should also delete paths (and path segments)
+ * completely contained in the expose_region
+ */
+ }
+#endif
+}
+
+static void
+setup_background_cr (GdkWindow *window,
+ cairo_t *cr,
+ int x_offset,
+ int y_offset)
+{
+ GdkWindowObject *private = (GdkWindowObject *)window;
+
+ if (private->bg_pixmap == GDK_PARENT_RELATIVE_BG && private->parent)
+ {
+ x_offset += private->x;
+ y_offset += private->y;
+
+ setup_background_cr (GDK_WINDOW (private->parent), cr, x_offset, y_offset);
+ }
+ else if (private->bg_pixmap &&
+ private->bg_pixmap != GDK_PARENT_RELATIVE_BG &&
+ private->bg_pixmap != GDK_NO_BG)
+ {
+ gdk_cairo_set_source_pixmap (cr, private->bg_pixmap, -x_offset, -y_offset);
+ }
+ else
+ {
+ gdk_cairo_set_source_color (cr, &private->bg_color);
+ }
+}
+
+static void
+initialize_background (GtkWidget *widget,
+ cairo_t *cr)
+{
+ setup_background_cr (widget->window, cr, 0, 0);
+
+ cairo_paint (cr);
+}
+
+static void
+clip_to_region (cairo_t *cr, GdkRegion *region)
+{
+ int n_rects;
+ GdkRectangle *rects;
+
+ gdk_region_get_rectangles (region, &rects, &n_rects);
+
+ cairo_new_path (cr);
+ while (n_rects--)
+ {
+ GdkRectangle *rect = &(rects[n_rects]);
+
+ cairo_rectangle (cr, rect->x, rect->y, rect->width, rect->height);
+ }
+ cairo_clip (cr);
+
+ g_free (rects);
+}
+
+static void
+simple_draw_drawable (GdkDrawable *dst,
+ GdkDrawable *src,
+ int src_x,
+ int src_y,
+ int dst_x,
+ int dst_y,
+ int width,
+ int height)
+{
+ GdkGC *gc = gdk_gc_new (dst);
+
+ gdk_draw_drawable (dst, gc, src, src_x, src_y, dst_x, dst_y, width, height);
+
+ g_object_unref (gc);
+}
+
+static gboolean
+foo_scroll_area_expose (GtkWidget *widget,
+ GdkEventExpose *expose)
+{
+ FooScrollArea *scroll_area = FOO_SCROLL_AREA (widget);
+ cairo_t *cr;
+ GdkRectangle extents;
+ GdkRegion *region;
+ int x_offset, y_offset;
+ GdkGC *gc;
+
+ /* I don't think expose can ever recurse for the same area */
+ g_assert (!scroll_area->priv->expose_region);
+
+ /* Note that this function can be called at a time
+ * where the adj->value is different from x_offset.
+ * Ie., the GtkScrolledWindow changed the adj->value
+ * without emitting the value_changed signal.
+ *
+ * Hence we must always use the value we got
+ * the last time the signal was emitted, ie.,
+ * priv->{x,y}_offset.
+ */
+
+ x_offset = scroll_area->priv->x_offset;
+ y_offset = scroll_area->priv->y_offset;
+
+ scroll_area->priv->expose_region = expose->region;
+
+ /* Setup input areas */
+ clear_exposed_input_region (scroll_area, scroll_area->priv->update_region);
+
+ scroll_area->priv->current_input = g_new0 (InputRegion, 1);
+ scroll_area->priv->current_input->region = gdk_region_copy (scroll_area->priv->update_region);
+ scroll_area->priv->current_input->paths = NULL;
+ g_ptr_array_add (scroll_area->priv->input_regions,
+ scroll_area->priv->current_input);
+
+ region = scroll_area->priv->update_region;
+ scroll_area->priv->update_region = gdk_region_new ();
+
+ /* Create cairo context */
+ cr = gdk_cairo_create (scroll_area->priv->pixmap);
+ translate_cairo_device (cr, -x_offset, -y_offset);
+ clip_to_region (cr, region);
+ initialize_background (widget, cr);
+
+ /* Create regions */
+ gdk_region_get_clipbox (region, &extents);
+
+ g_signal_emit (widget, signals[PAINT], 0, cr, &extents, region);
+
+ /* Destroy stuff */
+ cairo_destroy (cr);
+
+ scroll_area->priv->expose_region = NULL;
+ scroll_area->priv->current_input = NULL;
+
+ /* Finally draw the backing pixmap */
+ gc = gdk_gc_new (widget->window);
+
+ gdk_gc_set_clip_region (gc, expose->region);
+
+ gdk_draw_drawable (widget->window, gc, scroll_area->priv->pixmap,
+ 0, 0, widget->allocation.x, widget->allocation.y,
+ widget->allocation.width, widget->allocation.height);
+
+ g_object_unref (gc);
+ gdk_region_destroy (region);
+
+ return TRUE;
+}
+
+void
+foo_scroll_area_get_viewport (FooScrollArea *scroll_area,
+ GdkRectangle *viewport)
+{
+ g_return_if_fail (FOO_IS_SCROLL_AREA (scroll_area));
+
+ if (!viewport)
+ return;
+
+ get_viewport (scroll_area, viewport);
+}
+
+static void
+process_event (FooScrollArea *scroll_area,
+ FooScrollAreaEventType input_type,
+ int x,
+ int y);
+
+static void
+emit_viewport_changed (FooScrollArea *scroll_area,
+ GdkRectangle *new_viewport,
+ GdkRectangle *old_viewport)
+{
+ int px, py;
+ g_signal_emit (scroll_area, signals[VIEWPORT_CHANGED], 0,
+ new_viewport, old_viewport);
+
+ gdk_window_get_pointer (scroll_area->priv->input_window, &px, &py, NULL);
+
+#if 0
+ g_print ("procc\n");
+#endif
+
+ process_event (scroll_area, FOO_MOTION, px, py);
+}
+
+static void
+clamp_adjustment (GtkAdjustment *adj)
+{
+ double old_value = adj->value;
+
+ if (adj->upper >= adj->page_size)
+ adj->value = CLAMP (adj->value, 0.0, adj->upper - adj->page_size);
+ else
+ adj->value = 0.0;
+
+ if (old_value != adj->value)
+ gtk_adjustment_value_changed (adj);
+
+ gtk_adjustment_changed (adj);
+}
+
+static gboolean
+set_adjustment_values (FooScrollArea *scroll_area)
+{
+ GtkAllocation *allocation = >K_WIDGET (scroll_area)->allocation;
+
+ GtkAdjustment *hadj = scroll_area->priv->hadj;
+ GtkAdjustment *vadj = scroll_area->priv->vadj;
+
+ /* Horizontal */
+ hadj->page_size = allocation->width;
+ hadj->step_increment = 0.1 * allocation->width;
+ hadj->page_increment = 0.9 * allocation->width;
+ hadj->lower = 0.0;
+ hadj->upper = scroll_area->priv->width;
+
+ /* Vertical */
+ vadj->page_size = allocation->height;
+ vadj->step_increment = 0.1 * allocation->height;
+ vadj->page_increment = 0.9 * allocation->height;
+ vadj->lower = 0.0;
+ vadj->upper = scroll_area->priv->height;
+
+ clamp_adjustment (hadj);
+ clamp_adjustment (vadj);
+
+ return TRUE;
+}
+
+static void
+foo_scroll_area_realize (GtkWidget *widget)
+{
+ FooScrollArea *area = FOO_SCROLL_AREA (widget);
+ GdkWindowAttr attributes;
+ gint attributes_mask;
+
+ GTK_WIDGET_SET_FLAGS (widget, GTK_REALIZED);
+
+ attributes.window_type = GDK_WINDOW_CHILD;
+ attributes.x = widget->allocation.x;
+ attributes.y = widget->allocation.y;
+ attributes.width = widget->allocation.width;
+ attributes.height = widget->allocation.height;
+ attributes.wclass = GDK_INPUT_ONLY;
+ attributes.event_mask = gtk_widget_get_events (widget);
+ attributes.event_mask |= (GDK_BUTTON_PRESS_MASK |
+ GDK_BUTTON_RELEASE_MASK |
+ GDK_BUTTON1_MOTION_MASK |
+ GDK_BUTTON2_MOTION_MASK |
+ GDK_BUTTON3_MOTION_MASK |
+ GDK_POINTER_MOTION_MASK |
+ GDK_ENTER_NOTIFY_MASK |
+ GDK_LEAVE_NOTIFY_MASK);
+
+ attributes_mask = GDK_WA_X | GDK_WA_Y;
+
+ widget->window = gtk_widget_get_parent_window (widget);
+ g_object_ref (widget->window);
+
+ area->priv->input_window = gdk_window_new (widget->window,
+ &attributes, attributes_mask);
+ area->priv->pixmap = gdk_pixmap_new (widget->window,
+ widget->allocation.width,
+ widget->allocation.height,
+ -1);
+ gdk_window_set_user_data (area->priv->input_window, area);
+
+ widget->style = gtk_style_attach (widget->style, widget->window);
+}
+
+static void
+foo_scroll_area_unrealize (GtkWidget *widget)
+{
+ FooScrollArea *area = FOO_SCROLL_AREA (widget);
+
+ if (area->priv->input_window)
+ {
+ gdk_window_set_user_data (area->priv->input_window, NULL);
+ gdk_window_destroy (area->priv->input_window);
+ area->priv->input_window = NULL;
+ }
+
+ GTK_WIDGET_CLASS (parent_class)->unrealize (widget);
+}
+
+static GdkPixmap *
+create_new_pixmap (GtkWidget *widget,
+ GdkPixmap *old)
+{
+ GdkPixmap *new = gdk_pixmap_new (widget->window,
+ widget->allocation.width,
+ widget->allocation.height,
+ -1);
+
+ /* Unfortunately we don't know in which direction we were resized,
+ * so we just assume we were dragged from the south-east corner.
+ *
+ * Although, maybe we could get the root coordinates of the input-window?
+ * That might just work, actually. We need to make sure metacity uses
+ * static gravity for the window before this will be useful.
+ */
+ simple_draw_drawable (new, old, 0, 0, 0, 0, -1, -1);
+
+ return new;
+}
+
+static void
+allocation_to_canvas_region (FooScrollArea *area,
+ GdkRegion *region)
+{
+ gdk_region_offset (region, area->priv->x_offset, area->priv->y_offset);
+}
+
+
+static void
+foo_scroll_area_size_allocate (GtkWidget *widget,
+ GtkAllocation *allocation)
+{
+ FooScrollArea *scroll_area = FOO_SCROLL_AREA (widget);
+ GdkRectangle new_viewport;
+ GdkRectangle old_viewport;
+ GdkRegion *old_allocation;
+ GdkRegion *invalid;
+
+ get_viewport (scroll_area, &old_viewport);
+
+ old_allocation = gdk_region_rectangle (&widget->allocation);
+ gdk_region_offset (old_allocation,
+ -widget->allocation.x, -widget->allocation.y);
+ invalid = gdk_region_rectangle (allocation);
+ gdk_region_offset (invalid, -allocation->x, -allocation->y);
+ gdk_region_subtract (invalid, old_allocation);
+ allocation_to_canvas_region (scroll_area, invalid);
+ foo_scroll_area_invalidate_region (scroll_area, invalid);
+ gdk_region_destroy (old_allocation);
+ gdk_region_destroy (invalid);
+
+ widget->allocation = *allocation;
+
+ if (scroll_area->priv->input_window)
+ {
+ GdkPixmap *new_pixmap;
+
+ gdk_window_move_resize (scroll_area->priv->input_window,
+ allocation->x, allocation->y,
+ allocation->width, allocation->height);
+
+ new_pixmap = create_new_pixmap (widget, scroll_area->priv->pixmap);
+
+ g_object_unref (scroll_area->priv->pixmap);
+
+ scroll_area->priv->pixmap = new_pixmap;
+ }
+
+ get_viewport (scroll_area, &new_viewport);
+
+ emit_viewport_changed (scroll_area, &new_viewport, &old_viewport);
+}
+
+static void
+emit_input (FooScrollArea *scroll_area,
+ FooScrollAreaEventType type,
+ int x,
+ int y,
+ FooScrollAreaEventFunc func,
+ gpointer data)
+{
+ FooScrollAreaEvent event;
+
+ if (!func)
+ return;
+
+ if (type != FOO_MOTION)
+ emit_input (scroll_area, FOO_MOTION, x, y, func, data);
+
+#if 0
+ x += scroll_area->priv->x_offset;
+ y += scroll_area->priv->y_offset;
+#endif
+
+ event.type = type;
+ event.x = x;
+ event.y = y;
+
+ func (scroll_area, &event, data);
+}
+
+#if 0
+static void
+print_path (const char *header,
+ cairo_path_t *path)
+{
+ int i;
+
+ g_print ("%s\n", header);
+
+ for (i=0; i < path->num_data; i += path->data[i].header.length)
+ {
+ cairo_path_data_t *data = &(path->data[i]);
+
+ switch (data->header.type)
+ {
+ case CAIRO_PATH_MOVE_TO:
+ g_print ("move to: %f, %f\n", data[1].point.x, data[1].point.y);
+ break;
+
+ case CAIRO_PATH_LINE_TO:
+ g_print ("line to: %f, %f\n", data[1].point.x, data[1].point.y);
+ break;
+
+ case CAIRO_PATH_CURVE_TO:
+ g_print ("curve to: %f, %f\n", data[1].point.x, data[1].point.y);
+ g_print (" %f, %f\n", data[1].point.x, data[1].point.y);
+ g_print (" %f, %f\n", data[1].point.x, data[1].point.y);
+ break;
+
+ case CAIRO_PATH_CLOSE_PATH:
+ break;
+ }
+ }
+}
+#endif
+
+static void
+process_event (FooScrollArea *scroll_area,
+ FooScrollAreaEventType input_type,
+ int x,
+ int y)
+{
+ GtkWidget *widget = GTK_WIDGET (scroll_area);
+ int i;
+
+ allocation_to_canvas (scroll_area, &x, &y);
+
+ if (scroll_area->priv->grabbed)
+ {
+ emit_input (scroll_area, input_type, x, y,
+ scroll_area->priv->grab_func,
+ scroll_area->priv->grab_data);
+ return;
+ }
+
+
+#if 0
+ x += widget->allocation.x;
+ y += widget->allocation.y;
+#endif
+
+#if 0
+ g_print ("number of input regions: %d\n", scroll_area->priv->input_regions->len);
+#endif
+
+ for (i = 0; i < scroll_area->priv->input_regions->len; ++i)
+ {
+ InputRegion *region = scroll_area->priv->input_regions->pdata[i];
+
+#if 0
+ g_print ("%d ", i);
+ print_region ("region:", region->region);
+#endif
+
+ if (gdk_region_point_in (region->region, x, y))
+ {
+ InputPath *path;
+
+ path = region->paths;
+ while (path)
+ {
+ cairo_t *cr;
+ gboolean inside;
+
+ cr = gdk_cairo_create (widget->window);
+ cairo_set_fill_rule (cr, path->fill_rule);
+ cairo_set_line_width (cr, path->line_width);
+ cairo_append_path (cr, path->path);
+
+ if (path->is_stroke)
+ inside = cairo_in_stroke (cr, x, y);
+ else
+ inside = cairo_in_fill (cr, x, y);
+
+ cairo_destroy (cr);
+
+ if (inside)
+ {
+ emit_input (scroll_area, input_type,
+ x, y,
+ path->func,
+ path->data);
+ return;
+ }
+
+ path = path->next;
+ }
+
+ /* Since the regions are all disjoint, no other region
+ * can match. Of course we could be clever and try and
+ * sort the regions, but so far I have been unable to
+ * make this loop show up on a profile.
+ */
+ return;
+ }
+ }
+}
+
+static void
+process_gdk_event (FooScrollArea *scroll_area,
+ int x,
+ int y,
+ GdkEvent *event)
+{
+ FooScrollAreaEventType input_type;
+
+ if (event->type == GDK_BUTTON_PRESS)
+ input_type = FOO_BUTTON_PRESS;
+ else if (event->type == GDK_BUTTON_RELEASE)
+ input_type = FOO_BUTTON_RELEASE;
+ else if (event->type == GDK_MOTION_NOTIFY)
+ input_type = FOO_MOTION;
+
+ process_event (scroll_area, input_type, x, y);
+}
+
+static gboolean
+foo_scroll_area_button_press (GtkWidget *widget,
+ GdkEventButton *event)
+{
+ FooScrollArea *area = FOO_SCROLL_AREA (widget);
+
+ process_gdk_event (area, event->x, event->y, (GdkEvent *)event);
+
+ return TRUE;
+}
+
+static gboolean
+foo_scroll_area_button_release (GtkWidget *widget,
+ GdkEventButton *event)
+{
+ FooScrollArea *area = FOO_SCROLL_AREA (widget);
+
+ process_gdk_event (area, event->x, event->y, (GdkEvent *)event);
+
+ return FALSE;
+}
+
+static gboolean
+foo_scroll_area_motion (GtkWidget *widget,
+ GdkEventMotion *event)
+{
+ FooScrollArea *area = FOO_SCROLL_AREA (widget);
+
+ process_gdk_event (area, event->x, event->y, (GdkEvent *)event);
+ return TRUE;
+}
+
+void
+foo_scroll_area_set_size_fixed_y (FooScrollArea *scroll_area,
+ int width,
+ int height,
+ int old_y,
+ int new_y)
+{
+ int dy = new_y - old_y;
+
+ scroll_area->priv->width = width;
+ scroll_area->priv->height = height;
+
+#if 0
+ g_print ("diff: %d\n", new_y - old_y);
+#endif
+
+ scroll_area->priv->vadj->value += dy;
+
+ set_adjustment_values (scroll_area);
+
+ if (dy != 0)
+ gtk_adjustment_value_changed (scroll_area->priv->vadj);
+}
+
+void
+foo_scroll_area_set_size (FooScrollArea *scroll_area,
+ int width,
+ int height)
+{
+ g_return_if_fail (FOO_IS_SCROLL_AREA (scroll_area));
+
+ /* FIXME: Default scroll algorithm should probably be to
+ * keep the same *area* outside the screen as before.
+ *
+ * For wrapper widgets that will do something roughly
+ * right. For widgets that don't change size, it
+ * will do the right thing. Except for idle-layouting
+ * widgets.
+ *
+ * Maybe there should be some generic support for those
+ * widgets. Can that even be done?
+ *
+ * Should we have a version of this function using
+ * fixed points?
+ */
+
+ scroll_area->priv->width = width;
+ scroll_area->priv->height = height;
+
+ set_adjustment_values (scroll_area);
+}
+
+static void
+foo_scroll_area_size_request (GtkWidget *widget,
+ GtkRequisition *requisition)
+{
+ FooScrollArea *scroll_area = FOO_SCROLL_AREA (widget);
+
+ requisition->width = scroll_area->priv->min_width;
+ requisition->height = scroll_area->priv->min_height;
+
+#if 0
+ g_print ("request %d %d\n", requisition->width, requisition->height);
+#endif
+}
+
+#if 0
+static void
+translate_point (double *x, double *y, gpointer data)
+{
+ int *translation = data;
+
+ *x += translation[0];
+ *y += translation[1];
+}
+#endif
+
+#if 0
+static void
+path_translate (cairo_path_t *path,
+ int dx,
+ int dy)
+{
+ int translation[2] = {dx, dy};
+
+ path_foreach_point (path, translate_point, translation);
+}
+#endif
+
+static void
+translate_input_regions (FooScrollArea *scroll_area,
+ int dx,
+ int dy)
+{
+#if 0
+ int i;
+
+ for (i = 0; i < scroll_area->priv->input_regions->len; ++i)
+ {
+ InputRegion *region = scroll_area->priv->input_regions->pdata[i];
+ InputPath *path;
+
+ gdk_region_offset (region->region, dx, dy);
+
+ path = region->paths;
+ while (path != NULL)
+ {
+ path_translate (path->path, dx, dy);
+ path = path->next;
+ }
+ }
+#endif
+}
+
+#if 0
+static void
+paint_region (FooScrollArea *area, GdkRegion *region)
+{
+ int n_rects;
+ GdkRectangle *rects;
+ region = gdk_region_copy (region);
+
+ gdk_region_get_rectangles (region, &rects, &n_rects);
+
+ gdk_region_offset (region,
+ GTK_WIDGET (area)->allocation.x,
+ GTK_WIDGET (area)->allocation.y);
+
+ GdkGC *gc = gdk_gc_new (GTK_WIDGET (area)->window);
+ gdk_gc_set_clip_region (gc, region);
+ gdk_draw_rectangle (GTK_WIDGET (area)->window, gc, TRUE, 0, 0, -1, -1);
+ g_object_unref (gc);
+ g_free (rects);
+}
+#endif
+
+static void
+foo_scroll_area_scroll (FooScrollArea *area,
+ gint dx,
+ gint dy)
+{
+ GdkRectangle allocation = GTK_WIDGET (area)->allocation;
+ GdkRectangle src_area;
+ GdkRectangle move_area;
+ GdkRegion *invalid_region;
+
+ allocation.x = 0;
+ allocation.y = 0;
+
+ src_area = allocation;
+ src_area.x -= dx;
+ src_area.y -= dy;
+
+ invalid_region = gdk_region_rectangle (&allocation);
+
+ if (gdk_rectangle_intersect (&allocation, &src_area, &move_area))
+ {
+ GdkRegion *move_region;
+
+#if 0
+ g_print ("scrolling %d %d %d %d (%d %d)\n",
+ move_area.x, move_area.y,
+ move_area.width, move_area.height,
+ dx, dy);
+#endif
+
+ simple_draw_drawable (area->priv->pixmap, area->priv->pixmap,
+ move_area.x, move_area.y,
+ move_area.x + dx, move_area.y + dy,
+ move_area.width, move_area.height);
+ gtk_widget_queue_draw (GTK_WIDGET (area));
+
+ move_region = gdk_region_rectangle (&move_area);
+ gdk_region_offset (move_region, dx, dy);
+ gdk_region_subtract (invalid_region, move_region);
+ gdk_region_destroy (move_region);
+ }
+
+#if 0
+ paint_region (area, invalid_region);
+#endif
+
+ allocation_to_canvas_region (area, invalid_region);
+
+ foo_scroll_area_invalidate_region (area, invalid_region);
+
+ gdk_region_destroy (invalid_region);
+}
+
+static void
+foo_scrollbar_adjustment_changed (GtkAdjustment *adj,
+ FooScrollArea *scroll_area)
+{
+ GtkWidget *widget = GTK_WIDGET (scroll_area);
+ gint dx = 0;
+ gint dy = 0;
+ GdkRectangle old_viewport, new_viewport;
+
+ get_viewport (scroll_area, &old_viewport);
+
+ if (adj == scroll_area->priv->hadj)
+ {
+ /* FIXME: do we treat the offset as int or double, and,
+ * if int, how do we round?
+ */
+ dx = (int)adj->value - scroll_area->priv->x_offset;
+ scroll_area->priv->x_offset = adj->value;
+ }
+ else if (adj == scroll_area->priv->vadj)
+ {
+ dy = (int)adj->value - scroll_area->priv->y_offset;
+ scroll_area->priv->y_offset = adj->value;
+ }
+ else
+ {
+ g_assert_not_reached ();
+ }
+
+ if (GTK_WIDGET_REALIZED (widget))
+ {
+ foo_scroll_area_scroll (scroll_area, -dx, -dy);
+
+#if 0
+ window_scroll_area (widget->window, &widget->allocation, -dx, -dy);
+#endif
+ translate_input_regions (scroll_area, -dx, -dy);
+
+#if 0
+ gdk_window_process_updates (widget->window, TRUE);
+#endif
+ }
+
+ get_viewport (scroll_area, &new_viewport);
+
+ emit_viewport_changed (scroll_area, &new_viewport, &old_viewport);
+}
+
+static void
+set_one_adjustment (FooScrollArea *scroll_area,
+ GtkAdjustment *adjustment,
+ GtkAdjustment **location)
+{
+ g_return_if_fail (location != NULL);
+
+ if (adjustment == *location)
+ return;
+
+ if (!adjustment)
+ adjustment = new_adjustment ();
+
+ g_return_if_fail (GTK_IS_ADJUSTMENT (adjustment));
+
+ if (*location)
+ {
+ g_signal_handlers_disconnect_by_func (
+ *location, foo_scrollbar_adjustment_changed, scroll_area);
+
+ g_object_unref (*location);
+ }
+
+ *location = adjustment;
+
+ g_object_ref_sink (*location);
+
+ g_signal_connect (*location, "value_changed",
+ G_CALLBACK (foo_scrollbar_adjustment_changed),
+ scroll_area);
+}
+
+static void
+foo_scroll_area_set_scroll_adjustments (FooScrollArea *scroll_area,
+ GtkAdjustment *hadjustment,
+ GtkAdjustment *vadjustment)
+{
+ set_one_adjustment (scroll_area, hadjustment, &scroll_area->priv->hadj);
+ set_one_adjustment (scroll_area, vadjustment, &scroll_area->priv->vadj);
+
+ set_adjustment_values (scroll_area);
+}
+
+FooScrollArea *
+foo_scroll_area_new (void)
+{
+ return g_object_new (FOO_TYPE_SCROLL_AREA, NULL);
+}
+
+void
+foo_scroll_area_set_min_size (FooScrollArea *scroll_area,
+ int min_width,
+ int min_height)
+{
+ scroll_area->priv->min_width = min_width;
+ scroll_area->priv->min_height = min_height;
+
+ /* FIXME: think through invalidation.
+ *
+ * Goals: - no repainting everything on size_allocate(),
+ * - make sure input boxes are invalidated when
+ * needed
+ */
+ gtk_widget_queue_resize (GTK_WIDGET (scroll_area));
+}
+
+#if 0
+static void
+warn_about_adding_input_outside_expose (const char *func)
+{
+ static gboolean warned = FALSE;
+
+ if (!warned)
+ {
+ g_warning ("%s() can only be called "
+ "from the paint handler for the FooScrollArea\n", func);
+
+ warned = TRUE;
+ }
+}
+#endif
+
+static void
+user_to_device (double *x, double *y,
+ gpointer data)
+{
+ cairo_t *cr = data;
+
+ cairo_user_to_device (cr, x, y);
+}
+
+static InputPath *
+make_path (FooScrollArea *area,
+ cairo_t *cr,
+ gboolean is_stroke,
+ FooScrollAreaEventFunc func,
+ gpointer data)
+{
+ InputPath *path = g_new0 (InputPath, 1);
+
+ path->is_stroke = is_stroke;
+ path->fill_rule = cairo_get_fill_rule (cr);
+ path->line_width = cairo_get_line_width (cr);
+ path->path = cairo_copy_path (cr);
+ path_foreach_point (path->path, user_to_device, cr);
+ path->func = func;
+ path->data = data;
+ path->next = area->priv->current_input->paths;
+ area->priv->current_input->paths = path;
+ return path;
+}
+
+/* FIXME: we probably really want a
+ *
+ * foo_scroll_area_add_input_from_fill (area, cr, ...);
+ * and
+ * foo_scroll_area_add_input_from_stroke (area, cr, ...);
+ * as well.
+ */
+void
+foo_scroll_area_add_input_from_fill (FooScrollArea *scroll_area,
+ cairo_t *cr,
+ FooScrollAreaEventFunc func,
+ gpointer data)
+{
+ g_return_if_fail (FOO_IS_SCROLL_AREA (scroll_area));
+ g_return_if_fail (cr != NULL);
+ g_return_if_fail (scroll_area->priv->current_input);
+
+ make_path (scroll_area, cr, FALSE, func, data);
+}
+
+void
+foo_scroll_area_add_input_from_stroke (FooScrollArea *scroll_area,
+ cairo_t *cr,
+ FooScrollAreaEventFunc func,
+ gpointer data)
+{
+ g_return_if_fail (FOO_IS_SCROLL_AREA (scroll_area));
+ g_return_if_fail (cr != NULL);
+ g_return_if_fail (scroll_area->priv->current_input);
+
+ make_path (scroll_area, cr, TRUE, func, data);
+}
+
+void
+foo_scroll_area_invalidate (FooScrollArea *scroll_area)
+{
+ GtkWidget *widget = GTK_WIDGET (scroll_area);
+
+ foo_scroll_area_invalidate_rect (scroll_area,
+ scroll_area->priv->x_offset, scroll_area->priv->y_offset,
+ widget->allocation.width,
+ widget->allocation.height);
+}
+
+static void
+canvas_to_window (FooScrollArea *area,
+ GdkRegion *region)
+{
+ GtkWidget *widget = GTK_WIDGET (area);
+
+ gdk_region_offset (region,
+ -area->priv->x_offset + widget->allocation.x,
+ -area->priv->y_offset + widget->allocation.y);
+}
+
+static void
+window_to_canvas (FooScrollArea *area,
+ GdkRegion *region)
+{
+ GtkWidget *widget = GTK_WIDGET (area);
+
+ gdk_region_offset (region,
+ area->priv->x_offset - widget->allocation.x,
+ area->priv->y_offset - widget->allocation.y);
+}
+
+void
+foo_scroll_area_invalidate_region (FooScrollArea *area,
+ GdkRegion *region)
+{
+ g_return_if_fail (FOO_IS_SCROLL_AREA (area));
+
+ gdk_region_union (area->priv->update_region, region);
+
+ if (GTK_WIDGET_REALIZED (area))
+ {
+ canvas_to_window (area, region);
+
+ gdk_window_invalidate_region (GTK_WIDGET (area)->window, region, TRUE);
+
+ window_to_canvas (area, region);
+ }
+}
+
+void
+foo_scroll_area_invalidate_rect (FooScrollArea *scroll_area,
+ int x,
+ int y,
+ int width,
+ int height)
+{
+ GdkRectangle rect = { x, y, width, height };
+ GdkRegion *region;
+
+ g_return_if_fail (FOO_IS_SCROLL_AREA (scroll_area));
+
+ region = gdk_region_rectangle (&rect);
+
+ foo_scroll_area_invalidate_region (scroll_area, region);
+
+ gdk_region_destroy (region);
+}
+
+void
+foo_scroll_area_begin_grab (FooScrollArea *scroll_area,
+ FooScrollAreaEventFunc func,
+ gpointer input_data)
+{
+ g_return_if_fail (FOO_IS_SCROLL_AREA (scroll_area));
+ g_return_if_fail (!scroll_area->priv->grabbed);
+
+ scroll_area->priv->grabbed = TRUE;
+ scroll_area->priv->grab_func = func;
+ scroll_area->priv->grab_data = input_data;
+
+ /* FIXME: we should probably take a server grab */
+ /* Also, maybe there should be support for setting the grab cursor */
+}
+
+void
+foo_scroll_area_end_grab (FooScrollArea *scroll_area)
+{
+ g_return_if_fail (FOO_IS_SCROLL_AREA (scroll_area));
+
+ scroll_area->priv->grabbed = FALSE;
+ scroll_area->priv->grab_func = NULL;
+ scroll_area->priv->grab_data = NULL;
+}
+
+gboolean
+foo_scroll_area_is_grabbed (FooScrollArea *scroll_area)
+{
+ return scroll_area->priv->grabbed;
+}
+
+void
+foo_scroll_area_set_viewport_pos (FooScrollArea *scroll_area,
+ int x,
+ int y)
+{
+ int x_changed = scroll_area->priv->hadj->value != (double)x;
+ int y_changed = scroll_area->priv->vadj->value != (double)y;
+
+ scroll_area->priv->hadj->value = x;
+ scroll_area->priv->vadj->value = y;
+
+ set_adjustment_values (scroll_area);
+
+ if (x_changed)
+ gtk_adjustment_value_changed (scroll_area->priv->hadj);
+
+ if (y_changed)
+ gtk_adjustment_value_changed (scroll_area->priv->vadj);
+}
+
+static gboolean
+rect_contains (const GdkRectangle *rect, int x, int y)
+{
+ return (x >= rect->x &&
+ y >= rect->y &&
+ x < rect->x + rect->width &&
+ y < rect->y + rect->height);
+}
+
+static void
+stop_scrolling (FooScrollArea *area)
+{
+#if 0
+ g_print ("stop scrolling\n");
+#endif
+ if (area->priv->auto_scroll_info)
+ {
+ g_source_remove (area->priv->auto_scroll_info->timeout_id);
+ g_timer_destroy (area->priv->auto_scroll_info->timer);
+ g_free (area->priv->auto_scroll_info);
+
+ area->priv->auto_scroll_info = NULL;
+ }
+}
+
+static gboolean
+scroll_idle (gpointer data)
+{
+ GdkRectangle viewport, new_viewport;
+ FooScrollArea *area = data;
+ AutoScrollInfo *info = area->priv->auto_scroll_info;
+#if 0
+ int dx, dy;
+#endif
+ int new_x, new_y;
+ double elapsed;
+
+ get_viewport (area, &viewport);
+
+#if 0
+ g_print ("old info: %d %d\n", info->dx, info->dy);
+
+ g_print ("timeout (%d %d)\n", dx, dy);
+#endif
+
+#if 0
+ viewport.x += info->dx;
+ viewport.y += info->dy;
+#endif
+
+#if 0
+ g_print ("new info %d %d\n", info->dx, info->dy);
+#endif
+
+ elapsed = g_timer_elapsed (info->timer, NULL);
+
+ info->res_x = elapsed * info->dx / 0.2;
+ info->res_y = elapsed * info->dy / 0.2;
+
+#if 0
+ g_print ("%f %f\n", info->res_x, info->res_y);
+#endif
+
+ new_x = viewport.x + info->res_x;
+ new_y = viewport.y + info->res_y;
+
+#if 0
+ g_print ("%f\n", elapsed * (info->dx / 0.2));
+#endif
+
+#if 0
+ g_print ("new_x, new_y\n: %d %d\n", new_x, new_y);
+#endif
+
+ foo_scroll_area_set_viewport_pos (area, new_x, new_y);
+#if 0
+ viewport.x + info->dx,
+ viewport.y + info->dy);
+#endif
+
+ get_viewport (area, &new_viewport);
+
+ if (viewport.x == new_viewport.x &&
+ viewport.y == new_viewport.y &&
+ (info->res_x > 1.0 ||
+ info->res_y > 1.0 ||
+ info->res_x < -1.0 ||
+ info->res_y < -1.0))
+ {
+ stop_scrolling (area);
+
+ /* stop scrolling if it didn't have an effect */
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
+static void
+ensure_scrolling (FooScrollArea *area,
+ int dx,
+ int dy)
+{
+ if (!area->priv->auto_scroll_info)
+ {
+#if 0
+ g_print ("start scrolling\n");
+#endif
+ area->priv->auto_scroll_info = g_new0 (AutoScrollInfo, 1);
+ area->priv->auto_scroll_info->timeout_id =
+ g_idle_add (scroll_idle, area);
+ area->priv->auto_scroll_info->timer = g_timer_new ();
+ }
+
+#if 0
+ g_print ("setting scrolling to %d %d\n", dx, dy);
+#endif
+
+#if 0
+ g_print ("dx, dy: %d %d\n", dx, dy);
+#endif
+
+ area->priv->auto_scroll_info->dx = dx;
+ area->priv->auto_scroll_info->dy = dy;
+}
+
+void
+foo_scroll_area_auto_scroll (FooScrollArea *scroll_area,
+ FooScrollAreaEvent *event)
+{
+ GdkRectangle viewport;
+
+ get_viewport (scroll_area, &viewport);
+
+ if (rect_contains (&viewport, event->x, event->y))
+ {
+ stop_scrolling (scroll_area);
+ }
+ else
+ {
+ int dx, dy;
+
+ dx = dy = 0;
+
+ if (event->y < viewport.y)
+ {
+ dy = event->y - viewport.y;
+ dy = MIN (dy + 2, 0);
+ }
+ else if (event->y >= viewport.y + viewport.height)
+ {
+ dy = event->y - (viewport.y + viewport.height - 1);
+ dy = MAX (dy - 2, 0);
+ }
+
+ if (event->x < viewport.x)
+ {
+ dx = event->x - viewport.x;
+ dx = MIN (dx + 2, 0);
+ }
+ else if (event->x >= viewport.x + viewport.width)
+ {
+ dx = event->x - (viewport.x + viewport.width - 1);
+ dx = MAX (dx - 2, 0);
+ }
+
+#if 0
+ g_print ("dx, dy: %d %d\n", dx, dy);
+#endif
+
+ ensure_scrolling (scroll_area, dx, dy);
+ }
+}
+
+void
+foo_scroll_area_begin_auto_scroll (FooScrollArea *scroll_area)
+{
+ /* noop for now */
+}
+
+void
+foo_scroll_area_end_auto_scroll (FooScrollArea *scroll_area)
+{
+ stop_scrolling (scroll_area);
+}
+
+
+
+#if 0
+/*
+ * Backing Store
+ */
+struct BackingStore
+{
+ GdkPixmap *pixmap;
+ GdkRegion *update_region;
+ int width;
+ int height;
+};
+
+static BackingStore *
+backing_store_new (GdkWindow *window,
+ int width, int height)
+{
+ BackingStore *store = g_new0 (BackingStore, 1);
+ GdkRectangle rect = { 0, 0, width, height };
+
+ store->pixmap = gdk_pixmap_new (window, width, height, -1);
+ store->update_region = gdk_region_rectangle (&rect);
+ store->width = width;
+ store->height = height;
+
+ return store;
+}
+
+static void
+backing_store_free (BackingStore *store)
+{
+ g_object_unref (store->pixmap);
+ gdk_region_destroy (store->update_region);
+ g_free (store);
+}
+
+static void
+backing_store_draw (BackingStore *store,
+ GdkDrawable *dest,
+ GdkRegion *clip,
+ int x,
+ int y)
+{
+ GdkGC *gc = gdk_gc_new (dest);
+
+ gdk_gc_set_clip_region (gc, clip);
+
+ gdk_draw_drawable (dest, gc, store->pixmap,
+ 0, 0, x, y, store->width, store->height);
+
+ g_object_unref (gc);
+}
+
+static void
+backing_store_scroll (BackingStore *store,
+ int dx,
+ int dy)
+{
+ GdkGC *gc = gdk_gc_new (store->pixmap);
+ GdkRectangle rect;
+
+ gdk_draw_drawable (store->pixmap, gc, store->pixmap,
+ 0, 0, dx, dy,
+ store->width, store->height);
+
+ /* Invalidate vertically */
+ rect.x = 0;
+ rect.width = store->width;
+
+ if (dy > 0)
+ {
+ rect.y = 0;
+ rect.height = dy;
+ }
+ else
+ {
+ rect.y = store->height + dy;
+ rect.y = -dy;
+ }
+
+ gdk_region_union_with_rect (store->update_region, &rect);
+
+ /* Invalidate horizontally */
+ rect.y = 0;
+ rect.height = store->height;
+
+ if (dx > 0)
+ {
+ rect.x = 0;
+ rect.width = dx;
+ }
+ else
+ {
+ rect.x = store->width + dx;
+ rect.width = -dx;
+ }
+
+ gdk_region_union_with_rect (store->update_region, &rect);
+}
+
+static void
+backing_store_invalidate_rect (BackingStore *store,
+ GdkRectangle *rect)
+{
+ gdk_region_union_with_rect (store->update_region, rect);
+}
+
+static void
+backing_store_invalidate_region (BackingStore *store,
+ GdkRegion *region)
+{
+ gdk_region_union (store->update_region, region);
+}
+
+static void
+backing_store_invalidate_all (BackingStore *store)
+{
+ GdkRectangle rect = { 0, 0, store->width, store->height };
+ gdk_region_destroy (store->update_region);
+ store->update_region = gdk_region_rectangle (&rect);
+}
+
+static void
+backing_store_resize (BackingStore *store,
+ int width,
+ int height)
+{
+ GdkPixmap *pixmap = gdk_pixmap_new (store->pixmap, width, height, -1);
+
+ /* Unfortunately we don't know in which direction we were resized,
+ * so we just assume we were dragged from the south-east corner.
+ *
+ * Although, maybe we could get the root coordinates of the input-window?
+ * That might just work, actually. We need to make sure metacity uses
+ * static gravity for the window before this will be useful.
+ */
+ simple_draw_drawable (pixmap, store->pixmap, 0, 0, 0, 0, -1, -1);
+
+ g_object_unref (store->pixmap);
+
+ store->pixmap = pixmap;
+
+ /* FIXME: invalidate uncovered strip only */
+
+ backing_store_invalidate_all (store);
+}
+
+static void
+cclip_to_region (cairo_t *cr, GdkRegion *region)
+{
+ int n_rects;
+ GdkRectangle *rects;
+
+ gdk_region_get_rectangles (region, &rects, &n_rects);
+
+ cairo_new_path (cr);
+ while (n_rects--)
+ {
+ GdkRectangle *rect = &(rects[n_rects]);
+
+ cairo_rectangle (cr, rect->x, rect->y, rect->width, rect->height);
+ }
+ cairo_clip (cr);
+
+ g_free (rects);
+}
+
+static void
+backing_store_process_updates (BackingStore *store,
+ ExposeFunc func,
+ gpointer data)
+{
+ cairo_t *cr = gdk_cairo_create (store->pixmap);
+ GdkRegion *region = store->update_region;
+ store->update_region = gdk_region_new ();
+
+ cclip_to_region (cr, store->update_region);
+
+ func (cr, store->update_region, data);
+
+ gdk_region_destroy (region);
+ cairo_destroy (cr);
+}
+
+#endif
diff -up /dev/null gnome-control-center-2.21.5/capplets/display/monitor-db.c
--- /dev/null 2008-01-26 11:28:38.229690233 -0500
+++ gnome-control-center-2.21.5/capplets/display/monitor-db.c 2008-01-29 16:47:04.000000000 -0500
@@ -0,0 +1,637 @@
+#include <stdlib.h>
+#include <string.h>
+#include <glib.h>
+#include "monitor-db.h"
+#include "edid.h"
+
+/* Code to load and save monitor configurations
+ *
+ * A monitor configuration is a list of monitors and associated
+ * outputs, along with information about mode and rotation.
+ *
+ * A monitor is identified by a string like EDID-SUN-0x0567
+ * which is decoded as "We got this information from EDID" - the manufacturer
+ * was "SUN" and the product code was 0x0567.
+ *
+ * Do we put this in gconf?
+ *
+ * The database itself is an XML file with entries like this:
+ *
+ * <configuration>
+ * <output name="VGA0">
+ * <monitor>EDID-SUN-0x0567-12341234</monitor>
+ * <mode>
+ * <state>on</state>
+ * <width>1024</width>
+ * <height>768</height>
+ * <rotation>0</rotation>
+ * </mode>
+ * </output>
+ * <output name="LVDS">
+ * <monitor>EDID-CMO-0x1210</monitor>
+ * <mode>
+ * <...>
+ * </mode>
+ * <output>
+ * </configuration>
+ *
+ */
+
+/* A helper wrapper around the GMarkup parser stuff */
+static gboolean parse_file_gmarkup (const gchar *file,
+ const GMarkupParser *parser,
+ gpointer data,
+ GError **err);
+
+typedef struct Parser Parser;
+
+static void
+emit_configurations (Configuration **configs,
+ GString *string)
+{
+ int i;
+
+ for (i = 0; configs[i] != NULL; ++i)
+ {
+ Configuration *config = configs[i];
+ int j;
+
+ g_string_append_printf (string, "<configuration>\n");
+
+ for (j = 0; config->outputs[j] != NULL; ++j)
+ {
+ Output *output = config->outputs[j];
+
+ g_string_append_printf (
+ string, " <output name=\"%s\">\n", output->name);
+
+ if (output->connected && *output->vendor != '\0')
+ {
+ g_string_append_printf (
+ string, " <vendor>%s</vendor>\n", output->vendor);
+ g_string_append_printf (
+ string, " <product>0x%04x</product>\n", output->product);
+ g_string_append_printf (
+ string, " <serial>0x%08x</serial>\n", output->serial);
+ }
+
+ if (output->on)
+ {
+ g_string_append_printf (
+ string, " <width>%d</width>\n", output->width);
+ g_string_append_printf (
+ string, " <height>%d</height>\n", output->height);
+ g_string_append_printf (
+ string, " <rate>%d</rate>\n", output->rate);
+ g_string_append_printf (
+ string, " <x>%d</x>\n", output->x);
+ g_string_append_printf (
+ string, " <y>%d</y>\n", output->y);
+ }
+
+ g_string_append_printf (string, " </output>\n");
+ }
+
+ g_string_append_printf (string, "</configuration>\n");
+ }
+}
+
+gboolean
+configurations_write (Configuration **configs,
+ const gchar *filename,
+ GError **error)
+{
+ GString *output = g_string_new ("");
+ gboolean result;
+
+ emit_configurations (configs, output);
+
+ result = g_file_set_contents (filename, output->str, -1, error);
+
+ g_string_free (output, TRUE);
+
+ return result;
+}
+
+void
+configurations_dump (Configuration **configs)
+{
+ GString *output = g_string_new ("");
+
+ emit_configurations (configs, output);
+
+ g_print ("%s\n", output->str);
+
+ g_string_free (output, TRUE);
+}
+
+/* Parser for monitor configurations */
+struct Parser
+{
+ Output *output;
+ Configuration *configuration;
+ GPtrArray *outputs;
+ GPtrArray *configurations;
+ GQueue *stack;
+};
+
+static int
+parse_int (const char *text)
+{
+ return strtol (text, NULL, 0);
+}
+
+static gboolean
+stack_is (Parser *parser,
+ const char *s1,
+ ...)
+{
+ GList *stack = NULL;
+ const char *s;
+ GList *l1, *l2;
+ va_list args;
+
+ stack = g_list_prepend (stack, (gpointer)s1);
+
+ va_start (args, s1);
+
+ s = va_arg (args, const char *);
+ while (s)
+ {
+ stack = g_list_prepend (stack, (gpointer)s);
+ s = va_arg (args, const char *);
+ }
+
+ l1 = stack;
+ l2 = parser->stack->head;
+
+ while (l1 && l2)
+ {
+ if (strcmp (l1->data, l2->data) != 0)
+ {
+ g_list_free (stack);
+ return FALSE;
+ }
+
+ l1 = l1->next;
+ l2 = l2->next;
+ }
+
+ g_list_free (stack);
+
+ return (!l1 && !l2);
+}
+
+static void
+handle_start_element (GMarkupParseContext *context,
+ const gchar *name,
+ const gchar **attr_names,
+ const gchar **attr_values,
+ gpointer user_data,
+ GError **err)
+{
+ Parser *parser = user_data;
+
+ if (strcmp (name, "output") == 0)
+ {
+ int i;
+ g_assert (parser->output == NULL);
+
+ parser->output = g_new0 (Output, 1);
+
+ for (i = 0; attr_names[i] != NULL; ++i)
+ {
+ if (strcmp (attr_names[i], "name") == 0)
+ {
+ parser->output->name = g_strdup (attr_values[i]);
+ break;
+ }
+ }
+
+ if (!parser->output->name)
+ {
+ /* This really shouldn't happen, but it's better to make
+ * something up than to crash later.
+ */
+ g_warning ("Malformed monitor configuration file");
+
+ parser->output->name = g_strdup ("VGA");
+ }
+ parser->output->connected = FALSE;
+ parser->output->on = FALSE;
+ }
+ else if (strcmp (name, "configuration") == 0)
+ {
+ g_assert (parser->configuration == NULL);
+
+ parser->configuration = g_new0 (Configuration, 1);
+ }
+
+ g_queue_push_tail (parser->stack, g_strdup (name));
+}
+
+static void
+handle_end_element (GMarkupParseContext *context,
+ const gchar *name,
+ gpointer user_data,
+ GError **err)
+{
+ Parser *parser = user_data;
+
+ if (strcmp (name, "output") == 0)
+ {
+ g_ptr_array_add (parser->outputs, parser->output);
+
+ g_print (
+ "%s output %s\n", parser->output->connected?
+ "Connected" : "Unconnected", parser->output->name);
+
+ parser->output = NULL;
+ }
+ else if (strcmp (name, "configuration") == 0)
+ {
+ g_ptr_array_add (parser->outputs, NULL);
+ parser->configuration->outputs =
+ (Output **)g_ptr_array_free (parser->outputs, FALSE);
+ parser->outputs = g_ptr_array_new ();
+ g_ptr_array_add (parser->configurations, parser->configuration);
+ parser->configuration = NULL;
+ }
+
+ g_free (g_queue_pop_tail (parser->stack));
+}
+
+static void
+handle_text (GMarkupParseContext *context,
+ const gchar *text,
+ gsize text_len,
+ gpointer user_data,
+ GError **err)
+{
+ Parser *parser = user_data;
+
+ if (stack_is (parser, "vendor", "output", "configuration", NULL))
+ {
+ parser->output->connected = TRUE;
+
+ strncpy (parser->output->vendor, text, 3);
+ parser->output->vendor[3] = 0;
+ }
+ else if (stack_is (parser, "product", "output", "configuration", NULL))
+ {
+ parser->output->connected = TRUE;
+
+ parser->output->product = parse_int (text);
+ }
+ else if (stack_is (parser, "serial", "output", "configuration", NULL))
+ {
+ parser->output->connected = TRUE;
+
+ parser->output->serial = parse_int (text);
+ }
+ else if (stack_is (parser, "width", "output", "configuration", NULL))
+ {
+ parser->output->on = TRUE;
+
+ parser->output->width = parse_int (text);
+ }
+ else if (stack_is (parser, "x", "output", "configuration", NULL))
+ {
+ parser->output->on = TRUE;
+
+ parser->output->x = parse_int (text);
+ }
+ else if (stack_is (parser, "y", "output", "configuration", NULL))
+ {
+ parser->output->on = TRUE;
+
+ parser->output->y = parse_int (text);
+ }
+ else if (stack_is (parser, "height", "output", "configuration", NULL))
+ {
+ parser->output->on = TRUE;
+
+ parser->output->height = parse_int (text);
+ }
+ else if (stack_is (parser, "rate", "output", "configuration", NULL))
+ {
+ parser->output->on = TRUE;
+
+ parser->output->rate = parse_int (text);
+ }
+ else
+ {
+ /* Ignore other properties so we can expand the format in the future */
+ }
+}
+
+static void
+output_free (Output *output)
+{
+ g_free (output);
+}
+
+void
+configuration_free (Configuration *config)
+{
+ int i;
+
+ for (i = 0; config->outputs[i] != NULL; ++i)
+ output_free (config->outputs[i]);
+}
+
+Configuration **
+configurations_add (Configuration **configurations,
+ Configuration *configuration)
+{
+ int i;
+ GPtrArray *a = g_ptr_array_new ();
+
+ for (i = 0; configurations[i] != NULL; ++i)
+ g_ptr_array_add (a, configurations[i]);
+
+ g_ptr_array_add (a, configuration);
+
+ g_ptr_array_add (a, NULL);
+
+ g_free (configurations);
+
+ return (Configuration **)g_ptr_array_free (a, FALSE);
+}
+
+static void
+parser_free (Parser *parser)
+{
+ int i;
+ GList *list;
+
+ if (parser->output)
+ output_free (parser->output);
+
+ if (parser->configuration)
+ configuration_free (parser->configuration);
+
+ for (i = 0; i < parser->outputs->len; ++i)
+ {
+ Output *output = parser->outputs->pdata[i];
+
+ output_free (output);
+ }
+
+ g_ptr_array_free (parser->outputs, TRUE);
+
+ for (i = 0; i < parser->configurations->len; ++i)
+ {
+ Configuration *config = parser->configurations->pdata[i];
+
+ configuration_free (config);
+ }
+
+ g_ptr_array_free (parser->configurations, TRUE);
+
+ for (list = parser->stack->head; list; list = list->next)
+ g_free (list->data);
+ g_queue_free (parser->stack);
+
+ g_free (parser);
+}
+
+Configuration **
+configurations_read (const gchar *filename, GError **error)
+{
+ Parser *parser = g_new0 (Parser, 1);
+ Configuration **result;
+ GMarkupParser callbacks = {
+ handle_start_element,
+ handle_end_element,
+ handle_text,
+ NULL, /* passthrough */
+ NULL, /* error */
+ };
+
+ parser->configurations = g_ptr_array_new ();
+ parser->outputs = g_ptr_array_new ();
+ parser->stack = g_queue_new ();
+
+ if (!parse_file_gmarkup (filename, &callbacks, parser, error))
+ {
+ result = NULL;
+ goto out;
+ }
+
+ g_ptr_array_add (parser->configurations, NULL);
+ result = (Configuration **)g_ptr_array_free (parser->configurations, FALSE);
+ parser->configurations = g_ptr_array_new ();
+
+out:
+ parser_free (parser);
+
+ return result;
+}
+
+static gboolean configuration_match (Configuration *c1,
+ Configuration *c2);
+
+Configuration *
+configuration_new_current (RWScreen *screen)
+{
+ Configuration *config = g_new0 (Configuration, 1);
+ GPtrArray *a = g_ptr_array_new ();
+ int i;
+ RWOutput **rw_outputs = rw_screen_list_outputs (screen);
+
+ g_print ("New configuration:\n");
+
+ for (i = 0; rw_outputs[i] != NULL; ++i)
+ {
+ RWOutput *rw_output = rw_outputs[i];
+ Output *output = g_new0 (Output, 1);
+ RWCrtc *crtc;
+
+ output->name = g_strdup (rw_output_get_name (rw_output));
+
+ crtc = rw_output_get_crtc (rw_output);
+ if (crtc)
+ {
+ RWMode *mode = rw_crtc_get_current_mode (crtc);
+
+ output->on = TRUE;
+ output->width = rw_mode_get_width (mode);
+ output->height = rw_mode_get_height (mode);
+ output->rate = rw_mode_get_freq (mode);
+
+ rw_crtc_get_position (crtc, &output->x, &output->y);
+
+ g_print ("new output %s %d %d\n", output->name, output->width, output->height);
+ }
+
+ output->connected = rw_output_is_connected (rw_output);
+
+ g_print (" Output %s is %sconnected\n", output->name, output->connected? "" : "not ");
+
+ if (output->connected)
+ {
+ const guchar *edid_data = rw_output_get_edid_data (rw_output);
+
+ if (edid_data)
+ {
+ MonitorInfo *info = decode_edid (edid_data);
+
+ memcpy (output->vendor, info->manufacturer_code,
+ sizeof (output->vendor));
+
+ output->product = info->product_code;
+ output->serial = info->serial_number;
+
+ g_free (info);
+ }
+ else
+ {
+ strcpy (output->vendor, "???");
+ output->product = 0;
+ output->serial = 0;
+ }
+ }
+ else
+ {
+ output->vendor[0] = '\0';
+ output->serial = 0;
+ output->product = 0;
+ }
+
+ g_ptr_array_add (a, output);
+ }
+
+ g_ptr_array_add (a, NULL);
+
+ config->outputs = (Output **)g_ptr_array_free (a, FALSE);
+
+ g_assert (configuration_match (config, config));
+
+ return config;
+}
+
+void
+configurations_free (Configuration **configurations)
+{
+ int i;
+
+ for (i = 0; configurations[i] != NULL; ++i)
+ configuration_free (configurations[i]);
+
+ g_free (configurations);
+}
+
+static gboolean
+parse_file_gmarkup (const gchar *filename,
+ const GMarkupParser *parser,
+ gpointer data,
+ GError **err)
+{
+ GMarkupParseContext *context = NULL;
+ gchar *contents = NULL;
+ gboolean result = TRUE;
+ guint len;
+
+ if (!g_file_get_contents (filename, &contents, &len, err))
+ {
+ result = FALSE;
+ goto out;
+ }
+
+ context = g_markup_parse_context_new (parser, 0, data, NULL);
+
+ if (!g_markup_parse_context_parse (context, contents, len, err))
+ {
+ result = FALSE;
+ goto out;
+ }
+
+ if (!g_markup_parse_context_end_parse (context, err))
+ {
+ result = FALSE;
+ goto out;
+ }
+
+out:
+ if (contents)
+ g_free (contents);
+
+ if (context)
+ g_markup_parse_context_free (context);
+
+ return result;
+}
+
+static gboolean
+output_match (Output *output1, Output *output2)
+{
+ if (strcmp (output1->name, output2->name) != 0)
+ return FALSE;
+
+ if (strcmp (output1->vendor, output2->vendor) != 0)
+ return FALSE;
+
+ if (output1->product != output2->product)
+ return FALSE;
+
+ if (output1->serial != output2->serial)
+ return FALSE;
+
+ if (output1->connected != output2->connected)
+ return FALSE;
+
+ g_print ("matched %s to %s\n", output1->name, output2->name);
+
+ return TRUE;
+}
+
+static Output *
+find_output (Configuration *config, const char *name)
+{
+ int i;
+
+ for (i = 0; config->outputs[i] != NULL; ++i)
+ {
+ Output *output = config->outputs[i];
+
+ if (strcmp (name, output->name) == 0)
+ return output;
+ }
+
+ return NULL;
+}
+
+static gboolean
+configuration_match (Configuration *c1,
+ Configuration *c2)
+{
+ int i;
+
+ for (i = 0; c1->outputs[i] != NULL; ++i)
+ {
+ Output *output1 = c1->outputs[i];
+ Output *output2;
+
+ output2 = find_output (c2, output1->name);
+ if (!output2 || !output_match (output1, output2))
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
+Configuration *
+configuration_find (Configuration **haystack,
+ Configuration *needle)
+{
+ int i;
+
+ for (i = 0; haystack[i] != NULL; ++i)
+ {
+ if (configuration_match (haystack[i], needle))
+ return haystack[i];
+ }
+
+ return NULL;
+}
diff -up /dev/null gnome-control-center-2.21.5/capplets/display/monitor-db.h
--- /dev/null 2008-01-26 11:28:38.229690233 -0500
+++ gnome-control-center-2.21.5/capplets/display/monitor-db.h 2008-01-29 16:47:04.000000000 -0500
@@ -0,0 +1,53 @@
+#define I_KNOW_THIS_IS_UNSTABLE_AND_ONLY_IN_FEDORA
+#include <libgnomeui/randrwrap.h>
+#include <glib.h>
+
+typedef struct Output Output;
+typedef struct Configuration Configuration;
+
+struct Output
+{
+ char *name;
+
+ gboolean on;
+ int width;
+ int height;
+ int rate;
+ int x;
+ int y;
+
+ gboolean connected;
+ char vendor[4];
+ guint product;
+ guint serial;
+};
+
+struct Configuration
+{
+ Output **outputs;
+};
+
+Output *output_new (const char *name,
+ gboolean on,
+ int width,
+ int height,
+ int rate,
+ gboolean connected,
+ char vendor[4],
+ guint product,
+ guint serial);
+Configuration *configuration_new (Output **outputs);
+Configuration **configurations_read (const gchar *filename,
+ GError **error);
+gboolean configurations_write (Configuration **configs,
+ const gchar *filename,
+ GError **error);
+void configuration_free (Configuration *configuration);
+Configuration **configurations_add (Configuration **configurations,
+ Configuration *configuration);
+void configurations_free (Configuration **configurations);
+void configurations_dump (Configuration **configurations);
+Configuration *configuration_new_current (RWScreen *screen);
+Configuration *configuration_find (Configuration **haystack,
+ Configuration *needle);
+
diff -up /dev/null gnome-control-center-2.21.5/capplets/display/crtc.h
--- /dev/null 2008-01-26 11:28:38.229690233 -0500
+++ gnome-control-center-2.21.5/capplets/display/crtc.h 2008-01-29 16:47:04.000000000 -0500
@@ -0,0 +1,21 @@
+#include <gtk/gtk.h>
+#define I_KNOW_THIS_IS_UNSTABLE_AND_ONLY_IN_FEDORA
+#include <libgnomeui/randrwrap.h>
+
+typedef struct Setting Setting;
+typedef struct CrtcInfo CrtcInfo;
+typedef struct CrtcAssignment CrtcAssignment;
+typedef struct Crtc Crtc;
+
+struct Setting
+{
+ RWOutput *output;
+ RWMode *mode;
+ int x;
+ int y;
+};
+
+void crtc_assignment_apply (CrtcAssignment *assign);
+CrtcAssignment *assign_crtcs (RWScreen *screen,
+ Setting **settings);
+void crtc_assignment_free (CrtcAssignment *assign);
diff -up /dev/null gnome-control-center-2.21.5/capplets/display/apply.h
--- /dev/null 2008-01-26 11:28:38.229690233 -0500
+++ gnome-control-center-2.21.5/capplets/display/apply.h 2008-01-29 16:47:04.000000000 -0500
@@ -0,0 +1,2 @@
+/* This should go in g-s-d eventually */
+void apply_stored_configuration (RWScreen *screen);
diff -up /dev/null gnome-control-center-2.21.5/capplets/display/xrandr-capplet.c
--- /dev/null 2008-01-26 11:28:38.229690233 -0500
+++ gnome-control-center-2.21.5/capplets/display/xrandr-capplet.c 2008-01-29 16:47:04.000000000 -0500
@@ -0,0 +1,809 @@
+/* Monitor Settings. A preference panel for configuring monitors
+ *
+ * Copyright (C) 2007, 2008 Red Hat, Inc.
+ *
+ * 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 2 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, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ * Author: Soren Sandmann <sandmann@redhat.com>
+ */
+
+#include <gtk/gtk.h>
+#include <glade/glade.h>
+#include <string.h>
+#include <stdlib.h>
+#include "scrollarea.h"
+#define I_KNOW_THIS_IS_UNSTABLE_AND_ONLY_IN_FEDORA
+#include <libgnomeui/randrwrap.h>
+#include "edid.h"
+#include "monitor-db.h"
+#include "crtc.h"
+#include "apply.h"
+
+typedef struct App App;
+
+struct App
+{
+ RWScreen *screen;
+ Configuration *current_configuration;
+ Configuration **all_configurations;
+ Output *current_output;
+
+ GtkWidget *dialog;
+ GtkWidget *resolution_combo;
+ GtkWidget *refresh_combo;
+ GtkWidget *rotation_combo;
+ GtkWidget *panel_checkbox;
+
+ GtkWidget *area;
+ gboolean ignore_gui_changes;
+};
+
+static void rebuild_gui (App *app);
+
+static void
+show_error (const GError *err)
+{
+ if (!err)
+ return;
+
+ GtkWidget *dialog = gtk_message_dialog_new (
+ NULL,
+ GTK_DIALOG_DESTROY_WITH_PARENT,
+ GTK_MESSAGE_WARNING,
+ GTK_BUTTONS_OK, err->message);
+
+ gtk_window_set_title (GTK_WINDOW (dialog), "");
+
+ gtk_dialog_run (GTK_DIALOG (dialog));
+ gtk_widget_destroy (dialog);
+}
+
+static gboolean
+init_app (App *app)
+{
+ return TRUE;
+}
+
+static gboolean
+do_free (gpointer data)
+{
+ g_free (data);
+ return FALSE;
+}
+
+static gchar *
+idle_free (gchar *s)
+{
+ g_idle_add (do_free, s);
+
+ return s;
+}
+
+static const gchar *
+get_filename (void)
+{
+ return idle_free (
+ g_build_filename (
+ g_get_home_dir(), ".gnome2", "monitors.xml", NULL));
+}
+
+static int
+compare_outputs (const void *p1, const void *p2)
+{
+ Output *const *o1 = p1;
+ Output *const *o2 = p2;
+
+ return (**o1).x - (**o2).x;
+}
+
+static void
+on_screen_changed (RWScreen *scr,
+ gpointer data)
+{
+ Configuration *current;
+ Configuration *found;
+ App *app = data;
+ int i;
+ Output *best;
+
+ g_print ("screen changed\n");
+
+ current = configuration_new_current (app->screen);
+
+ found = configuration_find (app->all_configurations, current);
+ if (found)
+ {
+ g_print ("config found\n");
+
+ configuration_free (current);
+
+ current = found;
+ }
+ else
+ {
+ g_print ("config not found, adding\n");
+
+ app->all_configurations = configurations_add (
+ app->all_configurations, current);
+ }
+
+ g_assert (configuration_find (app->all_configurations, current));
+
+ app->current_configuration = current;
+
+ /* Sort outputs according to X coordinate */
+
+ for (i = 0; app->current_configuration->outputs[i] != NULL; ++i)
+ ;
+
+ qsort (app->current_configuration->outputs, i, sizeof (Output *),
+ compare_outputs);
+
+ /* Select an output */
+
+ best = NULL;
+ for (i = 0; app->current_configuration->outputs[i] != NULL; ++i)
+ {
+ Output *output = app->current_configuration->outputs[i];
+
+ if (output->connected)
+ {
+ char *cur_name = app->current_output? app->current_output->name : NULL;
+
+ if ((cur_name && strcmp (output->name, cur_name) == 0) || !best)
+ best = output;
+ }
+ }
+
+ app->current_output = best;
+
+ rebuild_gui (app);
+}
+
+static void
+on_viewport_changed (FooScrollArea *scroll_area,
+ GdkRectangle *old_viewport,
+ GdkRectangle *new_viewport)
+{
+ foo_scroll_area_set_size (scroll_area,
+ new_viewport->width,
+ new_viewport->height);
+
+ foo_scroll_area_invalidate (scroll_area);
+}
+
+static void
+layout_set_font (PangoLayout *layout, const char *font)
+{
+ PangoFontDescription *desc =
+ pango_font_description_from_string (font);
+
+ if (desc)
+ {
+ pango_layout_set_font_description (layout, desc);
+
+ pango_font_description_free (desc);
+ }
+}
+
+static void
+clear_combo (GtkWidget *widget)
+{
+ GtkComboBox *box = GTK_COMBO_BOX (widget);
+ GtkTreeModel *model = gtk_combo_box_get_model (box);
+ if (!model)
+ {
+ g_print ("asdf\n");
+ return;
+ }
+ GtkListStore *store = GTK_LIST_STORE (model);
+
+ gtk_list_store_clear (store);
+}
+
+static void
+add_keys (GtkWidget *widget,
+ GHashTable *table,
+ const char *selected)
+{
+ /* FIXME: sort the keys */
+ GtkComboBox *box = GTK_COMBO_BOX (widget);
+ GList *keys = g_hash_table_get_keys (table);
+ GList *values = g_hash_table_get_values (table);
+ GList *list;
+ gboolean did_select = FALSE;
+ GtkListStore *store = GTK_LIST_STORE (gtk_combo_box_get_model (box));
+
+ for (list = keys; list != NULL; list = list->next, values = values->next)
+ {
+ GtkTreeIter iter;
+ char *resolution = list->data;
+ RWMode *mode = values->data;
+ int w, h;
+
+ w = rw_mode_get_width (mode);
+ h = rw_mode_get_height (mode);
+
+ gtk_list_store_append (store, &iter);
+ gtk_list_store_set (store, &iter,
+ 0, resolution,
+ 1, w,
+ 2, h,
+ 3, rw_mode_get_freq (mode),
+ 4, w * h,
+ -1);
+
+ if (strcmp (selected, resolution) == 0)
+ {
+ gtk_combo_box_set_active_iter (box, &iter);
+ did_select = TRUE;
+ }
+ }
+
+ if (!did_select)
+ gtk_combo_box_set_active (box, 0);
+
+ g_list_free (keys);
+}
+
+static RWMode **
+get_current_modes (App *app)
+{
+ RWOutput *output;
+
+ if (!app->current_output)
+ return NULL;
+
+ output = rw_screen_get_output_by_name (
+ app->screen, app->current_output->name);
+
+ if (!output)
+ return NULL;
+
+ return rw_output_list_modes (output);
+}
+
+static void
+rebuild_rate_combo (App *app)
+{
+ GHashTable *rates = g_hash_table_new_full (
+ g_str_hash, g_str_equal, (GFreeFunc)g_free, NULL);
+ int i;
+ RWMode **modes;
+
+ clear_combo (app->refresh_combo);
+
+ if (!(modes = get_current_modes (app)))
+ return;
+
+ for (i = 0; modes[i] != NULL; ++i)
+ {
+ RWMode *mode = modes[i];
+
+ if (rw_mode_get_width (mode) == app->current_output->width &&
+ rw_mode_get_height (mode) == app->current_output->height)
+ {
+ char *rate;
+
+ rate = g_strdup_printf ("%d Hz",
+ rw_mode_get_freq (mode));
+
+ g_hash_table_insert (rates, rate, mode);
+ }
+ }
+
+ add_keys (app->refresh_combo, rates, idle_free (
+ g_strdup_printf ("%d Hz",
+ app->current_output->rate)));
+
+ g_hash_table_destroy (rates);
+}
+
+static void
+rebuild_resolution_combo (App *app)
+{
+ GHashTable *resolutions;
+ int i;
+ RWMode **modes;
+
+ clear_combo (app->resolution_combo);
+
+ if (!(modes = get_current_modes (app)))
+ return;
+
+ resolutions = g_hash_table_new_full (
+ g_str_hash, g_str_equal, (GFreeFunc)g_free, NULL);
+
+ for (i = 0; modes[i] != NULL; ++i)
+ {
+ RWMode *mode = modes[i];
+ char *resolution =
+ g_strdup_printf ("%d x %d",
+ rw_mode_get_width (mode),
+ rw_mode_get_height (mode));
+
+ g_hash_table_insert (resolutions, resolution, mode);
+ }
+
+ /* Add resolutions */
+ add_keys (app->resolution_combo, resolutions, idle_free (
+ g_strdup_printf ("%d x %d",
+ app->current_output->width,
+ app->current_output->height)));
+
+ g_hash_table_destroy (resolutions);
+}
+
+static void
+rebuild_gui (App *app)
+{
+ gboolean sensitive;
+
+ /* We would break spectacularly if we recursed, so
+ * just assert if that happens
+ */
+ g_assert (app->ignore_gui_changes == FALSE);
+
+ app->ignore_gui_changes = TRUE;
+
+ sensitive = app->current_output? TRUE : FALSE;
+
+ rebuild_resolution_combo (app);
+ rebuild_rate_combo (app);
+
+ gtk_widget_set_sensitive (app->resolution_combo, sensitive);
+ gtk_widget_set_sensitive (app->refresh_combo, sensitive);
+ gtk_widget_set_sensitive (app->rotation_combo, sensitive);
+ gtk_widget_set_sensitive (app->panel_checkbox, sensitive);
+
+ app->ignore_gui_changes = FALSE;
+}
+
+static gboolean
+get_mode (GtkWidget *widget, int *width, int *height, int *freq)
+{
+ GtkTreeIter iter;
+ GtkTreeModel *model;
+ GtkComboBox *box = GTK_COMBO_BOX (widget);
+ int dummy;
+
+ if (!gtk_combo_box_get_active_iter (box, &iter))
+ return FALSE;
+
+ if (!width)
+ width = &dummy;
+ if (!height)
+ height = &dummy;
+ if (!freq)
+ freq = &dummy;
+
+ model = gtk_combo_box_get_model (box);
+ gtk_tree_model_get (model, &iter,
+ 1, width,
+ 2, height,
+ 3, freq,
+ -1);
+
+ return TRUE;
+}
+
+static void
+on_rate_changed (GtkComboBox *box, gpointer data)
+{
+ App *app = data;
+ int rate;
+
+ if (!app->current_output)
+ return;
+
+ if (get_mode (app->refresh_combo, NULL, NULL, &rate))
+ app->current_output->rate = rate;
+
+ foo_scroll_area_invalidate (FOO_SCROLL_AREA (app->area));
+}
+
+static void
+on_resolution_changed (GtkComboBox *box, gpointer data)
+{
+ App *app = data;
+ int width;
+ int height;
+ int i;
+ int x;
+
+ if (!app->current_output)
+ return;
+
+ if (get_mode (app->resolution_combo, &width, &height, NULL))
+ {
+ app->current_output->width = width;
+ app->current_output->height = height;
+ }
+
+ if (app->current_configuration)
+ {
+ x = 0;
+ for (i = 0; app->current_configuration->outputs[i] != NULL; ++i)
+ {
+ Output *output = app->current_configuration->outputs[i];
+
+ if (output->connected)
+ {
+ output->x = x;
+
+ x += output->width;
+ }
+ }
+ }
+
+ rebuild_rate_combo (app);
+
+ foo_scroll_area_invalidate (FOO_SCROLL_AREA (app->area));
+}
+
+static void
+on_output_event (FooScrollArea *area,
+ FooScrollAreaEvent *event,
+ gpointer data)
+{
+ Output *output = data;
+ App *app = g_object_get_data (G_OBJECT (area), "app");
+
+ if (event->type == FOO_BUTTON_PRESS)
+ {
+ app->current_output = output;
+
+ rebuild_gui (app);
+
+ foo_scroll_area_invalidate (area);
+ }
+}
+
+#if 0
+static void
+on_canvas_event (FooScrollArea *area,
+ FooScrollAreaEvent *event,
+ gpointer data)
+{
+ App *app = g_object_get_data (G_OBJECT (area), "app");
+
+ if (event->type == FOO_BUTTON_PRESS)
+ {
+ app->current_output = NULL;
+
+ rebuild_gui (app);
+
+ foo_scroll_area_invalidate (area);
+ }
+}
+#endif
+
+static PangoLayout *
+get_display_name (App *app,
+ Output *output)
+{
+ RWOutput *rwout = rw_screen_get_output_by_name (
+ app->screen, output->name);
+ const char *text = "Unknown";
+
+ if (rwout)
+ {
+ const guint8 *edid = rw_output_get_edid_data (rwout);
+
+ if (edid)
+ {
+ MonitorInfo *info = decode_edid (edid);
+
+ text = idle_free (make_display_name (info));
+ }
+ }
+
+ return gtk_widget_create_pango_layout (GTK_WIDGET (app->area), text);
+}
+
+static void
+on_area_paint (FooScrollArea *area,
+ cairo_t *cr,
+ GdkRectangle *extent,
+ GdkRegion *region,
+ gpointer data)
+{
+ App *app = data;
+ GdkRectangle viewport;
+ int i;
+ int available;
+ int total_pixels;
+ int max_height;
+ double scale;
+ GList *connected_outputs = NULL;
+ GList *list;
+ int n_monitors;
+
+ foo_scroll_area_get_viewport (area, &viewport);
+
+ cairo_set_source_rgba (cr, 0.72, 0.78, 0.87, 1.0);
+
+ cairo_rectangle (cr,
+ viewport.x, viewport.y,
+ viewport.width, viewport.height);
+
+ cairo_fill_preserve (cr);
+
+#if 0
+ foo_scroll_area_add_input_from_fill (area, cr, on_canvas_event, NULL);
+#endif
+
+ cairo_set_source_rgba (cr, 0.44, 0.59, 0.76, 1.0);
+
+ cairo_stroke (cr);
+
+ if (!app->current_configuration)
+ return;
+
+#define SPACE 36
+#define MARGIN 24
+
+ for (i = 0; app->current_configuration->outputs[i] != NULL; ++i)
+ {
+ Output *output = app->current_configuration->outputs[i];
+
+ if (output->connected)
+ connected_outputs = g_list_prepend (connected_outputs, output);
+ }
+
+ connected_outputs = g_list_reverse (connected_outputs);
+
+ n_monitors = g_list_length (connected_outputs);
+
+ available = viewport.width - 2 * MARGIN - (n_monitors - 1) * SPACE;
+
+ total_pixels = 0;
+ max_height = 0;
+ for (list = connected_outputs; list != NULL; list = list->next)
+ {
+ Output *output = list->data;
+
+ total_pixels += output->width;
+ max_height = MAX (max_height, output->height);
+ }
+
+ scale = (double)available / total_pixels;
+
+ scale = MIN ((double)(viewport.height - 2 * MARGIN) / max_height, scale);
+
+ int x = MARGIN;
+ for (list = connected_outputs; list != NULL; list = list->next)
+ {
+ Output *output = list->data;
+ int w = output->width * scale + 0.5;
+ PangoLayout *layout = get_display_name (app, output);
+
+ g_print ("%s (%p) geometry %d %d %d\n", output->name, output,
+ output->width, output->height, output->rate);
+
+ cairo_rectangle (cr, x, MARGIN, w, (int)(
+ output->height * scale + 0.5));
+
+ cairo_set_source_rgba (cr, 1.0, 1.0, 1.0, 1.0);
+ cairo_fill_preserve (cr);
+
+ foo_scroll_area_add_input_from_fill (area, cr, on_output_event,
+ output);
+
+ if (output == app->current_output)
+ cairo_set_source_rgba (cr, 1.0, 0.0, 0.0, 1.0);
+ else
+ cairo_set_source_rgba (cr, 0.0, 0.0, 0.0, 1.0);
+ cairo_stroke (cr);
+
+ cairo_move_to (cr, x + 40, MARGIN + 40);
+ cairo_set_source_rgb (cr, 0.2, 0.2, 0.8);
+ layout_set_font (layout, "Sans Bold 18");
+ pango_cairo_show_layout (cr, layout);
+ g_object_unref (layout);
+
+ x += w + SPACE;
+ }
+}
+
+static void
+make_text_combo (GtkWidget *widget, int column)
+{
+ GtkComboBox *box = GTK_COMBO_BOX (widget);
+ GtkListStore *store = gtk_list_store_new (
+ 5,
+ G_TYPE_STRING, /* Text */
+ G_TYPE_INT, /* Width */
+ G_TYPE_INT, /* Height */
+ G_TYPE_INT, /* Frequency */
+ G_TYPE_INT); /* Width * Height */
+ GtkCellRenderer *cell;
+
+ gtk_combo_box_set_model (box, GTK_TREE_MODEL (store));
+
+ cell = gtk_cell_renderer_text_new ();
+ gtk_cell_layout_pack_start (GTK_CELL_LAYOUT (box), cell, TRUE);
+ gtk_cell_layout_set_attributes (GTK_CELL_LAYOUT (box), cell,
+ "text", 0,
+ NULL);
+
+ if (column != -1)
+ {
+ gtk_tree_sortable_set_sort_column_id (GTK_TREE_SORTABLE (store),
+ column,
+ GTK_SORT_DESCENDING);
+ }
+}
+
+static void
+apply (App *app)
+{
+#if 0
+ GPtrArray *array = g_ptr_array_new ();
+#endif
+
+ g_print ("apply\n");
+
+ if (configurations_write (app->all_configurations,
+ get_filename(), NULL))
+ {
+ g_print ("wrote\n");
+
+ configurations_dump (app->all_configurations);
+
+ apply_stored_configuration (app->screen);
+
+ g_print ("done applying\n");
+ }
+
+#if 0
+ g_print ("This is where we should actually set the mode, \n");
+ g_print ("and if accepted, write the configuration file\n");
+ g_print ("Well, we should check if the mode is possible\n");
+ g_print ("write the configuration file and signal gsd to set the mode\n");
+#endif
+}
+
+static gboolean
+refresh (gpointer data)
+{
+ App *app = data;
+
+ rw_screen_refresh (app->screen);
+
+ return TRUE;
+}
+
+static void
+run_application (App *app)
+{
+#ifndef GLADEDIR
+#define GLADEDIR ""
+#endif
+#define GLADE_FILE GLADEDIR "/display-capplet.glade"
+ GladeXML *xml;
+ GtkWidget *align;
+
+ xml = glade_xml_new (GLADE_FILE, NULL, NULL);
+ if (!xml)
+ {
+ g_warning ("Could not open " GLADE_FILE);
+ return;
+ }
+
+ app->screen = rw_screen_new (gdk_screen_get_default(),
+ on_screen_changed, app);
+
+ app->dialog = glade_xml_get_widget (xml, "dialog");
+
+ app->resolution_combo = glade_xml_get_widget (xml, "resolution_combo");
+ g_signal_connect (app->resolution_combo, "changed",
+ G_CALLBACK (on_resolution_changed), app);
+
+ app->refresh_combo = glade_xml_get_widget (xml, "refresh_combo");
+ g_signal_connect (app->refresh_combo, "changed",
+ G_CALLBACK (on_rate_changed), app);
+
+ app->rotation_combo = glade_xml_get_widget (xml, "rotation_combo");
+
+
+ app->panel_checkbox = glade_xml_get_widget (xml, "panel_checkbox");
+
+ make_text_combo (app->resolution_combo, 4);
+ make_text_combo (app->refresh_combo, 3);
+ make_text_combo (app->rotation_combo, -1);
+
+ g_assert (app->panel_checkbox);
+
+ /* Scroll Area */
+ app->area = (GtkWidget *)foo_scroll_area_new ();
+
+ g_object_set_data (G_OBJECT (app->area), "app", app);
+
+ /* FIXME: this should be computed dynamically */
+ foo_scroll_area_set_min_size (FOO_SCROLL_AREA (app->area), -1, 200);
+ gtk_widget_show (app->area);
+ g_signal_connect (app->area, "paint",
+ G_CALLBACK (on_area_paint), app);
+ g_signal_connect (app->area, "viewport_changed",
+ G_CALLBACK (on_viewport_changed), app);
+
+ align = glade_xml_get_widget (xml, "align");
+
+ gtk_container_add (GTK_CONTAINER (align), app->area);
+
+ app->all_configurations = configurations_read (get_filename(), NULL);
+ if (!app->all_configurations)
+ {
+ app->all_configurations = g_malloc (sizeof (gpointer));
+ *(app->all_configurations) = NULL;
+ }
+
+ g_timeout_add (3000, refresh, app);
+
+ on_screen_changed (app->screen, app);
+ rebuild_gui (app);
+
+restart:
+ switch (gtk_dialog_run (GTK_DIALOG (app->dialog)))
+ {
+ default:
+ g_print ("Unknown response\n");
+ /* Fall Through */
+ case GTK_RESPONSE_DELETE_EVENT:
+ case GTK_RESPONSE_CLOSE:
+ g_print ("Close\n");
+ break;
+
+ case GTK_RESPONSE_HELP:
+ g_print ("Help\n");
+ goto restart;
+ break;
+
+ case GTK_RESPONSE_APPLY:
+ apply (app);
+ goto restart;
+ break;
+ }
+
+ gtk_widget_destroy (app->dialog);
+}
+
+int
+main (int argc, char **argv)
+{
+ App *app;
+ GError *err = NULL;
+
+#if 0
+ bindtextdomain (GETTEXT_PACKAGE, DESKTOPEFFECTSLOCALEDIR);
+ bind_textdomain_codeset (GETTEXT_PACKAGE, "UTF-8");
+ textdomain (GETTEXT_PACKAGE);
+#endif
+
+ gtk_init (&argc, &argv);
+
+ app = g_new0 (App, 1);
+
+ if (!init_app (app))
+ {
+ show_error (err);
+ return 0;
+ }
+
+ run_application (app);
+
+ return 0;
+}
diff -up /dev/null gnome-control-center-2.21.5/capplets/display/crtc.c
--- /dev/null 2008-01-26 11:28:38.229690233 -0500
+++ gnome-control-center-2.21.5/capplets/display/crtc.c 2008-01-29 16:47:04.000000000 -0500
@@ -0,0 +1,291 @@
+#include <gtk/gtk.h>
+#define I_KNOW_THIS_IS_UNSTABLE_AND_ONLY_IN_FEDORA
+#include <libgnomeui/randrwrap.h>
+#include "crtc.h"
+
+struct CrtcInfo
+{
+ RWMode *mode;
+ int x;
+ int y;
+ GPtrArray *outputs;
+};
+
+struct CrtcAssignment
+{
+ RWScreen *screen;
+ GHashTable *info;
+};
+
+static void
+mode_info_free (CrtcInfo *info)
+{
+ g_ptr_array_free (info->outputs, TRUE);
+ g_free (info);
+}
+
+static CrtcAssignment *
+crtc_assignment_new (void)
+{
+ CrtcAssignment *assign = g_new0 (CrtcAssignment, 1);
+
+ assign->info = g_hash_table_new_full (g_direct_hash, g_direct_equal,
+ NULL, (GFreeFunc)mode_info_free);
+
+ return assign;
+}
+
+static gboolean
+can_clone (CrtcInfo *info,
+ RWOutput *output)
+{
+ int i;
+
+ for (i = 0; i < info->outputs->len; ++i)
+ {
+ RWOutput *clone = info->outputs->pdata[i];
+
+ if (!rw_output_can_clone (clone, output))
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
+static gboolean
+crtc_assignment_assign (CrtcAssignment *assign,
+ RWCrtc *crtc,
+ RWMode *mode,
+ int x,
+ int y,
+ RWOutput *output)
+{
+ CrtcInfo *info = g_hash_table_lookup (assign->info, crtc);
+
+ if (!rw_crtc_can_drive_output (crtc, output) ||
+ !rw_output_supports_mode (output, mode))
+ {
+ return FALSE;
+ }
+
+ if (info)
+ {
+ if (info->mode == mode &&
+ info->x == x &&
+ info->y == y &&
+ can_clone (info, output))
+ {
+ g_ptr_array_add (info->outputs, output);
+
+ return TRUE;
+ }
+ }
+ else
+ {
+ CrtcInfo *info = g_new (CrtcInfo, 1);
+
+ info->mode = mode;
+ info->x = x;
+ info->y = y;
+ info->outputs = g_ptr_array_new ();
+
+ g_ptr_array_add (info->outputs, output);
+
+ g_hash_table_insert (assign->info, crtc, info);
+
+ return TRUE;
+ }
+
+ return FALSE;
+}
+
+static void
+crtc_assignment_unassign (CrtcAssignment *assign,
+ RWCrtc *crtc,
+ RWOutput *output)
+{
+ CrtcInfo *info = g_hash_table_lookup (assign->info, crtc);
+
+ if (info)
+ {
+ g_ptr_array_remove (info->outputs, output);
+
+ if (info->outputs->len == 0)
+ g_hash_table_remove (assign->info, crtc);
+ }
+}
+
+void
+crtc_assignment_free (CrtcAssignment *assign)
+{
+ g_hash_table_destroy (assign->info);
+
+ g_free (assign);
+}
+
+static void
+configure_crtc (gpointer key,
+ gpointer value,
+ gpointer data)
+{
+ RWCrtc *crtc = key;
+ CrtcInfo *info = value;
+ GTimer *timer = g_timer_new ();
+
+
+ g_print ("Configuring crtc %x with ", rw_crtc_get_id (crtc));
+
+
+ if (info->mode)
+ {
+ int n_outputs = info->outputs->len;
+
+ g_print ("mode %x and %d outputs (%d %d %d %d) ",
+ rw_mode_get_id (info->mode),
+ n_outputs,
+ info->x, info->y,
+ rw_mode_get_width (info->mode),
+ rw_mode_get_height (info->mode));
+ }
+ else
+ {
+ g_print ("no mode ");
+ }
+
+ if (rw_crtc_set_config (crtc,
+ info->x, info->y,
+ info->mode,
+ (RWOutput **)info->outputs->pdata,
+ info->outputs->len))
+ {
+ g_print ("succeeded\n");
+ }
+ else
+ {
+ g_print ("failed\n");
+ }
+
+ g_print (" - %f seconds\n", g_timer_elapsed (timer, NULL));
+}
+
+void
+crtc_assignment_apply (CrtcAssignment *assign)
+{
+ GList *crtcs = g_hash_table_get_keys (assign->info);
+ GList *list;
+ int width, height;
+ GTimer *timer;
+
+ width = height = 0;
+ for (list = crtcs; list != NULL; list = list->next)
+ {
+ RWCrtc *crtc = list->data;
+ CrtcInfo *info = g_hash_table_lookup (assign->info, crtc);
+
+ width = MAX (width, info->x + rw_mode_get_width (info->mode));
+ height = MAX (height, info->y + rw_mode_get_height (info->mode));
+ }
+
+ /* Turn off CRTC's that are displaying outside the new screen area */
+ for (list = crtcs; list != NULL; list = list->next)
+ {
+ RWCrtc *crtc = list->data;
+ RWMode *mode = rw_crtc_get_current_mode (crtc);
+ int x, y;
+
+ if (mode)
+ {
+ rw_crtc_get_position (crtc, &x, &y);
+
+ if (x + rw_mode_get_width (mode) > width ||
+ y + rw_mode_get_height (mode) > height)
+ {
+ rw_crtc_set_config (crtc, 0, 0, NULL, NULL, 0);
+ }
+ }
+ }
+
+ g_list_free (crtcs);
+
+ /* FIXME: What do we do about physical size?
+ *
+ * As far as this function is concerned, probably just pass the problem up to
+ * the callers. There, the best possibility might be to pick the sum of physical
+ * sizes, but the reality is that the physical size of an X screen is a meaningless
+ * concept in a randr 1.2 enabled world.
+ */
+ timer = g_timer_new ();
+ rw_screen_set_size (assign->screen, width, height, 300, 230);
+
+ g_print ("time to set screen size: %f\n", g_timer_elapsed (timer, NULL));
+
+ g_timer_reset (timer);
+
+ g_hash_table_foreach (assign->info, configure_crtc, NULL);
+
+ g_print ("time to configure crtc's: %f\n", g_timer_elapsed (timer, NULL));
+}
+
+/* Check whether the given set of settings can be used
+ * at the same time -- ie. whether there is an assignment
+ * of CRTC's to outputs.
+ *
+ * Brute force - the number of objects involved is small
+ * enough that it doesn't matter.
+ */
+static gboolean
+real_assign_crtcs (RWScreen *screen,
+ Setting **settings,
+ CrtcAssignment *assignment)
+{
+ RWCrtc **crtcs = rw_screen_list_crtcs (screen);
+ Setting *setting;
+ int i;
+
+ setting = *settings;
+ if (!setting)
+ return TRUE;
+
+ /* It is always allowed for an output to be turned off */
+ if (setting->mode == NULL)
+ {
+ return real_assign_crtcs (screen, settings + 1, assignment);
+ }
+
+ for (i = 0; crtcs[i] != NULL; ++i)
+ {
+ RWCrtc *crtc = crtcs[i];
+
+ if (crtc_assignment_assign (
+ assignment, crtc, setting->mode,
+ setting->x, setting->y,
+ setting->output))
+ {
+ if (real_assign_crtcs (screen, settings + 1, assignment))
+ return TRUE;
+
+ crtc_assignment_unassign (assignment, crtc, setting->output);
+ }
+ }
+
+ return FALSE;
+}
+
+CrtcAssignment *
+assign_crtcs (RWScreen *screen, Setting **settings)
+{
+ CrtcAssignment *assignment = crtc_assignment_new ();
+
+ if (real_assign_crtcs (screen, settings, assignment))
+ {
+ assignment->screen = screen;
+
+ return assignment;
+ }
+ else
+ {
+ crtc_assignment_free (assignment);
+
+ return NULL;
+ }
+}
diff -up /dev/null gnome-control-center-2.21.5/capplets/display/display-capplet.glade
--- /dev/null 2008-01-26 11:28:38.229690233 -0500
+++ gnome-control-center-2.21.5/capplets/display/display-capplet.glade 2008-01-29 16:47:04.000000000 -0500
@@ -0,0 +1,363 @@
+<?xml version="1.0" standalone="no"?> <!--*- mode: xml -*-->
+<!DOCTYPE glade-interface SYSTEM "http://glade.gnome.org/glade-2.0.dtd">
+
+<glade-interface>
+
+<widget class="GtkDialog" id="dialog">
+ <property name="border_width">18</property>
+ <property name="title" translatable="yes"></property>
+ <property name="type">GTK_WINDOW_TOPLEVEL</property>
+ <property name="window_position">GTK_WIN_POS_NONE</property>
+ <property name="modal">False</property>
+ <property name="resizable">True</property>
+ <property name="destroy_with_parent">False</property>
+ <property name="decorated">True</property>
+ <property name="skip_taskbar_hint">False</property>
+ <property name="skip_pager_hint">False</property>
+ <property name="type_hint">GDK_WINDOW_TYPE_HINT_DIALOG</property>
+ <property name="gravity">GDK_GRAVITY_NORTH_WEST</property>
+ <property name="focus_on_map">True</property>
+ <property name="urgency_hint">False</property>
+ <property name="has_separator">False</property>
+
+ <child internal-child="vbox">
+ <widget class="GtkVBox" id="dialog-vbox1">
+ <property name="visible">True</property>
+ <property name="homogeneous">False</property>
+ <property name="spacing">0</property>
+
+ <child internal-child="action_area">
+ <widget class="GtkHButtonBox" id="dialog-action_area1">
+ <property name="visible">True</property>
+ <property name="layout_style">GTK_BUTTONBOX_END</property>
+
+ <child>
+ <widget class="GtkButton" id="helpbutton1">
+ <property name="visible">True</property>
+ <property name="can_default">True</property>
+ <property name="can_focus">True</property>
+ <property name="label">gtk-help</property>
+ <property name="use_stock">True</property>
+ <property name="relief">GTK_RELIEF_NORMAL</property>
+ <property name="focus_on_click">True</property>
+ <property name="response_id">-11</property>
+ </widget>
+ </child>
+
+ <child>
+ <widget class="GtkButton" id="button1">
+ <property name="visible">True</property>
+ <property name="can_default">True</property>
+ <property name="can_focus">True</property>
+ <property name="label">gtk-apply</property>
+ <property name="use_stock">True</property>
+ <property name="relief">GTK_RELIEF_NORMAL</property>
+ <property name="focus_on_click">True</property>
+ <property name="response_id">-10</property>
+ </widget>
+ </child>
+
+ <child>
+ <widget class="GtkButton" id="button2">
+ <property name="visible">True</property>
+ <property name="can_default">True</property>
+ <property name="can_focus">True</property>
+ <property name="label">gtk-close</property>
+ <property name="use_stock">True</property>
+ <property name="relief">GTK_RELIEF_NORMAL</property>
+ <property name="focus_on_click">True</property>
+ <property name="response_id">-7</property>
+ </widget>
+ </child>
+ </widget>
+ <packing>
+ <property name="padding">0</property>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="pack_type">GTK_PACK_END</property>
+ </packing>
+ </child>
+
+ <child>
+ <widget class="GtkVBox" id="vbox1">
+ <property name="visible">True</property>
+ <property name="homogeneous">False</property>
+ <property name="spacing">12</property>
+
+ <child>
+ <widget class="GtkLabel" id="label1">
+ <property name="visible">True</property>
+ <property name="label" translatable="yes"><b>Monitor Resolution Settings</b></property>
+ <property name="use_underline">False</property>
+ <property name="use_markup">True</property>
+ <property name="justify">GTK_JUSTIFY_LEFT</property>
+ <property name="wrap">False</property>
+ <property name="selectable">False</property>
+ <property name="xalign">0</property>
+ <property name="yalign">0.5</property>
+ <property name="xpad">0</property>
+ <property name="ypad">0</property>
+ <property name="ellipsize">PANGO_ELLIPSIZE_NONE</property>
+ <property name="width_chars">-1</property>
+ <property name="single_line_mode">False</property>
+ <property name="angle">0</property>
+ </widget>
+ <packing>
+ <property name="padding">0</property>
+ <property name="expand">False</property>
+ <property name="fill">False</property>
+ </packing>
+ </child>
+
+ <child>
+ <widget class="GtkAlignment" id="align">
+ <property name="visible">True</property>
+ <property name="xalign">0.5</property>
+ <property name="yalign">0.5</property>
+ <property name="xscale">1</property>
+ <property name="yscale">1</property>
+ <property name="top_padding">0</property>
+ <property name="bottom_padding">0</property>
+ <property name="left_padding">0</property>
+ <property name="right_padding">0</property>
+
+ <child>
+ <placeholder/>
+ </child>
+ </widget>
+ <packing>
+ <property name="padding">0</property>
+ <property name="expand">True</property>
+ <property name="fill">True</property>
+ </packing>
+ </child>
+
+ <child>
+ <widget class="GtkAlignment" id="alignment1">
+ <property name="visible">True</property>
+ <property name="xalign">0.5</property>
+ <property name="yalign">0.5</property>
+ <property name="xscale">1</property>
+ <property name="yscale">1</property>
+ <property name="top_padding">12</property>
+ <property name="bottom_padding">24</property>
+ <property name="left_padding">24</property>
+ <property name="right_padding">24</property>
+
+ <child>
+ <widget class="GtkTable" id="table1">
+ <property name="visible">True</property>
+ <property name="n_rows">2</property>
+ <property name="n_columns">4</property>
+ <property name="homogeneous">False</property>
+ <property name="row_spacing">12</property>
+ <property name="column_spacing">12</property>
+
+ <child>
+ <widget class="GtkLabel" id="label2">
+ <property name="visible">True</property>
+ <property name="label" translatable="yes">_Resolution</property>
+ <property name="use_underline">True</property>
+ <property name="use_markup">False</property>
+ <property name="justify">GTK_JUSTIFY_LEFT</property>
+ <property name="wrap">False</property>
+ <property name="selectable">False</property>
+ <property name="xalign">0</property>
+ <property name="yalign">0.5</property>
+ <property name="xpad">0</property>
+ <property name="ypad">0</property>
+ <property name="ellipsize">PANGO_ELLIPSIZE_NONE</property>
+ <property name="width_chars">-1</property>
+ <property name="single_line_mode">False</property>
+ <property name="angle">0</property>
+ </widget>
+ <packing>
+ <property name="left_attach">0</property>
+ <property name="right_attach">1</property>
+ <property name="top_attach">0</property>
+ <property name="bottom_attach">1</property>
+ <property name="x_options">fill</property>
+ <property name="y_options"></property>
+ </packing>
+ </child>
+
+ <child>
+ <widget class="GtkLabel" id="label3">
+ <property name="visible">True</property>
+ <property name="label" translatable="yes">Re_fresh Rate:</property>
+ <property name="use_underline">True</property>
+ <property name="use_markup">False</property>
+ <property name="justify">GTK_JUSTIFY_LEFT</property>
+ <property name="wrap">False</property>
+ <property name="selectable">False</property>
+ <property name="xalign">0</property>
+ <property name="yalign">0.5</property>
+ <property name="xpad">0</property>
+ <property name="ypad">0</property>
+ <property name="ellipsize">PANGO_ELLIPSIZE_NONE</property>
+ <property name="width_chars">-1</property>
+ <property name="single_line_mode">False</property>
+ <property name="angle">0</property>
+ </widget>
+ <packing>
+ <property name="left_attach">0</property>
+ <property name="right_attach">1</property>
+ <property name="top_attach">1</property>
+ <property name="bottom_attach">2</property>
+ <property name="x_options">fill</property>
+ <property name="y_options"></property>
+ </packing>
+ </child>
+
+ <child>
+ <widget class="GtkComboBox" id="resolution_combo">
+ <property name="visible">True</property>
+ <property name="add_tearoffs">False</property>
+ <property name="focus_on_click">True</property>
+ </widget>
+ <packing>
+ <property name="left_attach">1</property>
+ <property name="right_attach">2</property>
+ <property name="top_attach">0</property>
+ <property name="bottom_attach">1</property>
+ <property name="y_options">fill</property>
+ </packing>
+ </child>
+
+ <child>
+ <widget class="GtkComboBox" id="refresh_combo">
+ <property name="visible">True</property>
+ <property name="add_tearoffs">False</property>
+ <property name="focus_on_click">True</property>
+ </widget>
+ <packing>
+ <property name="left_attach">1</property>
+ <property name="right_attach">2</property>
+ <property name="top_attach">1</property>
+ <property name="bottom_attach">2</property>
+ <property name="x_options">fill</property>
+ <property name="y_options">fill</property>
+ </packing>
+ </child>
+
+ <child>
+ <widget class="GtkLabel" id="label4">
+ <property name="visible">True</property>
+ <property name="label" translatable="yes">Include _Panel</property>
+ <property name="use_underline">True</property>
+ <property name="use_markup">False</property>
+ <property name="justify">GTK_JUSTIFY_LEFT</property>
+ <property name="wrap">False</property>
+ <property name="selectable">False</property>
+ <property name="xalign">0</property>
+ <property name="yalign">0.5</property>
+ <property name="xpad">0</property>
+ <property name="ypad">0</property>
+ <property name="mnemonic_widget">panel_checkbox</property>
+ <property name="ellipsize">PANGO_ELLIPSIZE_NONE</property>
+ <property name="width_chars">-1</property>
+ <property name="single_line_mode">False</property>
+ <property name="angle">0</property>
+ </widget>
+ <packing>
+ <property name="left_attach">2</property>
+ <property name="right_attach">3</property>
+ <property name="top_attach">0</property>
+ <property name="bottom_attach">1</property>
+ <property name="x_options">fill</property>
+ <property name="y_options"></property>
+ </packing>
+ </child>
+
+ <child>
+ <widget class="GtkLabel" id="label5">
+ <property name="visible">True</property>
+ <property name="label" translatable="yes">R_otation</property>
+ <property name="use_underline">True</property>
+ <property name="use_markup">False</property>
+ <property name="justify">GTK_JUSTIFY_LEFT</property>
+ <property name="wrap">False</property>
+ <property name="selectable">False</property>
+ <property name="xalign">0</property>
+ <property name="yalign">0.5</property>
+ <property name="xpad">0</property>
+ <property name="ypad">0</property>
+ <property name="ellipsize">PANGO_ELLIPSIZE_NONE</property>
+ <property name="width_chars">-1</property>
+ <property name="single_line_mode">False</property>
+ <property name="angle">0</property>
+ </widget>
+ <packing>
+ <property name="left_attach">2</property>
+ <property name="right_attach">3</property>
+ <property name="top_attach">1</property>
+ <property name="bottom_attach">2</property>
+ <property name="x_options">fill</property>
+ <property name="y_options"></property>
+ </packing>
+ </child>
+
+ <child>
+ <widget class="GtkComboBox" id="rotation_combo">
+ <property name="visible">True</property>
+ <property name="items" translatable="yes">Normal
+Left
+Right
+Upside-down
+</property>
+ <property name="add_tearoffs">False</property>
+ <property name="focus_on_click">True</property>
+ </widget>
+ <packing>
+ <property name="left_attach">3</property>
+ <property name="right_attach">4</property>
+ <property name="top_attach">1</property>
+ <property name="bottom_attach">2</property>
+ <property name="y_options">fill</property>
+ </packing>
+ </child>
+
+ <child>
+ <widget class="GtkCheckButton" id="panel_checkbox">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="relief">GTK_RELIEF_NORMAL</property>
+ <property name="focus_on_click">True</property>
+ <property name="active">False</property>
+ <property name="inconsistent">False</property>
+ <property name="draw_indicator">True</property>
+
+ <child>
+ <placeholder/>
+ </child>
+ </widget>
+ <packing>
+ <property name="left_attach">3</property>
+ <property name="right_attach">4</property>
+ <property name="top_attach">0</property>
+ <property name="bottom_attach">1</property>
+ <property name="x_options">fill</property>
+ <property name="y_options"></property>
+ </packing>
+ </child>
+ </widget>
+ </child>
+ </widget>
+ <packing>
+ <property name="padding">0</property>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ </packing>
+ </child>
+ </widget>
+ <packing>
+ <property name="padding">0</property>
+ <property name="expand">True</property>
+ <property name="fill">True</property>
+ </packing>
+ </child>
+ </widget>
+ </child>
+</widget>
+
+</glade-interface>
diff -up /dev/null gnome-control-center-2.21.5/capplets/display/scrollarea.h
--- /dev/null 2008-01-26 11:28:38.229690233 -0500
+++ gnome-control-center-2.21.5/capplets/display/scrollarea.h 2008-01-29 16:47:04.000000000 -0500
@@ -0,0 +1,107 @@
+#include <cairo/cairo.h>
+#include <gtk/gtk.h>
+
+#define FOO_TYPE_SCROLL_AREA (foo_scroll_area_get_type ())
+#define FOO_SCROLL_AREA(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), FOO_TYPE_SCROLL_AREA, FooScrollArea))
+#define FOO_SCROLL_AREA_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), FOO_TYPE_SCROLL_AREA, FooScrollAreaClass))
+#define FOO_IS_SCROLL_AREA(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), FOO_TYPE_SCROLL_AREA))
+#define FOO_IS_SCROLL_AREA_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), FOO_TYPE_SCROLL_AREA))
+#define FOO_SCROLL_AREA_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), FOO_TYPE_SCROLL_AREA, FooScrollAreaClass))
+
+typedef struct FooScrollArea FooScrollArea;
+typedef struct FooScrollAreaClass FooScrollAreaClass;
+typedef struct FooScrollAreaPrivate FooScrollAreaPrivate;
+typedef struct FooScrollAreaEvent FooScrollAreaEvent;
+
+typedef enum
+{
+ FOO_BUTTON_PRESS,
+ FOO_BUTTON_RELEASE,
+ FOO_MOTION
+} FooScrollAreaEventType;
+
+struct FooScrollAreaEvent
+{
+ FooScrollAreaEventType type;
+ int x;
+ int y;
+};
+
+typedef void (* FooScrollAreaEventFunc) (FooScrollArea *area,
+ FooScrollAreaEvent *event,
+ gpointer data);
+
+struct FooScrollArea
+{
+ GtkContainer parent_instance;
+
+ FooScrollAreaPrivate *priv;
+};
+
+struct FooScrollAreaClass
+{
+ GtkContainerClass parent_class;
+
+ void (*set_scroll_adjustments) (FooScrollArea *scroll_area,
+ GtkAdjustment *hadjustment,
+ GtkAdjustment *vadjustment);
+
+ void (*viewport_changed) (FooScrollArea *scroll_area,
+ GdkRectangle *old_viewport,
+ GdkRectangle *new_viewport);
+
+ void (*paint) (FooScrollArea *scroll_area,
+ cairo_t *cr,
+ GdkRectangle *extents,
+ GdkRegion *region);
+};
+
+GType foo_scroll_area_get_type (void);
+
+FooScrollArea *foo_scroll_area_new (void);
+
+/* Set the requisition for the widget. */
+void foo_scroll_area_set_min_size (FooScrollArea *scroll_area,
+ int min_width,
+ int min_height);
+
+/* Set how much of the canvas can be scrolled into view */
+void foo_scroll_area_set_size (FooScrollArea *scroll_area,
+ int width,
+ int height);
+void foo_scroll_area_set_size_fixed_y (FooScrollArea *scroll_area,
+ int width,
+ int height,
+ int old_y,
+ int new_y);
+void foo_scroll_area_set_viewport_pos (FooScrollArea *scroll_area,
+ int x,
+ int y);
+void foo_scroll_area_get_viewport (FooScrollArea *scroll_area,
+ GdkRectangle *viewport);
+void foo_scroll_area_add_input_from_stroke (FooScrollArea *scroll_area,
+ cairo_t *cr,
+ FooScrollAreaEventFunc func,
+ gpointer data);
+void foo_scroll_area_add_input_from_fill (FooScrollArea *scroll_area,
+ cairo_t *cr,
+ FooScrollAreaEventFunc func,
+ gpointer data);
+void foo_scroll_area_invalidate_region (FooScrollArea *area,
+ GdkRegion *region);
+void foo_scroll_area_invalidate (FooScrollArea *scroll_area);
+void foo_scroll_area_invalidate_rect (FooScrollArea *scroll_area,
+ int x,
+ int y,
+ int width,
+ int height);
+void foo_scroll_area_begin_grab (FooScrollArea *scroll_area,
+ FooScrollAreaEventFunc func,
+ gpointer input_data);
+void foo_scroll_area_end_grab (FooScrollArea *scroll_area);
+gboolean foo_scroll_area_is_grabbed (FooScrollArea *scroll_area);
+
+void foo_scroll_area_begin_auto_scroll (FooScrollArea *scroll_area);
+void foo_scroll_area_auto_scroll (FooScrollArea *scroll_area,
+ FooScrollAreaEvent *event);
+void foo_scroll_area_end_auto_scroll (FooScrollArea *scroll_area);
diff -up /dev/null gnome-control-center-2.21.5/capplets/display/display-name.c
--- /dev/null 2008-01-26 11:28:38.229690233 -0500
+++ gnome-control-center-2.21.5/capplets/display/display-name.c 2008-01-29 16:47:04.000000000 -0500
@@ -0,0 +1,161 @@
+/*
+ * Copyright 2007 Red Hat, Inc.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * on the rights to use, copy, modify, merge, publish, distribute, sub
+ * license, and/or sell copies of the Software, and to permit persons to whom
+ * the Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice (including the next
+ * paragraph) shall be included in all copies or substantial portions of the
+ * Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
+ * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ */
+
+/* Author: Soren Sandmann <sandmann@redhat.com> */
+
+#include <stdlib.h>
+#include <math.h>
+#include <stdio.h>
+#include <string.h>
+#include "edid.h"
+
+typedef struct Vendor Vendor;
+struct Vendor
+{
+ const char vendor_id[4];
+ const char vendor_name[28];
+};
+
+/* This list of vendor codes derived from lshw
+ *
+ * http://ezix.org/project/wiki/HardwareLiSter
+ */
+static const struct Vendor vendors[] =
+{
+ { "AIC", "AG Neovo" },
+ { "ACR", "Acer" },
+ { "DEL", "DELL" },
+ { "SAM", "SAMSUNG" },
+ { "SNY", "SONY" },
+ { "SEC", "Epson" },
+ { "WAC", "Wacom" },
+ { "NEC", "NEC" },
+ { "CMO", "CMO" }, /* Chi Mei */
+
+ { "ABP", "Advansys" },
+ { "ACC", "Accton" },
+ { "ACE", "Accton" },
+ { "ADP", "Adaptec" },
+ { "ADV", "AMD" },
+ { "AIR", "AIR" },
+ { "AMI", "AMI" },
+ { "ASU", "ASUS" },
+ { "ATI", "ATI" },
+ { "ATK", "Allied Telesyn" },
+ { "AZT", "Aztech" },
+ { "BAN", "Banya" },
+ { "BRI", "Boca Research" },
+ { "BUS", "Buslogic" },
+ { "CCI", "Cache Computers Inc." },
+ { "CHA", "Chase" },
+ { "CMD", "CMD Technology, Inc." },
+ { "COG", "Cogent" },
+ { "CPQ", "Compaq" },
+ { "CRS", "Crescendo" },
+ { "CSC", "Crystal" },
+ { "CSI", "CSI" },
+ { "CTL", "Creative Labs" },
+ { "DBI", "Digi" },
+ { "DEC", "Digital Equipment" },
+ { "DBK", "Databook" },
+ { "EGL", "Eagle Technology" },
+ { "ELS", "ELSA" },
+ { "ESS", "ESS" },
+ { "FAR", "Farallon" },
+ { "FDC", "Future Domain" },
+ { "HWP", "Hewlett-Packard" },
+ { "IBM", "IBM" },
+ { "INT", "Intel" },
+ { "ISA", "Iomega" },
+ { "MDG", "Madge" },
+ { "MDY", "Microdyne" },
+ { "MET", "Metheus" },
+ { "MIC", "Micronics" },
+ { "MLX", "Mylex" },
+ { "NVL", "Novell" },
+ { "OLC", "Olicom" },
+ { "PRO", "Proteon" },
+ { "RII", "Racal" },
+ { "RTL", "Realtek" },
+ { "SCM", "SCM" },
+ { "SKD", "SysKonnect" },
+ { "SGI", "SGI" },
+ { "SMC", "SMC" },
+ { "SNI", "Siemens Nixdorf" },
+ { "STL", "Stallion Technologies" },
+ { "SUN", "Sun" },
+ { "SUP", "SupraExpress" },
+ { "SVE", "SVEC" },
+ { "TCC", "Thomas-Conrad" },
+ { "TCI", "Tulip" },
+ { "TCM", "3Com" },
+ { "TCO", "Thomas-Conrad" },
+ { "TEC", "Tecmar" },
+ { "TRU", "Truevision" },
+ { "TOS", "Toshiba" },
+ { "TYN", "Tyan" },
+ { "UBI", "Ungermann-Bass" },
+ { "USC", "UltraStor" },
+ { "VDM", "Vadem" },
+ { "VMI", "Vermont" },
+ { "WDC", "Western Digital" },
+ { "ZDS", "Zeos" },
+ { "???", "Unknown" },
+};
+
+static const char *
+find_vendor (const char *code)
+{
+ int i;
+
+ for (i = 0; i < sizeof (vendors) / sizeof (vendors[0]); ++i)
+ {
+ const Vendor *v = &(vendors[i]);
+
+ if (strcmp (v->vendor_id, code) == 0)
+ return v->vendor_name;
+ }
+
+ return code;
+};
+
+char *
+make_display_name (const MonitorInfo *info)
+{
+ const char *vendor = find_vendor (info->manufacturer_code);
+ int len = 32 + strlen (vendor) + 1 + sizeof(int) * 10 + 1;
+ char *result = malloc (len);
+
+ if (!result)
+ return NULL;
+
+ if (strncmp (info->dsc_product_name, "EPSON PJ", 8) == 0)
+ {
+ sprintf (result, "Epson Projector");
+ }
+ else
+ {
+ sprintf (result, "%s", vendor);
+ }
+
+ return result;
+}
diff -up gnome-control-center-2.21.5/capplets/display/Makefile.am.add-randr12-capplet gnome-control-center-2.21.5/capplets/display/Makefile.am
--- gnome-control-center-2.21.5/capplets/display/Makefile.am.add-randr12-capplet 2008-01-03 11:22:06.000000000 -0500
+++ gnome-control-center-2.21.5/capplets/display/Makefile.am 2008-01-29 16:47:04.000000000 -0500
@@ -1,9 +1,27 @@
# This is used in GNOMECC_CAPPLETS_CFLAGS
cappletname = display
+gladedir = $(pkgdatadir)/glade
+dist_glade_DATA = display-capplet.glade
+
bin_PROGRAMS = gnome-display-properties
-gnome_display_properties_SOURCES = main.c
+gnome_display_properties_SOURCES = \
+ xrandr-capplet.c \
+ monitor-db.c \
+ display-name.c \
+ edid-parse.c \
+ scrollarea.c \
+ crtc.c \
+ apply.c \
+ foo-marshal.c \
+ monitor-db.h \
+ edid.h \
+ scrollarea.h \
+ crtc.h \
+ apply.h \
+ foo-marshal.h
+
gnome_display_properties_LDFLAGS = -export-dynamic
gnome_display_properties_LDADD = \
$(DISPLAY_CAPPLET_LIBS) \
diff -up /dev/null gnome-control-center-2.21.5/capplets/display/apply.c
--- /dev/null 2008-01-26 11:28:38.229690233 -0500
+++ gnome-control-center-2.21.5/capplets/display/apply.c 2008-01-29 16:47:04.000000000 -0500
@@ -0,0 +1,178 @@
+#include "crtc.h"
+#include "monitor-db.h"
+
+/* This file should go in g-s-d eventually */
+
+static void
+push (GPtrArray *a,
+ RWOutput *output,
+ RWMode *mode,
+ int x, int y)
+{
+ Setting *setting = g_new0 (Setting, 1);
+
+ setting->output = output;
+ setting->mode = mode;
+ setting->x = x;
+ setting->y = y;
+
+ g_ptr_array_add (a, setting);
+}
+
+static void
+pop (GPtrArray *a)
+{
+ g_free (g_ptr_array_remove_index (a, a->len - 1));
+}
+
+typedef gboolean (* ForeachFunc) (Setting **settings, gpointer data);
+
+static gboolean
+foreach_setting_list (RWScreen *screen,
+ GPtrArray *settings,
+ Output **outputs,
+ ForeachFunc func,
+ gpointer data)
+{
+ gboolean result;
+
+ if (*outputs == NULL)
+ {
+ g_ptr_array_add (settings, NULL);
+
+ result = func ((Setting **)settings->pdata, data);
+
+ g_ptr_array_remove_index (settings, settings->len - 1);
+ }
+ else
+ {
+ Output *output = *outputs;
+ RWOutput *rw_output = rw_screen_get_output_by_name (
+ screen, output->name);
+ RWMode **modes = rw_output_list_modes (rw_output);
+ int i;
+
+ if (!output->connected)
+ {
+ g_print ("%s is not connected\n", output->name);
+ push (settings, rw_output, NULL, 0, 0);
+
+ result = foreach_setting_list (screen, settings, outputs + 1, func, data);
+
+ pop (settings);
+ }
+ else
+ {
+ result = FALSE;
+
+ g_print ("%s modes (%p)\n", output->name, output);
+
+ for (i = 0; modes[i] != NULL; ++i)
+ {
+ RWMode *mode = modes[i];
+
+ g_print ("%d x %d x %d match mode %d x %d x %d\n",
+ output->width, output->height, output->rate,
+ rw_mode_get_width (mode),
+ rw_mode_get_height (mode),
+ rw_mode_get_freq (mode));
+
+ if (rw_mode_get_width (mode) == output->width &&
+ rw_mode_get_height (mode) == output->height &&
+ rw_mode_get_freq (mode) == output->rate)
+ {
+ push (settings, rw_output, mode, output->x, 0);
+
+ result = foreach_setting_list (
+ screen, settings, outputs + 1, func, data);
+
+ pop (settings);
+
+ if (result)
+ break;
+ }
+ }
+ }
+ }
+
+ g_print ("returning %d\n", result);
+
+ return result;
+}
+
+static gboolean
+try_settings (Setting **settings, gpointer data)
+{
+ RWScreen *screen = data;
+ CrtcAssignment *assignment;
+ int i;
+
+ g_print ("found setting\n");
+
+ assignment = assign_crtcs (screen, settings);
+
+ for (i = 0; settings[i] != NULL; ++i)
+ {
+ RWMode *mode = settings[i]->mode;
+
+ g_print ("%s => ", rw_output_get_name (settings[i]->output));
+
+ if (mode)
+ {
+ g_print ("%d x %d x %d\n",
+ rw_mode_get_width (mode),
+ rw_mode_get_height (mode),
+ rw_mode_get_freq (mode));
+ }
+ else
+ {
+ g_print ("off\n");
+ }
+ }
+
+ if (assignment)
+ {
+ crtc_assignment_apply (assignment);
+
+ crtc_assignment_free (assignment);
+
+ return TRUE;
+ }
+ else
+ {
+ return FALSE;
+ }
+}
+
+/* This should go in g-s-d eventually */
+void
+apply_stored_configuration (RWScreen *screen)
+{
+ char *file = g_build_filename (
+ g_get_home_dir(), ".gnome2", "monitors.xml", NULL);
+ Configuration **configs = configurations_read (file, NULL);
+ Configuration *current = configuration_new_current (screen);
+ Configuration *found;
+
+ if ((found = configuration_find (configs, current)))
+ {
+ GPtrArray *array = g_ptr_array_new ();
+
+ g_print ("found\n");
+
+ foreach_setting_list (
+ screen, array, found->outputs,
+ try_settings, screen);
+
+ g_ptr_array_free (array, TRUE);
+ }
+ else
+ {
+ g_print ("Not found\n");
+ }
+
+ configuration_free (current);
+ configurations_free (configs);
+
+ g_free (file);
+}