/*
  Copyright (c) 2000 Caldera Systems

  This program is free software; you can redistribute it and/or modify
  it under the terms of the GNU General Public License as published by
  the Free Software Foundation; either version 2 of the License, or
  (at your option) any later version.

  This program is distributed in the hope that it will be useful,
  but WITHOUT ANY WARRANTY; without even the implied warranty of
  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  GNU General Public License for more details.

  You should have received a copy of the GNU General Public License
  along with this program; if not, write to the Free Software
  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/

#include "kxdata.h"

#include <qptrlist.h>
#include <qfile.h>
#include <qtextstream.h>

#include <klocale.h>
#include <kdebug.h>
#include <kmessagebox.h>
#include <kstandarddirs.h>

#include <X11/Xlib.h>
#ifndef __osf__
#include <X11/extensions/xf86misc.h>
#endif

// grmbllll
#undef Bool

#include <hwdata.h>

void KXMonitorData::setHSync(float min, float max)
{
    detach();
    d->m_hMin = min;
    d->m_hMax = max;
    d->m_hSyncStr = QString::number( d->m_hMin ) + '-' + QString::number( d->m_hMax );
}

void KXMonitorData::setVSync(float min, float max)
{
    detach();
    d->m_vMin = min;
    d->m_vMax = max;
    d->m_vSyncStr = QString::number( d->m_vMin ) + '-' + QString::number( d->m_vMax );
}

QString KXModeData::modeLine() const
{
    QString line;

    line += "\"" + name() + "\" ";

    line += QString::number( dotClock() ) + " ";

    KXModeTiming h = hTimings();
    line += QString::number( h.resolution ) + " ";
    line += QString::number( h.syncBegin ) + " ";
    line += QString::number( h.syncEnd ) + " ";
    line += QString::number( h.total ) + " ";

    KXModeTiming v = vTimings();
    line += QString::number( v.resolution ) + " ";
    line += QString::number( v.syncBegin ) + " ";
    line += QString::number( v.syncEnd ) + " ";
    line += QString::number( v.total ) + " ";

    line += flags();

    return line;
}

/**
    This function is used by ScreenPage::sortModes() to sort a ModeList. It is done in
    a way that bigger resolutions with higher refresh rates come first. x resolution
    takes precedence over y resolution and resolution takes precedence over refresh
    rate.
*/
bool KXModeData::operator<( const KXModeData &m)
{
    if ( xResolution() > m.xResolution() )
        return true;
    if ( xResolution() == m.xResolution() )
    {
        if (yResolution() > m.yResolution() )
            return true;
        if (yResolution() == m.yResolution())
        {
            if (vRefresh() > m.vRefresh())
                return true;
        }
    }

    return false;
}

/**
    Two monitors are considered to be equal, if they have the same vendor and
    model name and their horizontal and vertical frequency ranges are identical.
*/
bool KXMonitorData::operator==( const KXMonitorData &m) const
{
    if ( model() == m.model() && vendor() == m.vendor() &&
         hSyncMin() == m.hSyncMin() && hSyncMax() == m.hSyncMax() &&
         vSyncMin() == m.vSyncMin() && vSyncMax() == m.vSyncMax() )
        return true;
    else
        return false;
}

QString KXVideoCardData::name() const
{
    chk();
    if ( d->m_name.isEmpty() ) {
        return vendor() + " " + model();
    } else {
        return d->m_name;
    }
}


KXData *KXData::s_global = 0;
KXModeDataList *KXData::s_vesaModes = 0;

KXData::KXData()
{
}

