diff --git a/gnome-desktop-randr-1.2.diff b/gnome-desktop-randr-1.2.diff new file mode 100644 index 0000000..810f40e --- /dev/null +++ b/gnome-desktop-randr-1.2.diff @@ -0,0 +1,3723 @@ +diff --git a/configure.in b/configure.in +index f18ad7b..5b0202b 100644 +--- a/configure.in ++++ b/configure.in +@@ -51,10 +51,10 @@ AC_SUBST(GNOME_MICRO) + AC_SUBST(GNOME_DISTRIBUTOR) + AC_SUBST(GNOME_DATE) + +-GNOME_COMMON_INIT +-GNOME_DEBUG_CHECK +-GNOME_COMPILE_WARNINGS([maximum]) +-GNOME_MAINTAINER_MODE_DEFINES ++#GNOME_COMMON_INIT ++#GNOME_DEBUG_CHECK ++#GNOME_COMPILE_WARNINGS([maximum]) ++#GNOME_MAINTAINER_MODE_DEFINES + + # As a special favour for vuntz, support --disable-deprecations + +diff --git a/libgnome-desktop/ChangeLog b/libgnome-desktop/ChangeLog +index a6a4486..bc95a61 100644 +--- a/libgnome-desktop/ChangeLog ++++ b/libgnome-desktop/ChangeLog +@@ -1,3 +1,10 @@ ++2008-04-17 James Westby ++ ++ * monitor-db.c (configuration_new_current): Invert the logic in ++ the detection of clone mode so that it works for a single screen ++ as well. Without this change single screens are always reported as ++ clone, which makes no sense. ++ + 2008-03-29 Matthias Clasen + + * gnome-bg.c (get_current_slide): Handle start times in +diff --git a/libgnome-desktop/Makefile.am b/libgnome-desktop/Makefile.am +index 08e1395..68bf653 100644 +--- a/libgnome-desktop/Makefile.am ++++ b/libgnome-desktop/Makefile.am +@@ -20,7 +20,12 @@ libgnome_desktop_2_la_SOURCES = \ + gnome-desktop-item.c \ + gnome-ditem-edit.c \ + gnome-hint.c \ +- gnome-bg.c ++ gnome-bg.c \ ++ display-name.c \ ++ randrwrap.c \ ++ monitor-db.c \ ++ edid-parse.c \ ++ edid.h + + libgnome_desktop_2_la_LIBADD = \ + $(XLIB_LIBS) \ +diff --git a/libgnome-desktop/display-name.c b/libgnome-desktop/display-name.c +new file mode 100644 +index 0000000..5c46920 +--- /dev/null ++++ b/libgnome-desktop/display-name.c +@@ -0,0 +1,252 @@ ++/* ++ * 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 */ ++ ++#include ++#include ++#include ++#include ++#include ++#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 */ ++ { "BNQ", "BenQ" }, ++ ++ { "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" }, ++ ++ /* From http://faydoc.tripod.com/structures/01/0136.htm */ ++ { "ACT", "Targa" }, ++ { "ADI", "ADI" }, ++ { "AOC", "AOC Intl" }, ++ { "API", "Acer America" }, ++ { "APP", "Apple Computer" }, ++ { "ART", "ArtMedia" }, ++ { "AST", "AST Research" }, ++ { "CPL", "Compal" }, ++ { "CTX", "Chuntex Electronic Co." }, ++ { "DPC", "Delta Electronics" }, ++ { "DWE", "Daewoo" }, ++ { "ECS", "ELITEGROUP" }, ++ { "EIZ", "EIZO" }, ++ { "FCM", "Funai" }, ++ { "GSM", "LG Electronics" }, ++ { "GWY", "Gateway 2000" }, ++ { "HEI", "Hyundai" }, ++ { "HIT", "Hitachi" }, ++ { "HSL", "Hansol" }, ++ { "HTC", "Hitachi" }, ++ { "ICL", "Fujitsu ICL" }, ++ { "IVM", "Idek Iiyama" }, ++ { "KFC", "KFC Computek" }, ++ { "LKM", "ADLAS" }, ++ { "LNK", "LINK Tech" }, ++ { "LTN", "Lite-On" }, ++ { "MAG", "MAG InnoVision" }, ++ { "MAX", "Maxdata" }, ++ { "MEI", "Panasonic" }, ++ { "MEL", "Mitsubishi" }, ++ { "MIR", "miro" }, ++ { "MTC", "MITAC" }, ++ { "NAN", "NANAO" }, ++ { "NEC", "NEC Tech" }, ++ { "NOK", "Nokia" }, ++ { "OQI", "OPTIQUEST" }, ++ { "PBN", "Packard Bell" }, ++ { "PGS", "Princeton" }, ++ { "PHL", "Philips" }, ++ { "REL", "Relisys" }, ++ { "SDI", "Samtron" }, ++ { "SMI", "Smile" }, ++ { "SPT", "Sceptre" }, ++ { "SRC", "Shamrock Technology" }, ++ { "STP", "Sceptre" }, ++ { "TAT", "Tatung" }, ++ { "TRL", "Royal Information Company" }, ++ { "TSB", "Toshiba, Inc." }, ++ { "UNM", "Unisys" }, ++ { "VSC", "ViewSonic" }, ++ { "WTC", "Wen Tech" }, ++ { "ZCM", "Zenith Data Systems" }, ++ ++ { "???", "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 char *output_name, ++ const MonitorInfo *info) ++{ ++ const char *vendor; ++ int width_mm, height_mm, inches; ++ ++ if (output_name && ++ (strstr ("lvds", output_name) || ++ strstr ("LVDS", output_name) || ++ strstr ("Lvds", output_name))) ++ { ++ vendor = "Laptop"; ++ } ++ else if (info) ++ { ++ vendor = find_vendor (info->manufacturer_code); ++ } ++ else ++ { ++ vendor = "Unknown"; ++ } ++ ++ if (info && info->width_mm != -1 && info->height_mm) ++ { ++ width_mm = info->width_mm; ++ height_mm = info->height_mm; ++ } ++ else if (info && info->n_detailed_timings) ++ { ++ width_mm = info->detailed_timings[0].width_mm; ++ height_mm = info->detailed_timings[0].height_mm; ++ } ++ else ++ { ++ width_mm = -1; ++ height_mm = -1; ++ } ++ ++ if (width_mm != -1 && height_mm != -1) ++ { ++ double d = sqrt (width_mm * width_mm + height_mm * height_mm); ++ ++ inches = (int)(d / 25.4 + 0.5); ++ } ++ else ++ { ++ inches = -1; ++ } ++ ++ if (inches > 0) ++ return g_strdup_printf ("%s %d\"", vendor, inches); ++ else ++ return g_strdup_printf ("%s\n", vendor); ++} +diff --git a/libgnome-desktop/edid-parse.c b/libgnome-desktop/edid-parse.c +new file mode 100644 +index 0000000..2611a24 +--- /dev/null ++++ b/libgnome-desktop/edid-parse.c +@@ -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 */ ++ ++#include "edid.h" ++#include ++#include ++#include ++ ++#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 --git a/libgnome-desktop/edid.h b/libgnome-desktop/edid.h +new file mode 100644 +index 0000000..4f0573a +--- /dev/null ++++ b/libgnome-desktop/edid.h +@@ -0,0 +1,170 @@ ++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 char *output_name, ++ const MonitorInfo *info); +diff --git a/libgnome-desktop/libgnomeui/Makefile.am b/libgnome-desktop/libgnomeui/Makefile.am +index 24c762b..0a143b3 100644 +--- a/libgnome-desktop/libgnomeui/Makefile.am ++++ b/libgnome-desktop/libgnomeui/Makefile.am +@@ -1,5 +1,7 @@ + libgnomeui_desktopdir = $(includedir)/gnome-desktop-2.0/libgnomeui +-libgnomeui_desktop_HEADERS = \ +- gnome-ditem-edit.h \ +- gnome-hint.h \ +- gnome-bg.h ++libgnomeui_desktop_HEADERS = \ ++ gnome-ditem-edit.h \ ++ gnome-hint.h \ ++ gnome-bg.h \ ++ randrwrap.h \ ++ monitor-db.h +diff --git a/libgnome-desktop/libgnomeui/monitor-db.h b/libgnome-desktop/libgnomeui/monitor-db.h +new file mode 100644 +index 0000000..05c7749 +--- /dev/null ++++ b/libgnome-desktop/libgnomeui/monitor-db.h +@@ -0,0 +1,52 @@ ++#ifndef MONITOR_DB_H ++#define MONITOR_DB_H ++ ++#include ++#include ++ ++typedef struct Output Output; ++typedef struct Configuration Configuration; ++ ++struct Output ++{ ++ char * name; ++ ++ gboolean on; ++ int width; ++ int height; ++ int rate; ++ int x; ++ int y; ++ RWRotation rotation; ++ ++ gboolean connected; ++ char vendor[4]; ++ guint product; ++ guint serial; ++ double aspect; ++ int pref_width; ++ int pref_height; ++ char * display_name; ++ ++ gpointer user_data; ++}; ++ ++struct Configuration ++{ ++ gboolean clone; ++ ++ Output **outputs; ++}; ++ ++void configuration_free (Configuration *configuration); ++Configuration *configuration_new_current (RWScreen *screen); ++gboolean configuration_match (Configuration *config1, ++ Configuration *config2); ++gboolean configuration_save (Configuration *configuration, ++ GError **err); ++void configuration_sanitize (Configuration *configuration); ++gboolean configuration_apply_stored (RWScreen *screen); ++gboolean configuration_applicable (Configuration *configuration, ++ RWScreen *screen); ++ ++#endif +diff --git a/libgnome-desktop/libgnomeui/randrwrap.h b/libgnome-desktop/libgnomeui/randrwrap.h +new file mode 100644 +index 0000000..f9ab2c9 +--- /dev/null ++++ b/libgnome-desktop/libgnomeui/randrwrap.h +@@ -0,0 +1,96 @@ ++#ifndef RANDR_WRAP_H ++#define RANDR_WRAP_H ++ ++#include ++#include ++ ++typedef struct RWScreen RWScreen; ++typedef struct RWOutput RWOutput; ++typedef struct RWCrtc RWCrtc; ++typedef struct RWMode RWMode; ++ ++typedef void (* RWScreenChanged) (RWScreen *screen, gpointer data); ++ ++typedef enum ++{ ++ RW_ROTATION_0 = (1 << 0), ++ RW_ROTATION_90 = (1 << 1), ++ RW_ROTATION_180 = (1 << 2), ++ RW_ROTATION_270 = (1 << 3), ++ RW_REFLECT_X = (1 << 4), ++ RW_REFLECT_Y = (1 << 5) ++} RWRotation; ++ ++/* RWScreen */ ++RWScreen * rw_screen_new (GdkScreen *screen, ++ RWScreenChanged callback, ++ gpointer data); ++RWOutput ** rw_screen_list_outputs (RWScreen *screen); ++RWCrtc ** rw_screen_list_crtcs (RWScreen *screen); ++RWMode ** rw_screen_list_modes (RWScreen *screen); ++void rw_screen_set_size (RWScreen *screen, ++ int width, ++ int height, ++ int mm_width, ++ int mm_height); ++RWCrtc * rw_screen_get_crtc_by_id (RWScreen *screen, ++ guint32 id); ++gboolean rw_screen_refresh (RWScreen *screen); ++RWOutput * rw_screen_get_output_by_id (RWScreen *screen, ++ guint32 id); ++RWOutput * rw_screen_get_output_by_name (RWScreen *screen, ++ const char *name); ++void rw_screen_get_ranges (RWScreen *screen, ++ int *min_width, ++ int *max_width, ++ int *min_height, ++ int *max_height); ++ ++/* RWOutput */ ++guint32 rw_output_get_id (RWOutput *output); ++const char * rw_output_get_name (RWOutput *output); ++gboolean rw_output_is_connected (RWOutput *output); ++int rw_output_get_size_inches (RWOutput *output); ++int rw_output_get_width_mm (RWOutput *outout); ++int rw_output_get_height_mm (RWOutput *output); ++const guint8 *rw_output_get_edid_data (RWOutput *output); ++RWCrtc ** rw_output_get_possible_crtcs (RWOutput *output); ++RWMode * rw_output_get_current_mode (RWOutput *output); ++RWCrtc * rw_output_get_crtc (RWOutput *output); ++void rw_output_get_position (RWOutput *output, ++ int *x, ++ int *y); ++gboolean rw_output_can_clone (RWOutput *output, ++ RWOutput *clone); ++RWMode ** rw_output_list_modes (RWOutput *output); ++RWMode * rw_output_get_preferred_mode (RWOutput *output); ++gboolean rw_output_supports_mode (RWOutput *output, ++ RWMode *mode); ++ ++/* RWMode */ ++guint32 rw_mode_get_id (RWMode *mode); ++guint rw_mode_get_width (RWMode *mode); ++guint rw_mode_get_height (RWMode *mode); ++int rw_mode_get_freq (RWMode *mode); ++ ++/* RWCrtc */ ++guint32 rw_crtc_get_id (RWCrtc *crtc); ++gboolean rw_crtc_set_config (RWCrtc *crtc, ++ int x, ++ int y, ++ RWMode *mode, ++ RWRotation rotation, ++ RWOutput **outputs, ++ int n_outputs); ++gboolean rw_crtc_can_drive_output (RWCrtc *crtc, ++ RWOutput *output); ++RWMode * rw_crtc_get_current_mode (RWCrtc *crtc); ++void rw_crtc_get_position (RWCrtc *crtc, ++ int *x, ++ int *y); ++RWRotation rw_crtc_get_current_rotation (RWCrtc *crtc); ++RWRotation rw_crtc_get_rotations (RWCrtc *crtc); ++gboolean rw_crtc_supports_rotation (RWCrtc *crtc, ++ RWRotation rotation); ++ ++#endif +diff --git a/libgnome-desktop/monitor-db.c b/libgnome-desktop/monitor-db.c +new file mode 100644 +index 0000000..1a293fb +--- /dev/null ++++ b/libgnome-desktop/monitor-db.c +@@ -0,0 +1,1315 @@ ++#include ++#include ++#include ++#include "libgnomeui/monitor-db.h" ++#include "edid.h" ++ ++/* 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 CrtcAssignment CrtcAssignment; ++ ++static void crtc_assignment_apply (CrtcAssignment *assign); ++static CrtcAssignment *crtc_assignment_new (RWScreen *screen, ++ Output **outputs); ++static void crtc_assignment_free (CrtcAssignment *assign); ++static void output_free (Output *output); ++static Output * output_copy (Output *output); ++ ++typedef struct Parser Parser; ++ ++/* 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); ++ parser->output->rotation = 0; ++ ++ 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 ("default"); ++ } ++ 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); ++ parser->configuration->clone = FALSE; ++ parser->configuration->outputs = g_new0 (Output *, 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) ++ { ++ /* If no rotation properties were set, just use RW_ROTATION_0 */ ++ if (parser->output->rotation == 0) ++ parser->output->rotation = RW_ROTATION_0; ++ ++ g_ptr_array_add (parser->outputs, parser->output); ++ ++ 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, "clone", "configuration", NULL)) ++ { ++ if (strcmp (text, "yes") == 0) ++ parser->configuration->clone = TRUE; ++ } ++ 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 if (stack_is (parser, "rotation", "output", "configuration", NULL)) ++ { ++ if (strcmp (text, "normal") == 0) ++ { ++ parser->output->rotation |= RW_ROTATION_0; ++ } ++ else if (strcmp (text, "left") == 0) ++ { ++ parser->output->rotation |= RW_ROTATION_90; ++ } ++ else if (strcmp (text, "upside_down") == 0) ++ { ++ parser->output->rotation |= RW_ROTATION_180; ++ } ++ else if (strcmp (text, "right") == 0) ++ { ++ parser->output->rotation |= RW_ROTATION_270; ++ } ++ } ++ else if (stack_is (parser, "reflect_x", "output", "configuration", NULL)) ++ { ++ if (strcmp (text, "yes") == 0) ++ { ++ parser->output->rotation |= RW_REFLECT_X; ++ } ++ } ++ else if (stack_is (parser, "reflect_y", "output", "configuration", NULL)) ++ { ++ if (strcmp (text, "yes") == 0) ++ { ++ parser->output->rotation |= RW_REFLECT_Y; ++ } ++ } ++ else ++ { ++ /* Ignore other properties so we can expand the format in the future */ ++ } ++} ++ ++static void ++parser_free (Parser *parser) ++{ ++ int i; ++ GList *list; ++ ++ g_assert (parser != NULL); ++ ++ 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); ++} ++ ++static 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; ++ ++ g_assert (parser->outputs); ++ goto out; ++ } ++ ++ g_assert (parser->outputs); ++ ++ g_ptr_array_add (parser->configurations, NULL); ++ result = (Configuration **)g_ptr_array_free (parser->configurations, FALSE); ++ parser->configurations = g_ptr_array_new (); ++ ++ g_assert (parser->outputs); ++out: ++ parser_free (parser); ++ ++ return result; ++} ++ ++Configuration * ++configuration_new_current (RWScreen *screen) ++{ ++ Configuration *config = g_new0 (Configuration, 1); ++ GPtrArray *a = g_ptr_array_new (); ++ RWOutput **rw_outputs; ++ int i; ++ int clone_width = -1; ++ int clone_height = -1; ++ ++ g_return_val_if_fail (screen != NULL, NULL); ++ ++ rw_outputs = rw_screen_list_outputs (screen); ++ ++ config->clone = FALSE; ++ ++ for (i = 0; rw_outputs[i] != NULL; ++i) ++ { ++ RWOutput *rw_output = rw_outputs[i]; ++ Output *output = g_new0 (Output, 1); ++ RWMode *mode = NULL; ++ const guint8 *edid_data = rw_output_get_edid_data (rw_output); ++ RWCrtc *crtc; ++ ++ output->name = g_strdup (rw_output_get_name (rw_output)); ++ output->connected = rw_output_is_connected (rw_output); ++ ++ if (!output->connected) ++ { ++ output->x = -1; ++ output->y = -1; ++ output->width = -1; ++ output->height = -1; ++ output->rate = -1; ++ output->rotation = RW_ROTATION_0; ++ } ++ else ++ { ++ MonitorInfo *info = NULL; ++ ++ if (edid_data) ++ { ++ info = decode_edid (edid_data); ++ ++ memcpy (output->vendor, info->manufacturer_code, ++ sizeof (output->vendor)); ++ ++ output->product = info->product_code; ++ output->serial = info->serial_number; ++ output->aspect = info->aspect_ratio; ++ } ++ else ++ { ++ strcpy (output->vendor, "???"); ++ output->product = 0; ++ output->serial = 0; ++ } ++ ++ output->display_name = make_display_name ( ++ rw_output_get_name (rw_output), info); ++ ++ g_free (info); ++ ++ crtc = rw_output_get_crtc (rw_output); ++ mode = crtc? rw_crtc_get_current_mode (crtc) : NULL; ++ ++ if (crtc && mode) ++ { ++ output->on = TRUE; ++ ++ rw_crtc_get_position (crtc, &output->x, &output->y); ++ output->width = rw_mode_get_width (mode); ++ output->height = rw_mode_get_height (mode); ++ output->rate = rw_mode_get_freq (mode); ++ output->rotation = rw_crtc_get_current_rotation (crtc); ++ ++ if (output->x == 0 && output->y == 0) { ++ if (clone_width == -1) { ++ clone_width = output->width; ++ clone_height = output->height; ++ } else if (clone_width == output->width && ++ clone_height == output->height) { ++ config->clone = TRUE; ++ } ++ } ++ } ++ else ++ { ++ output->on = FALSE; ++ config->clone = FALSE; ++ } ++ ++ /* Get preferred size for the monitor */ ++ mode = rw_output_get_preferred_mode (rw_output); ++ ++ if (!mode) ++ { ++ RWMode **modes = rw_output_list_modes (rw_output); ++ ++ /* FIXME: we should pick the "best" mode here, where best is ++ * sorted wrt ++ * ++ * - closest aspect ratio ++ * - mode area ++ * - refresh rate ++ * - We may want to extend randrwrap so that get_preferred ++ * returns that - although that could also depend on ++ * the crtc. ++ */ ++ if (modes[0]) ++ mode = modes[0]; ++ } ++ ++ if (mode) ++ { ++ output->pref_width = rw_mode_get_width (mode); ++ output->pref_height = rw_mode_get_height (mode); ++ } ++ else ++ { ++ /* Pick some random numbers. This should basically never happen */ ++ output->pref_width = 1024; ++ output->pref_height = 768; ++ } ++ } ++ ++ 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; ++} ++ ++static void ++output_free (Output *output) ++{ ++ if (output->display_name) ++ g_free (output->display_name); ++ ++ if (output->name) ++ g_free (output->name); ++ ++ g_free (output); ++} ++ ++static Output * ++output_copy (Output *output) ++{ ++ Output *copy = g_new0 (Output, 1); ++ ++ *copy = *output; ++ ++ copy->name = g_strdup (output->name); ++ copy->display_name = g_strdup (output->display_name); ++ ++ return copy; ++} ++ ++static void ++outputs_free (Output **outputs) ++{ ++ int i; ++ ++ g_assert (outputs != NULL); ++ ++ for (i = 0; outputs[i] != NULL; ++i) ++ output_free (outputs[i]); ++} ++ ++void ++configuration_free (Configuration *config) ++{ ++ g_return_if_fail (config != NULL); ++ outputs_free (config->outputs); ++ ++ g_free (config); ++} ++ ++static void ++configurations_free (Configuration **configurations) ++{ ++ int i; ++ ++ g_assert (configurations != NULL); ++ ++ 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; ++ gsize 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) ++{ ++ g_assert (output1 != NULL); ++ g_assert (output2 != NULL); ++ ++ 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; ++ ++ 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; ++} ++ ++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; ++} ++ ++static Output ** ++make_outputs (Configuration *config) ++{ ++ GPtrArray *outputs; ++ Output *first_on;; ++ int i; ++ ++ outputs = g_ptr_array_new (); ++ ++ first_on = NULL; ++ ++ for (i = 0; config->outputs[i] != NULL; ++i) ++ { ++ Output *old = config->outputs[i]; ++ Output *new = output_copy (old); ++ ++ if (old->on && !first_on) ++ first_on = old; ++ ++ if (config->clone && new->on) ++ { ++ g_assert (first_on); ++ ++ new->width = first_on->width; ++ new->height = first_on->height; ++ new->rotation = first_on->rotation; ++ new->x = 0; ++ new->y = 0; ++ } ++ ++ g_ptr_array_add (outputs, new); ++ } ++ ++ g_ptr_array_add (outputs, NULL); ++ ++ return (Output **)g_ptr_array_free (outputs, FALSE); ++} ++ ++gboolean ++configuration_applicable (Configuration *configuration, ++ RWScreen *screen) ++{ ++ Output **outputs = make_outputs (configuration); ++ CrtcAssignment *assign = crtc_assignment_new (screen, outputs); ++ gboolean result; ++ ++ if (assign) ++ { ++ result = TRUE; ++ crtc_assignment_free (assign); ++ } ++ else ++ { ++ result = FALSE; ++ } ++ ++ outputs_free (outputs); ++ ++ return result; ++} ++ ++static 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; ++} ++ ++/* Database management */ ++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 const char * ++get_rotation_name (RWRotation r) ++{ ++ if (r & RW_ROTATION_0) ++ return "normal"; ++ if (r & RW_ROTATION_90) ++ return "left"; ++ if (r & RW_ROTATION_180) ++ return "upside_down"; ++ if (r & RW_ROTATION_270) ++ return "right"; ++ ++ return "normal"; ++} ++ ++static const char * ++yes_no (int x) ++{ ++ return x? "yes" : "no"; ++} ++ ++static const char * ++get_reflect_x (RWRotation r) ++{ ++ return yes_no (r & RW_REFLECT_X); ++} ++ ++static const char * ++get_reflect_y (RWRotation r) ++{ ++ return yes_no (r & RW_REFLECT_Y); ++} ++ ++static void ++emit_configuration (Configuration *config, ++ GString *string) ++{ ++ int j; ++ ++ g_string_append_printf (string, "\n"); ++ ++ g_string_append_printf (string, " %s\n", yes_no (config->clone)); ++ ++ for (j = 0; config->outputs[j] != NULL; ++j) ++ { ++ Output *output = config->outputs[j]; ++ ++ g_string_append_printf ( ++ string, " \n", output->name); ++ ++ if (output->connected && *output->vendor != '\0') ++ { ++ g_string_append_printf ( ++ string, " %s\n", output->vendor); ++ g_string_append_printf ( ++ string, " 0x%04x\n", output->product); ++ g_string_append_printf ( ++ string, " 0x%08x\n", output->serial); ++ } ++ ++ /* An unconnected output which is on does not make sense */ ++ if (output->connected && output->on) ++ { ++ g_string_append_printf ( ++ string, " %d\n", output->width); ++ g_string_append_printf ( ++ string, " %d\n", output->height); ++ g_string_append_printf ( ++ string, " %d\n", output->rate); ++ g_string_append_printf ( ++ string, " %d\n", output->x); ++ g_string_append_printf ( ++ string, " %d\n", output->y); ++ g_string_append_printf ( ++ string, " %s\n", get_rotation_name (output->rotation)); ++ g_string_append_printf ( ++ string, " %s\n", get_reflect_x (output->rotation)); ++ g_string_append_printf ( ++ string, " %s\n", get_reflect_y (output->rotation)); ++ } ++ ++ g_string_append_printf (string, " \n"); ++ } ++ ++ g_string_append_printf (string, "\n"); ++} ++ ++void ++configuration_sanitize (Configuration *config) ++{ ++ int i; ++ int x_offset, y_offset; ++ ++ /* Offset everything by the top/left-most coordinate to ++ * make sure the configuration starts at (0, 0) ++ */ ++ x_offset = y_offset = G_MAXINT; ++ for (i = 0; config->outputs[i]; ++i) ++ { ++ Output *output = config->outputs[i]; ++ ++ if (output->on) ++ { ++ x_offset = MIN (x_offset, output->x); ++ y_offset = MIN (y_offset, output->y); ++ } ++ } ++ ++ for (i = 0; config->outputs[i]; ++i) ++ { ++ Output *output = config->outputs[i]; ++ ++ if (output->on) ++ { ++ output->x -= x_offset; ++ output->y -= y_offset; ++ } ++ } ++} ++ ++gboolean ++configuration_save (Configuration *configuration, GError **err) ++{ ++ Configuration **configurations; ++ GString *output = g_string_new(""); ++ int i; ++ ++ configurations = configurations_read (get_filename(), NULL); ++ ++ if (configurations) ++ { ++ for (i = 0; configurations[i] != NULL; ++i) ++ { ++ if (!configuration_match (configurations[i], configuration)) ++ emit_configuration (configurations[i], output); ++ } ++ ++ configurations_free (configurations); ++ } ++ ++ emit_configuration (configuration, output); ++ ++ return g_file_set_contents (get_filename(), output->str, -1, err); ++} ++ ++static gboolean ++apply_configuration (Configuration *conf, RWScreen *screen) ++{ ++ CrtcAssignment *assignment; ++ Output **outputs; ++ ++ g_print ("applying configuration. Clone: %s\n", yes_no (conf->clone)); ++ ++ outputs = make_outputs (conf); ++ ++ assignment = crtc_assignment_new (screen, outputs); ++ ++ outputs_free (outputs); ++ ++ if (assignment) ++ { ++ crtc_assignment_apply (assignment); ++ ++ crtc_assignment_free (assignment); ++ ++ return TRUE; ++ } ++ else ++ { ++ g_print (" ... no assignment found\n"); ++ } ++ ++ return FALSE; ++} ++ ++gboolean ++configuration_apply_stored (RWScreen *screen) ++{ ++ char *file = g_build_filename ( ++ g_get_home_dir(), ".gnome2", "monitors.xml", NULL); ++ Configuration **configs = configurations_read (file, NULL); ++ Configuration *current; ++ Configuration *found; ++ gboolean result; ++ ++ rw_screen_refresh (screen); ++ ++ current = configuration_new_current (screen); ++ if (configs) ++ { ++ if ((found = configuration_find (configs, current))) ++ { ++ apply_configuration (found, screen); ++ result = TRUE; ++ } ++ else ++ { ++ result = FALSE; ++ } ++ ++ configurations_free (configs); ++ } ++ ++ g_free (file); ++ configuration_free (current); ++ ++ return result; ++} ++ ++ ++/* ++ * CRTC assignment ++ */ ++typedef struct CrtcInfo CrtcInfo; ++ ++struct CrtcInfo ++{ ++ RWMode *mode; ++ int x; ++ int y; ++ RWRotation rotation; ++ GPtrArray *outputs; ++}; ++ ++struct CrtcAssignment ++{ ++ RWScreen *screen; ++ GHashTable *info; ++}; ++ ++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, ++ RWRotation rotation, ++ RWOutput *output) ++{ ++ /* FIXME: We should reject stuff that is outside the screen ranges */ ++ ++ CrtcInfo *info = g_hash_table_lookup (assign->info, crtc); ++ ++ if (!rw_crtc_can_drive_output (crtc, output) || ++ !rw_output_supports_mode (output, mode) || ++ !rw_crtc_supports_rotation (crtc, rotation)) ++ { ++ return FALSE; ++ } ++ ++ if (info) ++ { ++ if (info->mode == mode && ++ info->x == x && ++ info->y == y && ++ info->rotation == rotation && ++ can_clone (info, output)) ++ { ++ g_ptr_array_add (info->outputs, output); ++ ++ return TRUE; ++ } ++ } ++ else ++ { ++ CrtcInfo *info = g_new0 (CrtcInfo, 1); ++ ++ info->mode = mode; ++ info->x = x; ++ info->y = y; ++ info->rotation = rotation; ++ 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); ++ } ++} ++ ++static 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; ++ ++ g_print ("Configuring crtc %x with ", rw_crtc_get_id (crtc)); ++ ++ if (info->mode) ++ { ++ int n_outputs = info->outputs->len; ++ ++ g_print ("mode %x, %d outputs (%d %d %d %d), and rotation %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), ++ info->rotation); ++ } ++ else ++ { ++ g_print ("no mode "); ++ } ++ ++ if (rw_crtc_set_config (crtc, ++ info->x, info->y, ++ info->mode, ++ info->rotation, ++ (RWOutput **)info->outputs->pdata, ++ info->outputs->len)) ++ { ++ g_print ("succeeded\n"); ++ } ++ else ++ { ++ g_print ("failed\n"); ++ } ++} ++ ++static gboolean ++mode_is_rotated (CrtcInfo *info) ++{ ++ if ((info->rotation & RW_ROTATION_270) || ++ (info->rotation & RW_ROTATION_90)) ++ { ++ g_print ("rotated: %d\n", info->rotation); ++ return TRUE; ++ } ++ return FALSE; ++} ++ ++static gboolean ++crtc_is_rotated (RWCrtc *crtc) ++{ ++ RWRotation r = rw_crtc_get_current_rotation (crtc); ++ ++ if ((r & RW_ROTATION_270) || ++ (r & RW_ROTATION_90)) ++ { ++ return TRUE; ++ } ++ ++ return FALSE; ++} ++ ++static void ++crtc_assignment_apply (CrtcAssignment *assign) ++{ ++ GList *active_crtcs = g_hash_table_get_keys (assign->info); ++ RWCrtc **all_crtcs = rw_screen_list_crtcs (assign->screen); ++ GList *list; ++ int width, height; ++ int i; ++ int min_width, max_width, min_height, max_height; ++ int width_mm, height_mm; ++ ++ /* Compute size of the screen */ ++ width = height = 1; ++ for (list = active_crtcs; list != NULL; list = list->next) ++ { ++ RWCrtc *crtc = list->data; ++ CrtcInfo *info = g_hash_table_lookup (assign->info, crtc); ++ int w, h; ++ ++ w = rw_mode_get_width (info->mode); ++ h = rw_mode_get_height (info->mode); ++ ++ if (mode_is_rotated (info)) ++ { ++ int tmp = h; ++ h = w; ++ w = tmp; ++ } ++ ++ width = MAX (width, info->x + w); ++ height = MAX (height, info->y + h); ++ } ++ g_list_free (active_crtcs); ++ ++ rw_screen_get_ranges ( ++ assign->screen, &min_width, &max_width, &min_height, &max_height); ++ ++ width = MAX (min_width, width); ++ width = MIN (max_width, width); ++ height = MAX (min_height, height); ++ height = MIN (max_height, height); ++ ++ /* Turn off all crtcs currently displaying outside the new screen */ ++ for (i = 0; all_crtcs[i] != NULL; ++i) ++ { ++ RWCrtc *crtc = all_crtcs[i]; ++ RWMode *mode = rw_crtc_get_current_mode (crtc); ++ int x, y; ++ ++ if (mode) ++ { ++ int w, h; ++ rw_crtc_get_position (crtc, &x, &y); ++ ++ w = rw_mode_get_width (mode); ++ h = rw_mode_get_height (mode); ++ ++ if (crtc_is_rotated (crtc)) ++ { ++ int tmp = h; ++ h = w; ++ w = tmp; ++ } ++ ++ if (x + w > width || y + h > height) ++ rw_crtc_set_config (crtc, 0, 0, NULL, RW_ROTATION_0, NULL, 0); ++ } ++ } ++ ++ /* Turn off all CRTC's that are not in the assignment */ ++ for (i = 0; all_crtcs[i] != NULL; ++i) ++ { ++ RWCrtc *crtc = all_crtcs[i]; ++ ++ if (!g_hash_table_lookup (assign->info, crtc)) ++ rw_crtc_set_config (crtc, 0, 0, NULL, RW_ROTATION_0, NULL, 0); ++ } ++ ++ /* The 'physical size' of an X screen is meaningless if that screen ++ * can consist of many monitors. So just pick a size that make the ++ * dpi 96. ++ * ++ * Firefox and Evince apparently believe what X tells them. ++ */ ++ width_mm = (width / 96.0) * 25.4 + 0.5; ++ height_mm = (height / 96.0) * 25.4 + 0.5; ++ ++ rw_screen_set_size (assign->screen, width, height, width_mm, height_mm); ++ ++ g_hash_table_foreach (assign->info, configure_crtc, 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, ++ Output **outputs, ++ CrtcAssignment *assignment) ++{ ++ RWCrtc **crtcs = rw_screen_list_crtcs (screen); ++ Output *output; ++ int i; ++ ++ output = *outputs; ++ if (!output) ++ return TRUE; ++ ++ /* It is always allowed for an output to be turned off */ ++ if (!output->on) ++ { ++ return real_assign_crtcs (screen, outputs + 1, assignment); ++ } ++ ++ for (i = 0; crtcs[i] != NULL; ++i) ++ { ++ int pass; ++ ++ /* Make two passses, one where frequencies must match, then ++ * one where they don't have to ++ */ ++ for (pass = 0; pass < 2; ++pass) ++ { ++ RWCrtc *crtc = crtcs[i]; ++ RWOutput *rw_output = rw_screen_get_output_by_name (screen, output->name); ++ RWMode **modes = rw_output_list_modes (rw_output); ++ int j; ++ ++ for (j = 0; modes[j] != NULL; ++j) ++ { ++ RWMode *mode = modes[j]; ++ ++ if (rw_mode_get_width (mode) == output->width && ++ rw_mode_get_height (mode) == output->height && ++ (pass == 1 || rw_mode_get_freq (mode) == output->rate)) ++ { ++ if (crtc_assignment_assign ( ++ assignment, crtc, modes[j], ++ output->x, output->y, ++ output->rotation, ++ rw_output)) ++ { ++ if (real_assign_crtcs (screen, outputs + 1, assignment)) ++ return TRUE; ++ ++ crtc_assignment_unassign (assignment, crtc, rw_output); ++ } ++ } ++ } ++ } ++ } ++ ++ return FALSE; ++} ++ ++static void ++crtc_info_free (CrtcInfo *info) ++{ ++ g_ptr_array_free (info->outputs, TRUE); ++ g_free (info); ++} ++ ++static CrtcAssignment * ++crtc_assignment_new (RWScreen *screen, Output **outputs) ++{ ++ CrtcAssignment *assignment = g_new0 (CrtcAssignment, 1); ++ ++ assignment->info = g_hash_table_new_full ( ++ g_direct_hash, g_direct_equal, NULL, (GFreeFunc)crtc_info_free); ++ ++ if (real_assign_crtcs (screen, outputs, assignment)) ++ { ++ assignment->screen = screen; ++ ++ return assignment; ++ } ++ else ++ { ++ crtc_assignment_free (assignment); ++ ++ return NULL; ++ } ++} +diff --git a/libgnome-desktop/randrwrap.c b/libgnome-desktop/randrwrap.c +new file mode 100644 +index 0000000..2cc0e72 +--- /dev/null ++++ b/libgnome-desktop/randrwrap.c +@@ -0,0 +1,1177 @@ ++#include "libgnomeui/randrwrap.h" ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#define DISPLAY(o) ((o)->info->screen->xdisplay) ++ ++typedef struct ScreenInfo ScreenInfo; ++ ++struct ScreenInfo ++{ ++ int min_width; ++ int max_width; ++ int min_height; ++ int max_height; ++ ++ XRRScreenResources *resources; ++ ++ RWOutput ** outputs; ++ RWCrtc ** crtcs; ++ RWMode ** modes; ++ ++ RWScreen * screen; ++}; ++ ++struct RWScreen ++{ ++ GdkScreen * gdk_screen; ++ GdkWindow * gdk_root; ++ Display * xdisplay; ++ Screen * xscreen; ++ Window xroot; ++ ScreenInfo * info; ++ ++ int randr_event_base; ++ ++ RWScreenChanged callback; ++ gpointer data; ++}; ++ ++struct RWOutput ++{ ++ ScreenInfo * info; ++ RROutput id; ++ ++ char * name; ++ RWCrtc * current_crtc; ++ gboolean connected; ++ gulong width_mm; ++ gulong height_mm; ++ RWCrtc ** possible_crtcs; ++ RWOutput ** clones; ++ RWMode ** modes; ++ int n_preferred; ++ guint8 * edid_data; ++}; ++ ++struct RWOutputWrap ++{ ++ RROutput id; ++}; ++ ++struct RWCrtc ++{ ++ ScreenInfo * info; ++ RRCrtc id; ++ ++ RWMode * current_mode; ++ RWOutput ** current_outputs; ++ RWOutput ** possible_outputs; ++ int x; ++ int y; ++ ++ RWRotation current_rotation; ++ RWRotation rotations; ++}; ++ ++struct RWMode ++{ ++ ScreenInfo * info; ++ RRMode id; ++ char * name; ++ int width; ++ int height; ++ int freq; /* in mHz */ ++}; ++ ++/* RWCrtc */ ++static RWCrtc * crtc_new (ScreenInfo *info, ++ RRCrtc id); ++static void crtc_free (RWCrtc *crtc); ++static void crtc_initialize (RWCrtc *crtc, ++ XRRScreenResources *res); ++ ++ ++/* RWOutput */ ++static RWOutput *output_new (ScreenInfo *info, ++ RROutput id); ++static void output_initialize (RWOutput *output, ++ XRRScreenResources *res); ++static void output_free (RWOutput *output); ++ ++ ++/* RWMode */ ++static RWMode * mode_new (ScreenInfo *info, ++ RRMode id); ++static void mode_initialize (RWMode *mode, ++ XRRModeInfo *info); ++static void mode_free (RWMode *mode); ++ ++ ++/* Screen */ ++static RWOutput * ++rw_output_by_id (ScreenInfo *info, RROutput id) ++{ ++ RWOutput **output; ++ ++ g_assert (info != NULL); ++ ++ for (output = info->outputs; *output; ++output) ++ { ++ if ((*output)->id == id) ++ return *output; ++ } ++ ++ return NULL; ++} ++ ++static RWCrtc * ++crtc_by_id (ScreenInfo *info, RRCrtc id) ++{ ++ RWCrtc **crtc; ++ ++ if (!info) ++ return NULL; ++ ++ for (crtc = info->crtcs; *crtc; ++crtc) ++ { ++ if ((*crtc)->id == id) ++ return *crtc; ++ } ++ ++ return NULL; ++} ++ ++static RWMode * ++mode_by_id (ScreenInfo *info, RRMode id) ++{ ++ RWMode **mode; ++ ++ g_assert (info != NULL); ++ ++ for (mode = info->modes; *mode; ++mode) ++ { ++ if ((*mode)->id == id) ++ return *mode; ++ } ++ ++ return NULL; ++} ++ ++static void ++screen_info_free (ScreenInfo *info) ++{ ++ RWOutput **output; ++ RWCrtc **crtc; ++ RWMode **mode; ++ ++ g_assert (info != NULL); ++ ++ if (info->resources) ++ { ++ XRRFreeScreenResources (info->resources); ++ ++ info->resources = NULL; ++ } ++ ++ if (info->outputs) ++ { ++ for (output = info->outputs; *output; ++output) ++ output_free (*output); ++ g_free (info->outputs); ++ } ++ ++ if (info->crtcs) ++ { ++ for (crtc = info->crtcs; *crtc; ++crtc) ++ crtc_free (*crtc); ++ g_free (info->crtcs); ++ } ++ ++ if (info->modes) ++ { ++ for (mode = info->modes; *mode; ++mode) ++ mode_free (*mode); ++ g_free (info->modes); ++ } ++ ++ g_free (info); ++} ++ ++static gboolean ++fill_out_screen_info (Display *xdisplay, Window xroot, ++ ScreenInfo *info) ++{ ++ XRRScreenResources *resources; ++ ++ g_assert (xdisplay != NULL); ++ g_assert (info != NULL); ++ ++ XRRGetScreenSizeRange (xdisplay, xroot, ++ &(info->min_width), ++ &(info->min_height), ++ &(info->max_width), ++ &(info->max_height)); ++ ++#if 0 ++ g_print ("ranges: %d - %d; %d - %d\n", ++ screen->min_width, screen->max_width, ++ screen->min_height, screen->max_height); ++#endif ++ ++ resources = XRRGetScreenResources (xdisplay, xroot); ++ ++ if (resources) ++ { ++ int i; ++ GPtrArray *a; ++ RWCrtc **crtc; ++ RWOutput **output; ++ ++#if 0 ++ g_print ("Resource Timestamp: %u\n", (guint32)resources->timestamp); ++ g_print ("Resource Configuration Timestamp: %u\n", (guint32)resources->configTimestamp); ++#endif ++ ++ info->resources = resources; ++ ++ /* We create all the structures before initializing them, so ++ * that they can refer to each other. ++ */ ++ a = g_ptr_array_new (); ++ for (i = 0; i < resources->ncrtc; ++i) ++ { ++ RWCrtc *crtc = crtc_new (info, resources->crtcs[i]); ++ ++ g_ptr_array_add (a, crtc); ++ } ++ g_ptr_array_add (a, NULL); ++ info->crtcs = (RWCrtc **)g_ptr_array_free (a, FALSE); ++ ++ a = g_ptr_array_new (); ++ for (i = 0; i < resources->noutput; ++i) ++ { ++ RWOutput *output = output_new (info, resources->outputs[i]); ++ ++ g_ptr_array_add (a, output); ++ } ++ g_ptr_array_add (a, NULL); ++ info->outputs = (RWOutput **)g_ptr_array_free (a, FALSE); ++ ++ a = g_ptr_array_new (); ++ for (i = 0; i < resources->nmode; ++i) ++ { ++ RWMode *mode = mode_new (info, resources->modes[i].id); ++ ++ g_ptr_array_add (a, mode); ++ } ++ g_ptr_array_add (a, NULL); ++ info->modes = (RWMode **)g_ptr_array_free (a, FALSE); ++ ++ /* Initialize */ ++ for (crtc = info->crtcs; *crtc; ++crtc) ++ crtc_initialize (*crtc, resources); ++ ++ for (output = info->outputs; *output; ++output) ++ output_initialize (*output, resources); ++ ++ for (i = 0; i < resources->nmode; ++i) ++ { ++ RWMode *mode = mode_by_id (info, resources->modes[i].id); ++ ++ mode_initialize (mode, &(resources->modes[i])); ++ } ++ ++ return TRUE; ++ } ++ else ++ { ++ g_print ("Couldn't get screen resources\n"); ++ ++ return FALSE; ++ } ++} ++ ++static ScreenInfo * ++screen_info_new (RWScreen *screen) ++{ ++ ScreenInfo *info = g_new0 (ScreenInfo, 1); ++ RWOutput **o; ++ ++ g_assert (screen != NULL); ++ ++ info->outputs = NULL; ++ info->crtcs = NULL; ++ info->modes = NULL; ++ info->screen = screen; ++ ++ if (fill_out_screen_info (screen->xdisplay, screen->xroot, info)) ++ { ++ return info; ++ } ++ else ++ { ++ g_free (info); ++ return NULL; ++ } ++ ++ for (o = info->outputs; *o; o++) ++ { ++ ++ } ++ ++} ++ ++static gboolean ++screen_update (RWScreen *screen, gboolean force_callback) ++{ ++ ScreenInfo *info; ++ gboolean changed = FALSE; ++ ++ g_assert (screen != NULL); ++ ++ info = screen_info_new (screen); ++ if (info) ++ { ++ if (info->resources->configTimestamp != screen->info->resources->configTimestamp) ++ changed = TRUE; ++ ++ screen_info_free (screen->info); ++ ++ screen->info = info; ++ } ++ ++ if ((changed || force_callback) && screen->callback) ++ screen->callback (screen, screen->data); ++ ++ return changed; ++} ++ ++static GdkFilterReturn ++screen_on_event (GdkXEvent *xevent, ++ GdkEvent *event, ++ gpointer data) ++{ ++ RWScreen *screen = data; ++ XEvent *e = xevent; ++ ++ if (e && e->type - screen->randr_event_base == RRNotify) ++ { ++ XRRNotifyEvent *event = (XRRNotifyEvent *)e; ++ ++ switch (event->subtype) ++ { ++ default: ++ break; ++ } ++ ++ /* FIXME: we may need to be more discriminating in ++ * what causes 'changed' events ++ */ ++ screen_update (screen, TRUE); ++ } ++ ++ /* Pass the event on to GTK+ */ ++ return GDK_FILTER_CONTINUE; ++} ++ ++RWScreen * ++rw_screen_new (GdkScreen *gdk_screen, ++ RWScreenChanged callback, ++ gpointer data) ++{ ++ Display *dpy = GDK_SCREEN_XDISPLAY (gdk_screen); ++ int event_base; ++ int ignore; ++ ++ if (XRRQueryExtension (dpy, &event_base, &ignore)) ++ { ++ RWScreen *screen = g_new0 (RWScreen, 1); ++ ++ screen->gdk_screen = gdk_screen; ++ screen->gdk_root = gdk_screen_get_root_window (gdk_screen); ++ screen->xroot = gdk_x11_drawable_get_xid (screen->gdk_root); ++ screen->xdisplay = dpy; ++ screen->xscreen = gdk_x11_screen_get_xscreen (screen->gdk_screen); ++ ++ screen->callback = callback; ++ screen->data = data; ++ ++ screen->randr_event_base = event_base; ++ ++ screen->info = screen_info_new (screen); ++ ++ XRRSelectInput (screen->xdisplay, ++ screen->xroot, ++ RRScreenChangeNotifyMask | ++ RRCrtcChangeNotifyMask | ++ RROutputPropertyNotifyMask); ++ ++ gdk_x11_register_standard_event_type ( ++ gdk_screen_get_display (gdk_screen), ++ event_base, ++ RRNotify + 1); ++ ++ gdk_window_add_filter (screen->gdk_root, screen_on_event, screen); ++ return screen; ++ } ++ ++ return NULL; ++} ++ ++void ++rw_screen_set_size (RWScreen *screen, ++ int width, ++ int height, ++ int mm_width, ++ int mm_height) ++{ ++ g_return_if_fail (screen != NULL); ++ ++ XRRSetScreenSize (screen->xdisplay, screen->xroot, ++ width, height, mm_width, mm_height); ++} ++ ++void ++rw_screen_get_ranges (RWScreen *screen, ++ int *min_width, ++ int *max_width, ++ int *min_height, ++ int *max_height) ++{ ++ g_return_if_fail (screen != NULL); ++ ++ if (min_width) ++ *min_width = screen->info->min_width; ++ ++ if (max_width) ++ *max_width = screen->info->max_width; ++ ++ if (min_height) ++ *min_height = screen->info->min_height; ++ ++ if (max_height) ++ *max_height = screen->info->max_height; ++} ++ ++gboolean ++rw_screen_refresh (RWScreen *screen) ++{ ++ return screen_update (screen, FALSE); ++} ++ ++RWMode ** ++rw_screen_list_modes (RWScreen *screen) ++{ ++ g_return_val_if_fail (screen != NULL, NULL); ++ g_return_val_if_fail (screen->info != NULL, NULL); ++ ++ return screen->info->modes; ++} ++ ++RWCrtc ** ++rw_screen_list_crtcs (RWScreen *screen) ++{ ++ g_return_val_if_fail (screen != NULL, NULL); ++ g_return_val_if_fail (screen->info != NULL, NULL); ++ ++ return screen->info->crtcs; ++} ++ ++RWOutput ** ++rw_screen_list_outputs (RWScreen *screen) ++{ ++ g_return_val_if_fail (screen != NULL, NULL); ++ g_return_val_if_fail (screen->info != NULL, NULL); ++ ++ return screen->info->outputs; ++} ++ ++RWCrtc * ++rw_screen_get_crtc_by_id (RWScreen *screen, ++ guint32 id) ++{ ++ int i; ++ ++ g_return_val_if_fail (screen != NULL, NULL); ++ g_return_val_if_fail (screen->info != NULL, NULL); ++ ++ for (i = 0; screen->info->crtcs[i] != NULL; ++i) ++ { ++ if (screen->info->crtcs[i]->id == id) ++ return screen->info->crtcs[i]; ++ } ++ ++ return NULL; ++} ++ ++RWOutput * ++rw_screen_get_output_by_id (RWScreen *screen, ++ guint32 id) ++{ ++ int i; ++ ++ g_return_val_if_fail (screen != NULL, NULL); ++ g_return_val_if_fail (screen->info != NULL, NULL); ++ ++ for (i = 0; screen->info->outputs[i] != NULL; ++i) ++ { ++ if (screen->info->outputs[i]->id == id) ++ return screen->info->outputs[i]; ++ } ++ ++ return NULL; ++} ++ ++/* RWOutput */ ++static RWOutput * ++output_new (ScreenInfo *info, RROutput id) ++{ ++ RWOutput *output = g_new0 (RWOutput, 1); ++ ++ output->id = id; ++ output->info = info; ++ ++ return output; ++} ++ ++static guint8 * ++get_property (Display *dpy, ++ RROutput output, ++ Atom atom, ++ int *len) ++{ ++ unsigned char *prop; ++ int actual_format; ++ unsigned long nitems, bytes_after; ++ Atom actual_type; ++ guint8 *result; ++ ++ XRRGetOutputProperty (dpy, output, atom, ++ 0, 100, False, False, ++ AnyPropertyType, ++ &actual_type, &actual_format, ++ &nitems, &bytes_after, &prop); ++ ++ if (actual_type == XA_INTEGER && actual_format == 8) ++ { ++ result = g_memdup (prop, nitems); ++ if (len) ++ *len = nitems; ++ } ++ else ++ { ++ result = NULL; ++ } ++ ++ XFree (prop); ++ ++ return result; ++} ++ ++static guint8 * ++read_edid_data (RWOutput *output) ++{ ++ Atom edid_atom = XInternAtom (DISPLAY (output), "EDID_DATA", FALSE); ++ guint8 *result; ++ int len; ++ ++ result = get_property (DISPLAY (output), ++ output->id, edid_atom, &len); ++ ++ if (result) ++ { ++ if (len == 128) ++ return result; ++ else ++ g_free (result); ++ } ++ ++ return NULL; ++} ++ ++static void ++output_initialize (RWOutput *output, XRRScreenResources *res) ++{ ++ XRROutputInfo *info = XRRGetOutputInfo ( ++ DISPLAY (output), res, output->id); ++ GPtrArray *a; ++ int i; ++ ++ g_print ("Output %lx Timestamp: %u\n", output->id, (guint32)info->timestamp); ++ ++ if (!info) ++ { ++ /* FIXME */ ++ return; ++ } ++ ++ output->name = g_strdup (info->name); /* FIXME: what is nameLen used for? */ ++ output->current_crtc = crtc_by_id (output->info, info->crtc); ++ output->width_mm = info->mm_width; ++ output->height_mm = info->mm_height; ++ output->connected = (info->connection == RR_Connected); ++ ++ /* Possible crtcs */ ++ a = g_ptr_array_new (); ++ ++ for (i = 0; i < info->ncrtc; ++i) ++ { ++ RWCrtc *crtc = crtc_by_id (output->info, info->crtcs[i]); ++ ++ if (crtc) ++ g_ptr_array_add (a, crtc); ++ } ++ g_ptr_array_add (a, NULL); ++ output->possible_crtcs = (RWCrtc **)g_ptr_array_free (a, FALSE); ++ ++ /* Clones */ ++ a = g_ptr_array_new (); ++ for (i = 0; i < info->nclone; ++i) ++ { ++ RWOutput *rw_output = rw_output_by_id (output->info, info->clones[i]); ++ ++ if (rw_output) ++ g_ptr_array_add (a, rw_output); ++ } ++ g_ptr_array_add (a, NULL); ++ output->clones = (RWOutput **)g_ptr_array_free (a, FALSE); ++ ++ /* Modes */ ++ a = g_ptr_array_new (); ++ for (i = 0; i < info->nmode; ++i) ++ { ++ RWMode *mode = mode_by_id (output->info, info->modes[i]); ++ ++ if (mode) ++ g_ptr_array_add (a, mode); ++ } ++ g_ptr_array_add (a, NULL); ++ output->modes = (RWMode **)g_ptr_array_free (a, FALSE); ++ ++ output->n_preferred = info->npreferred; ++ ++ /* Edid data */ ++ output->edid_data = read_edid_data (output); ++ ++ XRRFreeOutputInfo (info); ++} ++ ++static void ++output_free (RWOutput *output) ++{ ++ g_free (output); ++} ++ ++guint32 ++rw_output_get_id (RWOutput *output) ++{ ++ g_assert(output != NULL); ++ ++ return output->id; ++} ++ ++const guint8 * ++rw_output_get_edid_data (RWOutput *output) ++{ ++ g_return_val_if_fail (output != NULL, NULL); ++ ++ return output->edid_data; ++} ++ ++RWOutput * ++rw_screen_get_output_by_name (RWScreen *screen, ++ const char *name) ++{ ++ int i; ++ ++ g_return_val_if_fail (screen != NULL, NULL); ++ g_return_val_if_fail (screen->info != NULL, NULL); ++ ++ for (i = 0; screen->info->outputs[i] != NULL; ++i) ++ { ++ RWOutput *output = screen->info->outputs[i]; ++ ++ if (strcmp (output->name, name) == 0) ++ return output; ++ } ++ ++ return NULL; ++} ++ ++RWCrtc * ++rw_output_get_crtc (RWOutput *output) ++{ ++ g_return_val_if_fail (output != NULL, NULL); ++ ++ return output->current_crtc; ++} ++ ++RWMode * ++rw_output_get_current_mode (RWOutput *output) ++{ ++ RWCrtc *crtc; ++ ++ g_return_val_if_fail (output != NULL, NULL); ++ ++ if ((crtc = rw_output_get_crtc (output))) ++ return rw_crtc_get_current_mode (crtc); ++ ++ return NULL; ++} ++ ++void ++rw_output_get_position (RWOutput *output, ++ int *x, ++ int *y) ++{ ++ RWCrtc *crtc; ++ ++ g_return_if_fail (output != NULL); ++ ++ if ((crtc = rw_output_get_crtc (output))) ++ rw_crtc_get_position (crtc, x, y); ++} ++ ++const char * ++rw_output_get_name (RWOutput *output) ++{ ++ g_assert (output != NULL); ++ return output->name; ++} ++ ++int ++rw_output_get_width_mm (RWOutput *output) ++{ ++ g_assert (output != NULL); ++ return output->width_mm; ++} ++ ++int ++rw_output_get_height_mm (RWOutput *output) ++{ ++ g_assert (output != NULL); ++ return output->height_mm; ++} ++ ++RWMode * ++rw_output_get_preferred_mode (RWOutput *output) ++{ ++ g_return_val_if_fail (output != NULL, NULL); ++ if (output->n_preferred) ++ return output->modes[0]; ++ ++ return NULL; ++} ++ ++RWMode ** ++rw_output_list_modes (RWOutput *output) ++{ ++ g_return_val_if_fail (output != NULL, NULL); ++ return output->modes; ++} ++ ++gboolean ++rw_output_is_connected (RWOutput *output) ++{ ++ g_return_val_if_fail (output != NULL, FALSE); ++ return output->connected; ++} ++ ++gboolean ++rw_output_supports_mode (RWOutput *output, ++ RWMode *mode) ++{ ++ int i; ++ ++ g_return_val_if_fail (output != NULL, FALSE); ++ g_return_val_if_fail (mode != NULL, FALSE); ++ ++ for (i = 0; output->modes[i] != NULL; ++i) ++ { ++ if (output->modes[i] == mode) ++ return TRUE; ++ } ++ ++ return FALSE; ++} ++ ++gboolean ++rw_output_can_clone (RWOutput *output, ++ RWOutput *clone) ++{ ++ int i; ++ ++ g_return_val_if_fail (output != NULL, FALSE); ++ g_return_val_if_fail (clone != NULL, FALSE); ++ ++ for (i = 0; output->clones[i] != NULL; ++i) ++ { ++ if (output->clones[i] == clone) ++ return TRUE; ++ } ++ ++ return FALSE; ++} ++ ++/* RWCrtc */ ++typedef struct ++{ ++ Rotation xrot; ++ RWRotation rot; ++} RotationMap; ++static const RotationMap rotation_map[] = ++{ ++ { RR_Rotate_0, RW_ROTATION_0 }, ++ { RR_Rotate_90, RW_ROTATION_90 }, ++ { RR_Rotate_180, RW_ROTATION_180 }, ++ { RR_Rotate_270, RW_ROTATION_270 }, ++ { RR_Reflect_X, RW_REFLECT_X }, ++ { RR_Reflect_Y, RW_REFLECT_Y }, ++}; ++ ++static RWRotation ++rw_rotation_from_xrotation (Rotation r) ++{ ++ int i; ++ RWRotation result = 0; ++ ++ for (i = 0; i < G_N_ELEMENTS (rotation_map); ++i) ++ { ++ if (r & rotation_map[i].xrot) ++ result |= rotation_map[i].rot; ++ } ++ ++ return result; ++} ++ ++static Rotation ++xrotation_from_rotation (RWRotation r) ++{ ++ int i; ++ Rotation result = 0; ++ ++ for (i = 0; i < G_N_ELEMENTS (rotation_map); ++i) ++ { ++ if (r & rotation_map[i].rot) ++ result |= rotation_map[i].xrot; ++ } ++ ++ return result; ++} ++ ++gboolean ++rw_crtc_set_config (RWCrtc *crtc, ++ int x, ++ int y, ++ RWMode *mode, ++ RWRotation rotation, ++ RWOutput **outputs, ++ int n_outputs) ++{ ++ ScreenInfo *info; ++ GArray *output_ids; ++ int i; ++ ++ g_return_val_if_fail (crtc != NULL, FALSE); ++ g_return_val_if_fail (mode != NULL || outputs == NULL || n_outputs == 0, FALSE); ++ ++ info = crtc->info; ++ ++ if (mode) ++ { ++ g_return_val_if_fail (x + mode->width <= info->max_width, FALSE); ++ g_return_val_if_fail (y + mode->height <= info->max_height, FALSE); ++ } ++ ++ output_ids = g_array_new (FALSE, FALSE, sizeof (RROutput)); ++ ++ if (outputs) ++ { ++ for (i = 0; i < n_outputs; ++i) ++ g_array_append_val (output_ids, outputs[i]->id); ++ } ++ ++ XRRSetCrtcConfig (DISPLAY (crtc), info->resources, crtc->id, ++ CurrentTime, ++ x, y, ++ mode? mode->id : None, ++ xrotation_from_rotation (rotation), ++ (RROutput *)output_ids->data, ++ output_ids->len); ++ ++ g_array_free (output_ids, TRUE); ++ ++ return TRUE; ++} ++ ++RWMode * ++rw_crtc_get_current_mode (RWCrtc *crtc) ++{ ++ g_return_val_if_fail (crtc != NULL, NULL); ++ ++ return crtc->current_mode; ++} ++ ++guint32 ++rw_crtc_get_id (RWCrtc *crtc) ++{ ++ g_return_val_if_fail (crtc != NULL, 0); ++ ++ return crtc->id; ++} ++ ++gboolean ++rw_crtc_can_drive_output (RWCrtc *crtc, ++ RWOutput *output) ++{ ++ int i; ++ ++ g_return_val_if_fail (crtc != NULL, FALSE); ++ g_return_val_if_fail (output != NULL, FALSE); ++ ++ for (i = 0; crtc->possible_outputs[i] != NULL; ++i) ++ { ++ if (crtc->possible_outputs[i] == output) ++ return TRUE; ++ } ++ ++ return FALSE; ++} ++ ++/* FIXME: merge with get_mode()? */ ++void ++rw_crtc_get_position (RWCrtc *crtc, ++ int *x, ++ int *y) ++{ ++ g_return_if_fail (crtc != NULL); ++ ++ if (x) ++ *x = crtc->x; ++ ++ if (y) ++ *y = crtc->y; ++} ++ ++/* FIXME: merge with get_mode()? */ ++RWRotation ++rw_crtc_get_current_rotation (RWCrtc *crtc) ++{ ++ g_assert(crtc != NULL); ++ return crtc->current_rotation; ++} ++ ++RWRotation ++rw_crtc_get_rotations (RWCrtc *crtc) ++{ ++ g_assert(crtc != NULL); ++ return crtc->rotations; ++} ++ ++gboolean ++rw_crtc_supports_rotation (RWCrtc * crtc, ++ RWRotation rotation) ++{ ++ g_return_val_if_fail (crtc != NULL, FALSE); ++ return (crtc->rotations & rotation); ++} ++ ++static RWCrtc * ++crtc_new (ScreenInfo *info, RROutput id) ++{ ++ RWCrtc *crtc = g_new0 (RWCrtc, 1); ++ ++ crtc->id = id; ++ crtc->info = info; ++ ++ return crtc; ++} ++ ++static void ++crtc_initialize (RWCrtc *crtc, XRRScreenResources *res) ++{ ++ XRRCrtcInfo *info = XRRGetCrtcInfo (DISPLAY (crtc), res, crtc->id); ++ GPtrArray *a; ++ int i; ++ ++ g_print ("CRTC %lx Timestamp: %u\n", crtc->id, (guint32)info->timestamp); ++ ++ if (!info) ++ { ++ /* FIXME: We need to reaquire the screen resources */ ++ return; ++ } ++ ++ /* RWMode */ ++ crtc->current_mode = mode_by_id (crtc->info, info->mode); ++ ++ crtc->x = info->x; ++ crtc->y = info->y; ++ ++ /* Current outputs */ ++ a = g_ptr_array_new (); ++ for (i = 0; i < info->noutput; ++i) ++ { ++ RWOutput *output = rw_output_by_id (crtc->info, info->outputs[i]); ++ ++ if (output) ++ g_ptr_array_add (a, output); ++ } ++ g_ptr_array_add (a, NULL); ++ crtc->current_outputs = (RWOutput **)g_ptr_array_free (a, FALSE); ++ ++ /* Possible outputs */ ++ a = g_ptr_array_new (); ++ for (i = 0; i < info->npossible; ++i) ++ { ++ RWOutput *output = rw_output_by_id (crtc->info, info->possible[i]); ++ ++ if (output) ++ g_ptr_array_add (a, output); ++ } ++ g_ptr_array_add (a, NULL); ++ crtc->possible_outputs = (RWOutput **)g_ptr_array_free (a, FALSE); ++ ++ /* Rotations */ ++ crtc->current_rotation = rw_rotation_from_xrotation (info->rotation); ++ crtc->rotations = rw_rotation_from_xrotation (info->rotations); ++ ++ XRRFreeCrtcInfo (info); ++} ++ ++static void ++crtc_free (RWCrtc *crtc) ++{ ++ g_free (crtc->current_outputs); ++ g_free (crtc->possible_outputs); ++ g_free (crtc); ++} ++ ++/* RWMode */ ++static RWMode * ++mode_new (ScreenInfo *info, RRMode id) ++{ ++ RWMode *mode = g_new0 (RWMode, 1); ++ ++ mode->id = id; ++ mode->info = info; ++ ++ return mode; ++} ++ ++guint32 ++rw_mode_get_id (RWMode *mode) ++{ ++ g_return_val_if_fail (mode != NULL, 0); ++ return mode->id; ++} ++ ++guint ++rw_mode_get_width (RWMode *mode) ++{ ++ g_return_val_if_fail (mode != NULL, 0); ++ return mode->width; ++} ++ ++int ++rw_mode_get_freq (RWMode *mode) ++{ ++ g_return_val_if_fail (mode != NULL, 0); ++ return (mode->freq) / 1000; ++} ++ ++guint ++rw_mode_get_height (RWMode *mode) ++{ ++ g_return_val_if_fail (mode != NULL, 0); ++ return mode->height; ++} ++ ++static void ++mode_initialize (RWMode *mode, XRRModeInfo *info) ++{ ++ g_assert (mode != NULL); ++ g_assert (info != NULL); ++ ++ mode->name = g_strdup (info->name); ++ mode->width = info->width; ++ mode->height = info->height; ++ mode->freq = ((info->dotClock / (double)info->hTotal) / info->vTotal + 0.5) * 1000; ++} ++ ++static void ++mode_free (RWMode *mode) ++{ ++ g_free (mode->name); ++ g_free (mode); ++} ++ ++ ++#ifdef INCLUDE_MAIN ++static void ++on_screen_changed (RWScreen *screen, gpointer data) ++{ ++ g_print ("Changed\n"); ++} ++ ++static gboolean ++do_refresh (gpointer data) ++{ ++ RWScreen *screen = data; ++ ++ rw_screen_refresh (screen); ++ ++ return TRUE; ++} ++ ++int ++main (int argc, char **argv) ++{ ++ int i; ++ ++ gtk_init (&argc, &argv); ++ ++ RWScreen *screen = rw_screen_new (gdk_screen_get_default(), ++ on_screen_changed, ++ NULL); ++ ++ for (i = 0; screen->info->crtcs[i]; ++i) ++ { ++ RWCrtc *crtc = screen->info->crtcs[i]; ++ ++ if (crtc->current_mode) ++ { ++ g_print ("CRTC %p: (%d %d %d %d)\n", ++ crtc, crtc->x, crtc->y, ++ crtc->current_mode->width, crtc->current_mode->height); ++ } ++ else ++ { ++ g_print ("CRTC %p: turned off\n", crtc); ++ } ++ } ++ ++ for (i = 0; screen->info->outputs[i]; ++i) ++ { ++ RWOutput *output = screen->info->outputs[i]; ++ ++ g_print ("Output %s currently", output->name); ++ ++ if (!output->current_crtc) ++ g_print (" turned off\n"); ++ else ++ g_print (" driven by CRTC %p\n", output->current_crtc); ++ } ++ ++ g_timeout_add (500, do_refresh, screen); ++ ++ gtk_main (); ++ ++ return 0; ++} ++#endif diff --git a/gnome-desktop.changes b/gnome-desktop.changes index e218607..02b6f12 100644 --- a/gnome-desktop.changes +++ b/gnome-desktop.changes @@ -1,3 +1,9 @@ +------------------------------------------------------------------- +Fri Apr 25 18:41:10 CEST 2008 - federico@novell.com + +- Added gnome-desktop-randr-1.2.diff to integrate the new + functionality to configure RandR 1.2. + ------------------------------------------------------------------- Mon Apr 21 12:32:58 CEST 2008 - vuntz@suse.de diff --git a/gnome-desktop.spec b/gnome-desktop.spec index b962725..be51e75 100644 --- a/gnome-desktop.spec +++ b/gnome-desktop.spec @@ -17,7 +17,7 @@ License: GNU Free Documentation License, Version 1.1 (GFDL 1.1); GPL v2 o Group: System/GUI/GNOME Obsoletes: gnome-core Version: 2.22.1 -Release: 5 +Release: 8 Summary: The GNOME Desktop API Library Source: %{name}-%{version}.tar.bz2 Url: http://www.gnome.org @@ -27,6 +27,8 @@ Patch1: X-KDE-SubstituteUID.dif Patch2: gnome-desktop-desktop.patch # PATCH-FEATURE-OPENSUSE gnome-desktop-recently-used-apps.patch -- Add launched .desktop files to recently used apps. Patch3: gnome-desktop-recently-used-apps.patch +# PATCH-FEATURE-UPSTREAM gnome-desktop-randr-1.2.diff -- Add support for RandR 1.2 to the libraries. federico@novell.com +Patch4: gnome-desktop-randr-1.2.diff Requires: %{name}-lang = %{version} Requires: libgnome-desktop-2-2 = %{version} @@ -121,6 +123,7 @@ Authors: %patch1 %patch2 -p0 %patch3 -p1 +%patch4 -p1 %build autoreconf -f -i @@ -179,6 +182,9 @@ rm -rf $RPM_BUILD_ROOT %{_datadir}/gtk-doc/html/gnome-desktop %changelog +* Fri Apr 25 2008 federico@novell.com +- Added gnome-desktop-randr-1.2.diff to integrate the new + functionality to configure RandR 1.2. * Mon Apr 21 2008 vuntz@suse.de - Rewrite gnome-desktop-recently-used-apps.patch so that it applies.