Commit 961adc98 authored by ROOL's avatar ROOL 🤖
Browse files

Offer export of raw EDID blob and complementary MDF export

Detail:
  Export the EDID raw data as a file in ResourceFS. This allows the Screen Setup plugin to scan that directory to pick up the currently connected monitor, and similarly select it from !Boot.Choices.PreDesk. The EDID data can also be drag & drop copied or exported for offline analysis.
  Add SaveModeFile command. This differs in that it works on the current set, to be symmetrical with LoadModeFile which loads the current set. Also fixes issues of losing the original mode names given; NULL pointer dereference when the EDID block contains no modes (eg. VIDCDriver); and no long writes out malformed MDFs which can't be loaded into !MakeModes (this seems to be because the CreateModeFile command was based on a stale copy of EDIDDecode which fixed the same bug circa 2012).
  Query the current GraphicsV driver for the deepest colour depth for the preferred mode, rather than assuming 32bpp is always possible (eg. because of bandwidth or graphics controller constraints).
  If the configured MonitorType is EDID but no EDID could be read, or it was corrupt, substitute a safe VESA monitor to allow the kernel to select something close to MODE 28.
  Add ScreenModes_Features SWI so the Screen Setup plugin knows whether it's safe to offer EDID in its dialogues.
  Retire the ReadEDID command (no longer needed since LoadModeFile can load the exported EDID blob, or one from disc) and CreateModeFile commands (see SaveModeFile).
  Internationalise the default monitor title ("Unidentified") used when EDID doesn't contain one.
Admin:
  New file - doc/BootStates documents all possible situations, and their recovery, for both the MDF and (non-hotplug) EDID schemes.
  New file - doc/EDIDGoals states the aims of the EDID support from both a user and support perspective.

  Submission for the EDID bounty.