bool KXData::loadMonitorData()
{
    HWData db;

    // don't use appdata here because at this point there might be no kapp, yet
    if ( !db.open(  locate( "data", "kxconfig/hwdata/Monitors" ) ) )
    {
        KMessageBox::error( 0, i18n( "Cannot locate hardware database!\n"
                                     "Please make sure the hwdata package is installed." ) );
        return false;
    }

    HWDataEntryList data = db.data();

    uint horizSyncKey = db.indexOfKey( "HorizSync" );
    uint vertRefreshKey = db.indexOfKey( "VertRefresh" );
    uint vendorKey = db.indexOfKey( "Vendor" );
    uint modelKey = db.indexOfKey( "Model" );
    uint sizeKey = db.indexOfKey( "Size" );

    // ### HACK
    QString typicalMonitorsVendor = i18n( "Typical Monitors" );

    HWDataEntry typicalMonitorEntry;
    typicalMonitorEntry << QString::null << QString::null << QString::null << QString::null << QString::null;

    typicalMonitorEntry[ vendorKey ] = typicalMonitorsVendor;
    typicalMonitorEntry[ modelKey ] = QString::fromLatin1( "640x480, 60Hz" );
    typicalMonitorEntry[ horizSyncKey ] = QString::fromLatin1( "31.5-31.5" );
    typicalMonitorEntry[ vertRefreshKey ] = QString::fromLatin1( "60-60" );
    typicalMonitorEntry[ sizeKey ] = QString::fromLatin1( "14" );

    data.append( typicalMonitorEntry );

    typicalMonitorEntry[ modelKey ] = QString::fromLatin1( "800x600, 56Hz" );
    typicalMonitorEntry[ horizSyncKey ] = QString::fromLatin1( "31.5-31.5" );
    typicalMonitorEntry[ vertRefreshKey ] = QString::fromLatin1( "55-60" );
    typicalMonitorEntry[ sizeKey ] = QString::fromLatin1( "14" );

    data.append( typicalMonitorEntry );

    typicalMonitorEntry[ modelKey ] = QString::fromLatin1( "800x600, 60Hz" );
    typicalMonitorEntry[ horizSyncKey ] = QString::fromLatin1( "31.5-37.9" );
    typicalMonitorEntry[ vertRefreshKey ] = QString::fromLatin1( "55-90" );
    typicalMonitorEntry[ sizeKey ] = QString::fromLatin1( "14" );

    data.append( typicalMonitorEntry );

    typicalMonitorEntry[ modelKey ] = QString::fromLatin1( "1024x768, 60Hz" );
    typicalMonitorEntry[ horizSyncKey ] = QString::fromLatin1( "48.5-55" );
    typicalMonitorEntry[ vertRefreshKey ] = QString::fromLatin1( "55-90" );
    typicalMonitorEntry[ sizeKey ] = QString::fromLatin1( "15" );

    data.append( typicalMonitorEntry );

    typicalMonitorEntry[ modelKey ] = QString::fromLatin1( "1024x768, 70Hz" );
    typicalMonitorEntry[ horizSyncKey ] = QString::fromLatin1( "31.5-57" );
    typicalMonitorEntry[ vertRefreshKey ] = QString::fromLatin1( "50-90" );
    typicalMonitorEntry[ sizeKey ] = QString::fromLatin1( "15" );

    data.append( typicalMonitorEntry );

    typicalMonitorEntry[ modelKey ] = QString::fromLatin1( "1280x1024, 60Hz" );
    typicalMonitorEntry[ horizSyncKey ] = QString::fromLatin1( "31.5-64.3" );
    typicalMonitorEntry[ vertRefreshKey ] = QString::fromLatin1( "50-90" );
    typicalMonitorEntry[ sizeKey ] = QString::fromLatin1( "16" );

    data.append( typicalMonitorEntry );

    typicalMonitorEntry[ modelKey ] = QString::fromLatin1( "1280x1024, 74Hz" );
    typicalMonitorEntry[ horizSyncKey ] = QString::fromLatin1( "31.5-79" );
    typicalMonitorEntry[ vertRefreshKey ] = QString::fromLatin1( "50-100" );
    typicalMonitorEntry[ sizeKey ] = QString::fromLatin1( "17" );

    data.append( typicalMonitorEntry );

    typicalMonitorEntry[ modelKey ] = QString::fromLatin1( "1280x1024, 76Hz" );
    typicalMonitorEntry[ horizSyncKey ] = QString::fromLatin1( "31.5-82" );
    typicalMonitorEntry[ vertRefreshKey ] = QString::fromLatin1( "40-100" );
    typicalMonitorEntry[ sizeKey ] = QString::fromLatin1( "17" );

    data.append( typicalMonitorEntry );

    typicalMonitorEntry[ modelKey ] = QString::fromLatin1( "1280x1024, 75Hz" );
    typicalMonitorEntry[ horizSyncKey ] = QString::fromLatin1( "31.5-95" );
    typicalMonitorEntry[ vertRefreshKey ] = QString::fromLatin1( "50-150" );
    typicalMonitorEntry[ sizeKey ] = QString::fromLatin1( "19" );

    data.append( typicalMonitorEntry );

    typicalMonitorEntry[ modelKey ] = QString::fromLatin1( "1280x1024, 70Hz" );
    typicalMonitorEntry[ horizSyncKey ] = QString::fromLatin1( "31.5-105" );
    typicalMonitorEntry[ vertRefreshKey ] = QString::fromLatin1( "50-160" );
    typicalMonitorEntry[ sizeKey ] = QString::fromLatin1( "21" );

    data.append( typicalMonitorEntry );

    for ( uint i = 0; i < data.count(); ++i )
    {
        HWDataEntry entry = data[ i ];

        QString horizSync = entry[ horizSyncKey ];
        QString vertRefresh = entry[ vertRefreshKey ];

        int separatorPos = horizSync.find( '-' );
        QString hMin = horizSync.left( separatorPos );
        QString hMax = horizSync.mid( separatorPos + 1 );

        separatorPos = vertRefresh.find( '-' );
        QString vMin = vertRefresh.left( separatorPos );
        QString vMax = vertRefresh.mid( separatorPos + 1 );

        KXModeDataList modes;

        KXMonitorData monitor( entry[ vendorKey ], entry[ modelKey ],
                               entry[ sizeKey ].toInt(),
                               hMin.toFloat(), hMax.toFloat(),
                               vMin.toFloat(), vMax.toFloat(), modes );

        m_monitors[ entry[ vendorKey ] ].append( monitor );

//        kdDebug() << "hwdata: '" << entry[vendorKey] << "' '" << entry[modelKey] << "'" << endl;
    }

    return true;
}

bool KXData::loadVideoCardData()
{
    HWData db;

    // don't use appdata here because at this point there might be no kapp, yet
    QFile f( locate( "data", "kxconfig/hwdata/VideoCards" ) );

    if ( !db.open( locate( "data", "kxconfig/hwdata/VideoCards" ) ) )
    {
        KMessageBox::error( 0, i18n( "Cannot locate hardware database!\n"
                                     "Please make sure the hwdata package is installed." ) );
        return false;
    }

    uint vendorKey = db.indexOfKey( "Vendor" );
    uint modelKey = db.indexOfKey( "Model" );
    uint nameKey = db.indexOfKey( "Name" );
    uint pciVendorKey = db.indexOfKey( "PCI-Vendor" );
    uint pciDeviceKey = db.indexOfKey( "PCI-Device" );
    uint serverKey = db.indexOfKey( "Server" );
    uint driverKey = db.indexOfKey( "Driver" );

    // ### temporary hack until 'vesa' is listed in hardware database
    bool haveVesaItem = false;

    for ( uint i = 0; i < db.count(); ++i )
    {
        HWDataEntry entry = db[ i ];

        QString vendor = entry[ vendorKey ];
        QString pciVendor = entry[ pciVendorKey ];

        QString driver = entry[ driverKey ];

        if ( driver.isEmpty() || driver.lower() == "none" )
            continue;

        // ### FIXME!!
        KXVideoCardData vcard( vendor, entry[ modelKey ], entry[ nameKey ],
                               pciVendor.mid( 2 ).toUInt( 0, 16 ),
                               entry[ pciDeviceKey ].mid( 2 ).toUInt( 0, 16 ),
                               entry[ serverKey ], driver );

        m_videoCards[ vendor ].append( vcard );

        if ( !pciVendor.isEmpty() && !vendor.isEmpty() )
            m_pciVendorIDMap.insert( pciVendor.mid( 2 ).toUInt( 0, 16 ), vendor );

        if ( vendor == QString::fromLatin1( "Generic" ) &&
             vcard.model() == "vesa" )
            haveVesaItem = true;
    }

    if ( !haveVesaItem )
    {
        KXVideoCardData vcard( QString::fromLatin1( "Generic" ),
                               QString::fromLatin1( "vesa" ),
                               QString::fromLatin1( "Generic vesa" ),
                               0, 0, QString::null, "vesa" );


        m_videoCards[ QString::fromLatin1( "Generic" ) ].append( vcard );
    }

    return true;
}