Version 0.61. Tagged as 'ScrModes-0_61'
parent 95d1dca9
......@@ -28,3 +28,4 @@ E25:EDID checksum is incorrect (block %0 of %1 failed) - cannot read monitor set
E26:Unable to read EDID - does this hardware support it?
E27:EDID channel communications error.
E28:Bad ScreenModes_EnumerateAudioFormats flags
NoName:Unidentified
No preview for this file type
......@@ -28,3 +28,4 @@ E25:EDID checksum is incorrect (block %0 of %1 failed) - cannot read monitor set
E26:Unable to read EDID - does this hardware support it?
E27:EDID channel communications error
E28:Bad ScreenModes_EnumerateAudioFormats flags
NoName:Unidentified
/* (0.60)
/* (0.61)
*
* This file is automatically maintained by srccommit, do not edit manually.
* Last processed by srccommit version: 1.1.
*
*/
#define Module_MajorVersion_CMHG 0.60
#define Module_MajorVersion_CMHG 0.61
#define Module_MinorVersion_CMHG
#define Module_Date_CMHG 05 Mar 2017
#define Module_MajorVersion "0.60"
#define Module_Version 60
#define Module_MajorVersion "0.61"
#define Module_Version 61
#define Module_MinorVersion ""
#define Module_Date "05 Mar 2017"
......@@ -18,6 +18,6 @@
#define Module_ComponentName "ScrModes"
#define Module_ComponentPath "castle/RiscOS/Sources/Video/UserI/ScrModes"
#define Module_FullVersion "0.60"
#define Module_HelpVersion "0.60 (05 Mar 2017)"
#define Module_LibraryVersionInfo "0:60"
#define Module_FullVersion "0.61"
#define Module_HelpVersion "0.61 (05 Mar 2017)"
#define Module_LibraryVersionInfo "0:61"
......@@ -30,6 +30,10 @@
#include "Global/GraphicsV.h"
#include "Global/Services.h"
#include "Global/VduExt.h"
#include "Global/Variables.h"
#include "Global/FileTypes.h"
#include "Global/OsBytes.h"
#include "Global/CMOS.h"
#include "Global/ModHand.h"
#undef Module_Title /* CMHG defines this too */
#include "Interface/HighFSI.h"
......@@ -42,11 +46,12 @@
#include "mdfsupport.h"
#include "edidsupport.h"
/* Switch to define whether EDID is computed, or whether only traditional
* loadmodefile activity happens. EDID activity enabled by a ReadEDID
* command, and disabled by a LoadModeFile command.
/* Switch to define whether live EDID read from the monitor is in use.
* If the graphics driver supports it and MonitorType is EDID this
* will cause the EDID to be reloaded if the graphics driver changes.
* Should an MDF be loaded, this behaviour is cancelled (latching).
*/
bool EDIDEnabled;
static bool using_edid = false;
/* Pointer to (root block of) current monitor definition structure.
* This becomes valid (non NULL) on successful completion of a
......@@ -77,49 +82,72 @@ int old_monitortype = -1; /* -1 means we haven't loaded a file yet */
* about efficiency; e.g. we could keep the file open and the handle
* lying around, but there really isn't any point.
*/
_kernel_oserror *error(int error, const char *arg0, const char *arg1, const char *arg2)
_kernel_oserror *messages_lookup(char *buffer, size_t space, const char *token,
const char *arg0, const char *arg1, const char *arg2)
{
/* Where the final returned message is constructed */
static _kernel_oserror theerror;
/* Handle for MessageTrans. */
int file_data[4];
_kernel_swi_regs r;
_kernel_oserror *res;
char token[8];
/* Open the Messages file */
r.r[0] = (int)file_data;
r.r[1] = (int)Module_MessagesFile;
r.r[2] = 0;
if ((res = _kernel_swi(MessageTrans_OpenFile, &r, &r)) != NULL)
res = _swix(MessageTrans_OpenFile, _INR(0,2),
file_data, Module_MessagesFile, 0);
if (res != NULL)
{
return res;
}
sprintf(token, "E%02d", error);
r.r[0] = (int)file_data;
r.r[1] = (int)token;
r.r[2] = (int)theerror.errmess;
r.r[3] = 252;
r.r[4] = (int)arg0; /* for %0 */
r.r[5] = (int)arg1; /* for %1 */
r.r[6] = (int)arg2; /* for %2 */
r.r[7] = 0; /* not expecting %3, so don't substitute */
res = _kernel_swi(MessageTrans_Lookup, &r, &r);
res = _swix(MessageTrans_Lookup, _INR(0,7),
file_data, token, buffer, space, arg0, arg1, arg2, NULL);
/* Always close the messages file, ignoring possible but most unlikely errors */
r.r[0] = (int)file_data;
(void)_kernel_swi(MessageTrans_CloseFile, &r, &r);
/* Check for error from the lookup */
if (res)
(void)_swix(MessageTrans_CloseFile, _IN(0), file_data);
if (res != NULL)
{
return res; /* lookup failed */
buffer[0] = '?'; buffer[1] = '\0'; /* lookup failed */
}
return res;
}
_kernel_oserror *error(int error, const char *arg0, const char *arg1, const char *arg2)
{
/* Where the final returned message is constructed */
static _kernel_oserror theerror;
_kernel_oserror *res;
char token[8];
sprintf(token, "E%02d", error);
res = messages_lookup(theerror.errmess, sizeof(theerror.errmess), token, arg0, arg1, arg2);
if (res != NULL)
{
return res;
}
/* Construct the rest of the error block, i.e. the error number */
theerror.errnum = ERROR_BASE + error;
return &theerror;
}
_kernel_oserror *new_monitordescription(MonitorDescriptionRef *description)
{
MonitorDescriptionRef md;
md = (MonitorDescriptionRef) calloc(1, sizeof(MonitorDescription));
if (md == NULL)
{
return error(ERR_NOSPACE, 0, 0, 0);
}
messages_lookup(md->name, sizeof(md->name), "NoName", NULL, NULL, NULL);
md->output_format = -1; /* Output format */
md->external_clock = -1; /* External clock not present */
md->modelist = NULL; /* Empty initially */
md->audio_formats = NULL; /* Empty initially */
*description = md;
return NULL;
}
void free_monitordescription(MonitorDescriptionRef description)
{
ModeDescriptionRef md;
......@@ -364,10 +392,76 @@ static _kernel_oserror *loadmodefile(const char *file)
rewind(handle); /* Reset the pointer to the beginning */
res = loadtextMDF(file, handle);
}
if (res == NULL)
{
/* Use of an MDF cancels (latching) the automatic EDID side effects */
using_edid = false;
}
}
return res;
}
static _kernel_oserror *savemodefile(const char *file)
{
char *fbuf;
FILE *f = NULL;
size_t length;
ModeDescriptionRef this;
if (current_monitor == NULL)
{
return error(ERR_NOMODEFILE, 0, 0, 0);
}
/* open file for new mode file if required */
/* file is a string terminated in 0x0D */
length = strcspn(file, "\r");
fbuf = malloc(length + 1);
if (fbuf)
{
memcpy(fbuf, file, length);
fbuf[length] = '\0';
f = fopen(fbuf, "w");
free(fbuf);
}
if (f == NULL)
{
return _kernel_last_oserror();
}
fprintf(f, "# Exported from ScrModes " Module_VersionString "\n"
"file_format:1\n"
"monitor_title:%s\n", current_monitor->name);
if (current_monitor->dpms_state!=-1)
{
fprintf(f, "DPMS_state:%d\n\n", current_monitor->dpms_state);
}
this = current_monitor->modelist;
while (this)
{
ModeDef *mp = &this->definition.timings;
fprintf(f, "# Mode: %d x %d @ %dHz\n", mp->xres, mp->yres, this->frame_hz);
fprintf(f, "startmode\n"
" mode_name:%s\n"
" x_res:%d\n"
" y_res:%d\n"
" pixel_rate:%d\n", this->definition.name, mp->xres, mp->yres, mp->pixel_khz);
fprintf(f, " h_timings:%d,%d,%d,%d,%d,%d\n", mp->hpar[0], mp->hpar[1], mp->hpar[2],
mp->hpar[3], mp->hpar[4], mp->hpar[5]);
fprintf(f, " v_timings:%d,%d,%d,%d,%d,%d\n", mp->vpar[0], mp->vpar[1], mp->vpar[2],
mp->vpar[3], mp->vpar[4], mp->vpar[5]);
fprintf(f, " sync_pol:%d\n", mp->syncpol);
if (mp->interlaced == 1)
{
fprintf(f, " interlaced\n");
}
fprintf(f,"endmode\n\n");
this = this->next;
}
fclose(f);
return NULL;
}
/* List of new PixelFormats available from old GraphicsV_DisplayFeatures */
static PixelFormat oldformats[] = {
{1,0,0},
......@@ -387,7 +481,7 @@ static void pixelformat_from_depth(PixelFormatRef pf,int depth)
}
/* Can this PixelFormat be represented as a pixel depth? (if so, pf->log2bpp is the value) */
static bool is_old_format(const PixelFormatRef pf)
bool is_old_format(const PixelFormatRef pf)
{
if (pf->log2bpp > 5)
return false;
......@@ -591,6 +685,55 @@ static int mode_valid(ModeDescriptionRef mp, const PixelFormatRef pf,
return 1;
}
bool find_deepest_by_xyhz(PixelFormatRef fp, ModeDescriptionRef mp)
{
_kernel_swi_regs r;
PixelFormatRef pflist;
size_t pflistlen;
int driver, maxlog2bpp = 0;
bool found = false;
/* See if the driver is helpful in listing the pixel formats */
driver = current_graphicsv_driver();
r.r[4] = GraphicsV_PixelFormats | (driver<<24);
r.r[9] = GraphicsV;
_kernel_swi(OS_CallAVector, &r, &r);
if (r.r[4] == 0)
{
pflist = (PixelFormatRef) r.r[0];
pflistlen = r.r[1];
}
else
{
/* Use the old list */
pflist = oldformats;
pflistlen = sizeof(oldformats)/sizeof(oldformats[0]);
}
IFDEBUG printf("Trying %d pixel formats\n", pflistlen);
/* The format list isn't necessarily sorted by depth, so just try them
* all to find the highest log2bpp.
*/
while (pflistlen)
{
if (mode_valid(mp, pflist, INT32_MAX, INT32_MAX))
{
/* That depth is achievable, keep it if bigger */
if (pflist->log2bpp > maxlog2bpp)
{
maxlog2bpp = pflist->log2bpp;
IFDEBUG printf("Deeper mode_valid at log2bpp = %d\n", maxlog2bpp);
*fp = *pflist;
found = true;
}
}
pflist++;
pflistlen--;
}
return found;
}
static ModeDescriptionRef find_by_xy(ModeDescriptionRef mp,
uint32_t xres, uint32_t yres, int *count)
{
......@@ -798,7 +941,6 @@ static void service_enumeratescreenmodes(_kernel_swi_regs *regs)
pflistlen = sizeof(oldformats)/sizeof(oldformats[0]);
}
/* Scan all available modes at all available pixel formats */
mp = current_monitor->modelist;
pf = pflist;
......@@ -1032,29 +1174,26 @@ retry_with_4bpp:
static void service_displaychanged(_kernel_swi_regs *regs)
{
_kernel_oserror *res;
switch (regs->r[0])
{
case DisplayChanged_Changing:
/* The current driver is changing. We must recache any
* driver-specific things which we store.
* At the moment all the driver-specific stuff happens during the
* enumerate/translate calls, so there's nothing to do.
* enumerate/translate calls, so just check the monitor's still there.
* Note that doing it here means:
* a) ScreenModes must precede the driver if you want a mode defined
* during module init AND the MonitorType is EDID. If the MonitorType
* is some other value, including Auto, it doesn't matter
* where ScreenModes is as the Kernel will use a built in numbered mode.
* b) Loading ScreenModes off disc won't automatically load the EDID data
* as this service call doesn't happen. Calling readedid() from module
* init isn't possible because the module isn't linked to the service
* call chain, so Display Manager et al can't enumerate the modes
* with OS_ScreenMode 2. This is only a problem for developers, who
* can manually re-read it (standalone builds are only partly supported today).
*/
if (EDIDEnabled)
{
res = readedid(regs->r[2],NULL);
}
break;
case DisplayChanged_Changed:
/* Display change is complete
* Issue mode file changed service call so that the display manager
* is up to date.
* (might be possible to skip this if EDID is in use and EDID has
* already issued it)
*/
_swix(OS_ServiceCall,_IN(1),Service_ModeFileChanged);
readedid(regs->r[2], using_edid);
break;
}
}
......@@ -1239,19 +1378,31 @@ find_next_sub_entry:
/* EXPORTED */
_kernel_oserror *ScreenModes_initialise(const char *cmd_tail, int podule_base, void *pw)
{
preferred_mode = malloc(sizeof(ModeSelector));
if (preferred_mode)
char typevar[16];
int config;
/* Keep a preferred mode with space for 2 extra mode variables */
preferred_mode = calloc(1, sizeof(ModeSelector) + (2 * sizeof(ModeParam)));
/* Define the EDID type name */
sprintf(typevar, "File$Type_%03X", FileType_EDID);
_swix(OS_SetVarVal, _INR(0,4), typevar, FileType_EDID_Name,
strlen(FileType_EDID_Name), 0, VarType_String);
/* Look at the MonitorType to see whether to proactively parse EDID */
if (!_swix(OS_Byte, _INR(0,1) | _OUT(2), OsByte_ReadCMOS, VduCMOS, &config))
{
memset(preferred_mode,0,sizeof(ModeSelector));
using_edid = ((config & MonitorTypeBits) >> MonitorTypeShift) == MONITOR_EDID;
}
UNUSED(cmd_tail);
UNUSED(podule_base);
UNUSED(pw);
EDIDEnabled = 0; /* not enabled on module boot */
return NULL;
}
/* EXPORTED */
_kernel_oserror *ScreenModes_final(int fatal, int podule, void *pw)
{
/* Free the space claimed for the current monitor
......@@ -1260,6 +1411,7 @@ _kernel_oserror *ScreenModes_final(int fatal, int podule, void *pw)
*/
(void) restore_monitortype(); /* restore old value */
release_currentmonitor();
release_edidblocks();
if (preferred_mode)
{
free(preferred_mode);
......@@ -1318,11 +1470,8 @@ _kernel_oserror *ScreenModes_cmdhandler(const char *arg_string, int argc, int cm
case CMD_LoadModeFile:
result = loadmodefile(arg_string);
break;
case CMD_ReadEDID:
result = readedid(current_graphicsv_driver(),NULL);
break;
case CMD_CreateModeFile:
result = readedid(current_graphicsv_driver(),arg_string);
case CMD_SaveModeFile:
result = savemodefile(arg_string);
break;
default:
return NULL;
......@@ -1392,6 +1541,11 @@ _kernel_oserror *ScreenModes_swihandler(int swi_no, _kernel_swi_regs *r, void *p
case ScreenModes_EnumerateAudioFormats - ScreenModes_00:
return swi_enumerateaudioformats(r);
case ScreenModes_Features - ScreenModes_00:
r->r[0] = ScreenModes_Features_EDID; /* These extra features are supported */
result = NULL;
break;
default: /* Unknown ScreenModes SWI */
return error_BAD_SWI;
}
......
This diff is collapsed.
......@@ -719,22 +719,22 @@ static _kernel_oserror *parse_modefile(MonitorDescriptionRef *description)
}
res = check_keyword(k_monitor_title, 1);
if (res)
if (res == NULL)
{
/* OK, commit to reading a monitor description - go allocate space */
res = new_monitordescription(&md);
}
if (res != NULL)
{
/* Fatal */
return res;
}
/* OK, commit to reading a monitor description - go allocate space */
md = (MonitorDescriptionRef) malloc(sizeof(MonitorDescription));
if (md == NULL)
if (res == NULL)
{
return error(ERR_NOSPACE, 0, 0, 0);
res = read_text(md->name, sizeof(md->name), ERR_BLANKMONTITLE);
}
/* From here on, need to release memory on error, so do most of
* the rest as a subroutine.
*/
memset(md, 0, sizeof(MonitorDescription));
md->modelist = NULL; /* to start with */
res = read_text(md->name, sizeof(md->name), ERR_BLANKMONTITLE);
/* Now check for optional DPMS_state keyword */
if (res == NULL)
......@@ -835,7 +835,6 @@ _kernel_oserror *loadtextMDF(const char *file, FILE *handle)
_kernel_oserror *res;
MonitorDescriptionRef new_monitor;
EDIDEnabled = 0; /* disable any automatic edid stuff */
thefile = handle;
thefilename = file;
lineno = 1;
......
......@@ -43,15 +43,12 @@ command-keyword-table: ScreenModes_cmdhandler
LoadModeFile(min-args:1, max-args: 1, international:,
invalid-syntax: "SSMDLMF",
help-text: "HSMDLMF"),
ReadEDID(min-args:0,max-args:0, international:,
invalid-syntax: "SSMDRED",
help-text: "HSMDRED"),
CreateModeFile(min-args:1,max-args:1, international:,
invalid-syntax: "SSMDCMF",
help-text: "HSMDCMF")
SaveModeFile(min-args:1, max-args: 1, international:,
invalid-syntax: "SSMDSMF",
help-text: "HSMDSMF")
swi-chunk-base-number: 0x487C0
swi-handler-code: ScreenModes_swihandler
swi-decoding-table: ScreenModes ReadInfo EnumerateAudioFormats
swi-decoding-table: ScreenModes ReadInfo EnumerateAudioFormats Features
1.MDF based scheme
~~~~~~~~~~~~~~~~~~
OS: Doesn't support EDID, configured with MonitorType Auto
Monitor is? Plugged in? Outcome? Recovery story?
---------- ---------- ------- --------------
Same model Yes Good image (most common situation) -
as earlier No No image -
No but later Good image (LoadModeFile loaded the MDF regardless) -
reconnected
Different model Yes Only a good image if earlier MDF works on new monitor Blind type *WimpMode, then run !Configure to choose new model
than before No No image -
No but later Only a good image if earlier MDF works on new monitor Blind type *WimpMode, then run !Configure to choose new model
reconnected
2.EDID based scheme
~~~~~~~~~~~~~~~~~~~
OS: Does support EDID, has !Boot on read/write medium, EDID monitor was selected in !ScrnSetup
Monitor is? Plugged in? Outcome? Recovery story?
---------- ---------- ------- --------------
Same model Yes Early boot uses CMOS MonitorType until monitor choices -
as earlier obey file reached => good image (most common situation)
No No image -
No but later Only a good image if OS's default mode (SVGA?) supported Blind type *LoadModeFile
reconnected or reboot
Different model Yes Early boot uses CMOS MonitorType until monitor choices -
than before obey file reached => good image (read from new monitor)
No No image -
No but later Only a good image if OS's default mode (SVGA?) supported Blind type *LoadModeFile
reconnected or reboot
3.EDID based scheme softloaded on top of MDF only ROM
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
OS: Initial boot from old OS, as early as OS 3.50, softload does support EDID and was selected in !ScrnSetup
Monitor is? Plugged in? Outcome? Recovery story?
---------- ---------- ------- --------------
Same model Yes Early boot uses Auto until softload starts then good -
as earlier image (most common situation); if softload is not
selected, loads MDF and behaves per scenario 1
No No image
No but later Only a good image if OS's default mode (SVGA?) supported Blind type *LoadModeFile
reconnected or reboot
Different model Yes Early boot uses Auto until softload starts then good -
than before image (read from new monitor)
No No image -
No but later Only a good image if OS's default mode (SVGA?) supported Blind type *LoadModeFile
reconnected or reboot
4.EDID based scheme discless boot
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
OS: Does support EDID, no disc, or failed !Boot, configured MonitorType is EDID
Monitor is? Plugged in? Outcome? Recovery story?
---------- ---------- ------- --------------
Same model Yes Preferred mode chosen (most common situation) -
as earlier No No image -
No but later Only a good image if OS's default mode (SVGA?) supported Blind type *LoadModeFile
reconnected or reboot
Different model Yes Preferred mode chosen (read from new monitor) -
than before No No image -
No but later Only a good image if OS's default mode (SVGA?) supported Blind type *LoadModeFile
reconnected or reboot
Notes
~~~~~
Monitors are required to return EDID data if connected in, even if not turned on - the EDID memory is powered by
the motherboard. Therefore, the above tables only consider the connected/not connected possibilities since the
monitor being off or on isn't important.
Tables assume hotplug is not supported by the hardware, let alone the software, and that EDID data from the
monitor is truthful. Hotplug support in a later version will remove all requirements to recover a bad mode choice
by blind typing when the monitor is plugged in after booting, presently a reboot is required.
Statement of aims
~~~~~~~~~~~~~~~~~
* Resiliance against corrupt or inaccurate EDID data from the monitor
=> some default display, and ability to override EDID data with 'known good' library copy
in a similar manner to Windows overriding the EDID from an INF file as described here
https://msdn.microsoft.com/en-us/library/windows/hardware/jj133967(v=vs.85).aspx
=> allow loading of disc based EDID on platforms that don't support it (eg. Risc PC)
* Simple user interface
=> user unlikely to care where the monitor settings came from, but they must work at least
as well as the previous MDF system
=> checkbox or menu entry in !Configure plugin
* Fallback to MonitorType=Auto behaviour if EDID unavailable
=> avoids problem when softload sets up to use EDID but underlying ROM OS doesn't support it
=> R-power-on/Delete-power-on/Keypad-dot-power-on revert to Auto as currently
* Advanced diagnostics for support staff
=> "many to 1" problem, so most of the complexity should reside on the support staff's desk,
the remote system where the problem lies can be relatively simple
=> PC tools used to offline analyse EDID dumps (or, enhanced !MakeModes application)
=> means to capture EDID dump (utility, star command, BASIC, or similar)
* Discless boot
=> ability to select EDID without a boot sequence having run
=> ensure that the initial mode (prior to !Boot running) is feasible by vetting modes with the
graphics driver
=> fallback to kernel's numbered modes if EDID is corrupt or inaccurate
* Hotplug and multi head
=> not currently supported, but design should not preclude their introduction in future, for
example by setting the CMHG keyword matcher to let through some extra args but ignore
them today
......@@ -13,7 +13,8 @@
* limitations under the License.
*/
/* edidsupport.h */
void release_edidblocks(void);
_kernel_oserror *loadedid(const char *file);
_kernel_oserror *readedid(int, const char *);
_kernel_oserror *readedid(int, bool);
/* EOF edidsupport.h */
......@@ -163,6 +163,7 @@ typedef struct
#define IS_MODE_NUMBER(p) ((uint32_t)(p) < 256)
#define IS_MODE_SEL_PTR(p) (!IS_MODE_NUMBER(p))
#define ALIGN(a) (((a) + 3) & ~3)
#define UNUSED(k) ((k)=(k))
#define MODESEL(p) ((ModeSelectorRef)(p))
#define MAX(a,b) (((a)<(b))?(b):(a))
......@@ -257,15 +258,18 @@ typedef struct
/* Extern variables and functions */
extern ModeSelectorRef preferred_mode;
extern MonitorDescriptionRef current_monitor;
extern bool EDIDEnabled;
extern int old_monitortype;
extern int preferred_sync_type;
_kernel_oserror *error(int, const char *, const char *, const char *);
_kernel_oserror *messages_lookup(char *, size_t, const char *, const char *, const char *, const char *);
_kernel_oserror *new_monitordescription(MonitorDescriptionRef *);
void release_currentmonitor(void);