void KXData::loadPointerData()
{
    // ### hardcoded

#ifndef __osf__
    m_pointerModels.append( KXPointerModel( i18n( "Generic PS/2" ), "PS/2",
                                            MTYPE_PS_2 ) );
    m_pointerModels.append( KXPointerModel( i18n( "Generic PS/2 Wheelmouse" ), "PS/2",
                                            MTYPE_PS_2, KX_PS2, 3, 1 ) );
    m_pointerModels.append( KXPointerModel( "Mouse Man Plus", "MouseManPlusPS/2",
                                            MTYPE_MMANPLUSPS2, KX_PS2,
                                            3, 1 ) );
    m_pointerModels.append( KXPointerModel( "MS IntelliMouse", "IMPS/2",
                                            MTYPE_IMPS2, KX_PS2,
                                            3, 1 ) );
    m_pointerModels.append( KXPointerModel( "MS Explorer", "ExplorerPS/2",
                                            MTYPE_IMPS2, KX_PS2,
                                            3, 1 ) );
    m_pointerModels.append( KXPointerModel( "Thinking Mouse", "ThinkingMousePS/2",
                                            MTYPE_THINKINGPS2, KX_PS2,
                                            3, 1 ) );
    m_pointerModels.append( KXPointerModel( "GlidePoint", "GlidePointPS/2",
                                            MTYPE_GLIDEPOINTPS2 ) );
    m_pointerModels.append( KXPointerModel( "Net Mouse", "NetMousePS/2",
                                            MTYPE_NETPS2, KX_PS2,
                                            3, 1 ) );
    m_pointerModels.append( KXPointerModel( "NetScroll Mouse", "NetScrollPS/2",
                                            MTYPE_NETSCROLLPS2, KX_PS2,
                                            3, 1 ) );

    m_pointerModels.append( KXPointerModel( "Microsoft", "Microsoft", MTYPE_MICROSOFT,
                                            KX_Serial ) );
    m_pointerModels.append( KXPointerModel( "MS IntelliMouse", "IntelliMouse", MTYPE_MICROSOFT,
                                            KX_Serial, 3, 1 ) );
    m_pointerModels.append( KXPointerModel( "Logitech", "Logitech", MTYPE_LOGIMAN,
                                            KX_Serial ) );
    m_pointerModels.append( KXPointerModel( "Mouse Systems", "MouseSystems", MTYPE_MOUSESYS,
                                            KX_Serial ) );
    m_pointerModels.append( KXPointerModel( "Sun Mouse", "Sun", 0 /* ### FIXME??? */,
                                            KX_Serial ) );

    m_pointerModels.append( KXPointerModel( "Bus mouse", "BusMouse", MTYPE_BUSMOUSE,
                                            KX_Bus ) );

    m_pointerModels.append( KXPointerModel( "USB mouse", "USB", MTYPE_IMPS2, // ### ???
                                            KX_USB ) );

    m_currentPointers.append( KXPointerData( m_pointerModels[ 0 ],
                              QString::fromLatin1( "/dev/psaux" ) ) ); // one mouse default. fixme?
#endif
}

void KXData::loadKeyboardData()
{
    // keyboard model and layout lists are hardcoded for now
    m_keyboardModels.append( KXKeyboardModel(QString::fromLatin1("Generic 101-key PC"),
                                             QString::fromLatin1("pc101")));
    m_keyboardModels.append( KXKeyboardModel(QString::fromLatin1("Generic 102-key Intl PC"),
                                             QString::fromLatin1("pc102")));
    m_keyboardModels.append( KXKeyboardModel(QString::fromLatin1("Generic 104-key PC"),
                                             QString::fromLatin1("pc104")));
    m_keyboardModels.append( KXKeyboardModel(QString::fromLatin1("Generic 105-key PC"),
                                             QString::fromLatin1("pc105")));
    m_keyboardModels.append( KXKeyboardModel(QString::fromLatin1("Dell 101-key PC"),
                                             QString::fromLatin1("dell101")));
    m_keyboardModels.append( KXKeyboardModel(QString::fromLatin1("Everex STEPnote"),
                                             QString::fromLatin1("STEPnote")));
    m_keyboardModels.append( KXKeyboardModel(QString::fromLatin1("Keytronic FlexPro"),
                                             QString::fromLatin1("FlexPro")));
    m_keyboardModels.append( KXKeyboardModel(QString::fromLatin1("Microsoft Natural"),
                                             QString::fromLatin1("microsoft")));
    m_keyboardModels.append( KXKeyboardModel(QString::fromLatin1("Northgate OmniKey 101"),
                                             QString::fromLatin1("omnikey101")));
    m_keyboardModels.append( KXKeyboardModel(QString::fromLatin1("Winbook Model XP5"),
                                             QString::fromLatin1("XP5")));
    m_keyboardModels.append( KXKeyboardModel(QString::fromLatin1("Japanese 106-key"),
                                             QString::fromLatin1("jp106")));
    m_keyboardModels.append( KXKeyboardModel(QString::fromLatin1("Sun Type-4"),
                                             QString::fromLatin1("sun4")));
    m_keyboardModels.append( KXKeyboardModel(QString::fromLatin1("Sun Type-5"),
                                             QString::fromLatin1("sun5")));

    m_keyboardLayouts.append( KXKeyboardLayout(QString::fromLatin1("U.S. English"),
                                               QString::fromLatin1("us")));
    m_keyboardLayouts.append( KXKeyboardLayout(QString::fromLatin1("U.S. English/Euro"),
                                               QString::fromLatin1("us_group3")));
    m_keyboardLayouts.append( KXKeyboardLayout(QString::fromLatin1("Belgian"),
                                               QString::fromLatin1("be")));
    m_keyboardLayouts.append( KXKeyboardLayout(QString::fromLatin1("Bulgarian"),
                                               QString::fromLatin1("bg")));
    m_keyboardLayouts.append( KXKeyboardLayout(QString::fromLatin1("Canadian"),
                                               QString::fromLatin1("ca")));
    m_keyboardLayouts.append( KXKeyboardLayout(QString::fromLatin1("Czech"),
                                               QString::fromLatin1("cz")));
    m_keyboardLayouts.append( KXKeyboardLayout(QString::fromLatin1("Czech (qwerty)"),
                                               QString::fromLatin1("cz_qwerty")));
    m_keyboardLayouts.append( KXKeyboardLayout(QString::fromLatin1("Slovak"),
                                               QString::fromLatin1("sk")));
    m_keyboardLayouts.append( KXKeyboardLayout(QString::fromLatin1("Swiss German"),
                                               QString::fromLatin1("de_CH")));
    m_keyboardLayouts.append( KXKeyboardLayout(QString::fromLatin1("Danish"),
                                               QString::fromLatin1("dk")));
    m_keyboardLayouts.append( KXKeyboardLayout(QString::fromLatin1("Spanish"),
                                               QString::fromLatin1("es")));
    m_keyboardLayouts.append( KXKeyboardLayout(QString::fromLatin1("German"),
                                               QString::fromLatin1("de")));
    m_keyboardLayouts.append( KXKeyboardLayout(QString::fromLatin1("Finish"),
                                               QString::fromLatin1("fi")));
    m_keyboardLayouts.append( KXKeyboardLayout(QString::fromLatin1("Swiss French"),
                                               QString::fromLatin1("sf")));
    m_keyboardLayouts.append( KXKeyboardLayout(QString::fromLatin1("United Kingdom"),
                                               QString::fromLatin1("gb")));
    m_keyboardLayouts.append( KXKeyboardLayout(QString::fromLatin1("Hungarian"),
                                               QString::fromLatin1("hu")));
    m_keyboardLayouts.append( KXKeyboardLayout(QString::fromLatin1("Italian"),
                                               QString::fromLatin1("it")));
    m_keyboardLayouts.append( KXKeyboardLayout(QString::fromLatin1("Japanese"),
                                               QString::fromLatin1("jp")));
    m_keyboardLayouts.append( KXKeyboardLayout(QString::fromLatin1("Norwegian"),
                                               QString::fromLatin1("no")));
    m_keyboardLayouts.append( KXKeyboardLayout(QString::fromLatin1("Polish"),
                                               QString::fromLatin1("pl")));
    m_keyboardLayouts.append( KXKeyboardLayout(QString::fromLatin1("Portuguese"),
                                               QString::fromLatin1("pt")));
    m_keyboardLayouts.append( KXKeyboardLayout(QString::fromLatin1("Russian"),
                                               QString::fromLatin1("ru")));
    m_keyboardLayouts.append( KXKeyboardLayout(QString::fromLatin1("Swedish"),
                                               QString::fromLatin1("se")));
    m_keyboardLayouts.append( KXKeyboardLayout(QString::fromLatin1("Thai"),
                                               QString::fromLatin1("th")));
    m_keyboardLayouts.append( KXKeyboardLayout(QString::fromLatin1("French"),
                                               QString::fromLatin1("fr")));

    // ### hack
    m_currentKeyboards.append( KXKeyboardData( m_keyboardModels[ 2 ],
                                               m_keyboardLayouts[ 0 ] ) );
}

bool KXData::initStatic()
{
    if ( s_global )
        return true;

    s_global = new KXData;
    return s_global->init();
}

bool KXData::recreate()
{
    delete s_global;
    s_global = 0;
    return initStatic();
}

bool KXData::init()
{
    loadPointerData();

    loadKeyboardData();

    if ( !loadMonitorData() )
        return false;

    // ### hack
    m_currentMonitors.append( m_monitors.begin().data()[ 0 ] );

    if ( !loadVideoCardData() )
        return false;

    // ### hack!
    KXVideoCardData defaultCard = m_videoCards.begin().data()[ 0 ];

    // Use Generic/vesa as default, if it is in the list.
    KXVideoCardDataList genericList = m_videoCards[ QString::fromLatin1( "Generic" ) ];
    KXVideoCardDataList::ConstIterator genericIt;
    for( genericIt = genericList.begin(); genericIt != genericList.end(); ++genericIt ) {
        if ( (*genericIt).driver() == "vga" ) {
            defaultCard = *genericIt;
            break;
        }
    }

    m_currentVideoCards.append( defaultCard );

    // ### hack
    KXDisplayData display = KXDisplayData( 8, "640x480", QSize( 0, 0 ) );
    KXDisplayDataList displayList;
    displayList << display;

    m_currentScreens.append( KXScreenData( 8, displayList ) );

    m_currentModes = vesaModes();

    return true;
}

KXData *KXData::clone()
{
    KXData *res = new KXData();

    res->m_pointerModels = m_pointerModels;
    res->m_keyboardModels = m_keyboardModels;
    res->m_keyboardLayouts = m_keyboardLayouts;
    res->m_videoCards = m_videoCards;
    res->m_monitors = m_monitors;
    res->m_generalServerData = m_generalServerData;

    res->m_currentMonitors = m_currentMonitors;
    res->m_currentKeyboards = m_currentKeyboards;
    res->m_currentVideoCards = m_currentVideoCards;
    res->m_currentPointers = m_currentPointers;
    res->m_currentScreens = m_currentScreens;
    res->m_currentModes = m_currentModes;

    return res;
}

void KXData::copyFrom( KXData *other )
{
    m_pointerModels = other->m_pointerModels;
    m_keyboardModels = other->m_keyboardModels;
    m_keyboardLayouts = other->m_keyboardLayouts;
    m_videoCards = other->m_videoCards;
    m_monitors = other->m_monitors;
    m_generalServerData = other->m_generalServerData;

    m_currentMonitors = other->m_currentMonitors;
    m_currentKeyboards = other->m_currentKeyboards;
    m_currentVideoCards = other->m_currentVideoCards;
    m_currentPointers = other->m_currentPointers;
    m_currentScreens = other->m_currentScreens;
    m_currentModes = other->m_currentModes;
}

QString KXData::lookupVendor( uint pciVendorID )
{
    assert( s_global );

    return s_global->m_pciVendorIDMap[ pciVendorID ];
}

KXModeDataList KXData::vesaModes()
{
    if ( s_vesaModes )
        return *s_vesaModes;

    s_vesaModes = new KXModeDataList;

    // the following numbers are from XFree86.
    // $XConsortium: mondata.tcl /main/4 1996/10/28 05:42:19 kaleb $
    // $XFree86: xc/programs/Xserver/hw/xfree86/XF86Setup/mondata.tcl,v 3.8.2.4 1998/02/15 16:08:53 hohndel Exp $
    s_vesaModes->append( parseModeLine( "16.442 640 649 656 663 400 407 415 440" ) );
    s_vesaModes->append( parseModeLine( "25.175 640  664  760  800   400  409  411  450" ) );
    s_vesaModes->append( parseModeLine( "25.175 640  664  760  800   480  491  493  525" ) );
    s_vesaModes->append( parseModeLine( "36     800  824  896 1024   600  601  603  625" ) );
    s_vesaModes->append( parseModeLine( "28     640  664  760  800   480  491  493  525" ) );
    s_vesaModes->append( parseModeLine( "31.5    640  672 736   832   400  401  404  445 -HSync +VSync" ) );
    s_vesaModes->append( parseModeLine( "31.5   640  680  720  864   480  488  491  521" ) );
    s_vesaModes->append( parseModeLine( "31.5   640  656  720  840   480  481  484  500 -HSync -VSync" ) );
    s_vesaModes->append( parseModeLine( "40     800  840  968 1056   600  601  605  628 +HSync +VSync" ) );
    s_vesaModes->append( parseModeLine( "36     640  696  752  832   480  481  484  509 -HSync -VSync" ) );
    s_vesaModes->append( parseModeLine( "50     800  856  976 1040   600  637  643  666 +HSync +VSync" ) );
    s_vesaModes->append( parseModeLine( "65    1024 1032 1176 1344   768  771  777  806 -HSync -VSync" ) );
    s_vesaModes->append( parseModeLine( "45.8   640  672  768  864   480  488  494  530 -HSync -VSync" ) );
    s_vesaModes->append( parseModeLine( "89.9  1152 1216 1472 1680   864  868  876  892 -HSync -VSync" ) );
    s_vesaModes->append( parseModeLine( "60.75   800  864  928 1088   600  616  621  657 -HSync -VSync" ) );
    s_vesaModes->append( parseModeLine( "75    1024 1048 1184 1328   768  771  777  806 -HSync -VSync" ) );
    s_vesaModes->append( parseModeLine( "85    1024 1032 1152 1360   768  784  787  823" ) );
    s_vesaModes->append( parseModeLine( "92    1152 1208 1368 1474   864  865  875  895" ) );
    s_vesaModes->append( parseModeLine( "69.650 800  864  928 1088   600  604  610  640 -HSync -VSync" ) );
    s_vesaModes->append( parseModeLine( "98.9  1024 1056 1216 1408   768 782 788 822 -HSync -VSync" ) );
    s_vesaModes->append( parseModeLine( "110    1152 1240 1324 1552   864  864  876  908" ) );
    s_vesaModes->append( parseModeLine( "135    1152 1464 1592 1776   864  864  876  908" ) );
    s_vesaModes->append( parseModeLine( "110    1280 1328 1512 1712  1024 1025 1028 1054" ) );
    s_vesaModes->append( parseModeLine( "126.5  1280 1312 1472 1696  1024 1032 1040 1068 -HSync -VSync" ) );
    s_vesaModes->append( parseModeLine( "162    1600 1664 1856 2160  1200 1201 1204 1250 +HSync +VSync" ) );
    s_vesaModes->append( parseModeLine( "135    1280 1312 1456 1712  1024 1027 1030 1064" ) );
    s_vesaModes->append( parseModeLine( "115.5  1024 1056 1248 1440  768  771  781  802 -HSync -VSync" ) );
    s_vesaModes->append( parseModeLine( "135    1280 1312 1416 1664  1024 1027 1030 1064" ) );
    s_vesaModes->append( parseModeLine( "189    1600 1664 1856 2160  1200 1201 1204 1250 -HSync -VSync" ) );
    s_vesaModes->append( parseModeLine( "137.65 1152 1184 1312 1536   864  866  885  902 -HSync -VSync" ) );
    s_vesaModes->append( parseModeLine( "157.5  1280 1344 1504 1728  1024 1025 1028 1072 +HSync +VSync" ) );
    s_vesaModes->append( parseModeLine( "202.5  1600 1664 1856 2160  1200 1201 1204 1250 +HSync +VSync" ) );
    s_vesaModes->append( parseModeLine( "220    1600 1616 1808 2080  1200 1204 1207 1244 +HSync +VSync" ) );
    s_vesaModes->append( parseModeLine( "181.75 1280 1312 1440 1696  1024 1031 1046 1072 -HSync -VSync" ) );
    s_vesaModes->append( parseModeLine( "230    1800 1896 2088 2392 1440 1441 1444 1490 +HSync +VSync" ) );
    s_vesaModes->append( parseModeLine( "250    1800 1896 2088 2392 1440 1441 1444 1490 +HSync +VSync" ) );
    s_vesaModes->append( parseModeLine( "12.588 320  336  384  400   200  204  205  225 Doublescan" ) );
    // vossi: Sun standard (correct?)
    s_vesaModes->append( parseModeLine( "80    1152 1208 1368 1474   900  901  911  932" ) );

    return *s_vesaModes;
}

KXModeData KXData::parseModeLine( const QString &line )
{
    QStringList lst = QStringList::split( ' ', line );
    assert( lst.count() >= 9 );

    float dotClock = lst[ 0 ].toDouble();

    KXModeTiming hTimings( lst[ 1 ].toUInt(), lst[ 2 ].toUInt(), lst[ 3 ].toUInt(),
                           lst[ 4 ].toUInt() );

    KXModeTiming vTimings( lst[ 5 ].toUInt(), lst[ 6 ].toUInt(), lst[ 7 ].toUInt(),
                           lst[ 8 ].toUInt() );

    QString flags;

    for ( uint i = 9; i < lst.count(); ++i )
    {
        flags += " ";
        flags += lst[ i ];
    }



    // example: 640x480/60Hz
    QString name = lst[ 1 ] + 'x' + lst[ 5 ]
                   + '/' + QString::number( static_cast<uint>( KXModeData::calcVRefresh( KXModeData::calcHFrequency(
                   dotClock, hTimings.total ), vTimings.total ) + 0.5 ) )
                   + "Hz";

//    kdDebug() << "-- " << name << ": " << line << endl;

    return KXModeData( name, dotClock, hTimings, vTimings, flags );
}

QString KXData::pointerClassToName( KXPointerClass klass )
{
    switch ( klass )
    {
        case KX_PS2: return i18n( "PS/2" );
        case KX_Serial: return i18n( "Serial" );
        case KX_USB: return i18n( "USB" );
        case KX_Bus: return i18n( "Bus" );
        default: assert( false );
    }

    return QString::null;
}
