/****************************************************************************
**
** Implementation of QDesktopWidget class.
**
** Copyright (C) 1992-2008 Trolltech ASA.  All rights reserved.
**
** This file is part of the kernel module of the Qt GUI Toolkit.
**
** This file may be used under the terms of the GNU General
** Public License versions 2.0 or 3.0 as published by the Free
** Software Foundation and appearing in the files LICENSE.GPL2
** and LICENSE.GPL3 included in the packaging of this file.
** Alternatively you may (at your option) use any later version
** of the GNU General Public License if such license has been
** publicly approved by Trolltech ASA (or its successors, if any)
** and the KDE Free Qt Foundation.
**
** Please review the following information to ensure GNU General
** Public Licensing requirements will be met:
** http://trolltech.com/products/qt/licenses/licensing/opensource/.
** If you are unsure which license is appropriate for your use, please
** review the following information:
** http://trolltech.com/products/qt/licenses/licensing/licensingoverview
** or contact the sales department at sales@trolltech.com.
**
** This file may be used under the terms of the Q Public License as
** defined by Trolltech ASA and appearing in the file LICENSE.QPL
** included in the packaging of this file.  Licensees holding valid Qt
** Commercial licenses may use this file in accordance with the Qt
** Commercial License Agreement provided with the Software.
**
** This file is provided "AS IS" with NO WARRANTY OF ANY KIND,
** INCLUDING THE WARRANTIES OF DESIGN, MERCHANTABILITY AND FITNESS FOR
** A PARTICULAR PURPOSE. Trolltech reserves all rights not granted
** herein.
**
**********************************************************************/

#include "qdesktopwidget.h"
#include "qapplication.h"
#include "qobjectlist.h"
#include "qt_x11_p.h"
#include <cstdlib>

// defined in qwidget_x11.cpp
extern int qt_x11_create_desktop_on_screen;

// defined in qapplication_x11.cpp
extern Atom qt_net_workarea;
extern bool qt_net_supports(Atom atom);

// function to update the workarea of the screen
static bool qt_desktopwidget_workarea_dirty = TRUE;
void qt_desktopwidget_update_workarea()
{
    qt_desktopwidget_workarea_dirty = TRUE;
}


class QSingleDesktopWidget : public QWidget
{
public:
    QSingleDesktopWidget();
    ~QSingleDesktopWidget();
};

QSingleDesktopWidget::QSingleDesktopWidget()
    : QWidget( 0, "desktop", WType_Desktop )
{
}

QSingleDesktopWidget::~QSingleDesktopWidget()
{
    while ( children() )
        removeChild( children()->getFirst() );
}


class QDesktopWidgetPrivate
{
public:
    QDesktopWidgetPrivate();
    ~QDesktopWidgetPrivate();

    void init();

    bool use_xinerama;
    int defaultScreen;
    int screenCount;

    QWidget **screens;
    QRect *rects;
    QRect *workareas;
};

QDesktopWidgetPrivate::QDesktopWidgetPrivate()
    : use_xinerama(FALSE), defaultScreen(0), screenCount(1),
      screens( 0 ), rects( 0 ), workareas( 0 )
{
}

QDesktopWidgetPrivate::~QDesktopWidgetPrivate()
{
    if ( screens ) {
	for ( int i = 0; i < screenCount; ++i ) {
	    if (i == defaultScreen) continue;
	    delete screens[i];
	    screens[i] = 0;
	}

	free(screens);
    }

    if ( rects )     delete [] rects;
    if ( workareas ) delete [] workareas;
}

void QDesktopWidgetPrivate::init()
{
    // get the screen count
    int newScreenCount;

#ifndef QT_NO_XINERAMA
    XineramaScreenInfo *xinerama_screeninfo = 0;
    int unused;
    use_xinerama = (XineramaQueryExtension(QPaintDevice::x11AppDisplay(),
					   &unused, &unused) &&
		    XineramaIsActive(QPaintDevice::x11AppDisplay()));

    if (use_xinerama) {
	xinerama_screeninfo =
	    XineramaQueryScreens(QPaintDevice::x11AppDisplay(), &newScreenCount);

    if (xinerama_screeninfo)
	defaultScreen = 0;
    } else
#endif // QT_NO_XINERAMA
    {
	defaultScreen = DefaultScreen(QPaintDevice::x11AppDisplay());
	newScreenCount = ScreenCount(QPaintDevice::x11AppDisplay());
        use_xinerama = false;
    }

    delete [] rects;
    rects     = new QRect[ newScreenCount ];
    delete [] workareas;
    workareas = new QRect[ newScreenCount ];

    // get the geometry of each screen
    int i, j, x, y, w, h;
    for ( i = 0, j = 0; i < newScreenCount; i++ ) {

#ifndef QT_NO_XINERAMA
	if (use_xinerama) {
	    x = xinerama_screeninfo[i].x_org;
	    y = xinerama_screeninfo[i].y_org;
	    w = xinerama_screeninfo[i].width;
	    h = xinerama_screeninfo[i].height;
	} else
#endif // QT_NO_XINERAMA
	    {
		x = 0;
		y = 0;
		w = WidthOfScreen(ScreenOfDisplay(QPaintDevice::x11AppDisplay(), i));
		h = HeightOfScreen(ScreenOfDisplay(QPaintDevice::x11AppDisplay(), i));
	    }

	workareas[i] = QRect();
	rects[j].setRect(x, y, w, h);

        // overlapping?
        if (j > 0 && rects[j-1].intersects(rects[j])) {
            // pick the bigger one, ignore the other
            if ((rects[j].width()*rects[j].height()) >
                (rects[j-1].width()*rects[j-1].height()))
            rects[j-1] = rects[j];
        }
        else
            j++;
    }

    if (screens) {
        // leaks QWidget* pointers on purpose, can't delete them as pointer escapes
        screens = (QWidget**) realloc(screens, j * sizeof(QWidget*));
        if (j > screenCount)
            memset(&screens[screenCount], 0, (j-screenCount) * sizeof(QWidget*));
    }

    screenCount = j;

#ifndef QT_NO_XINERAMA
    if (use_xinerama && screenCount == 1)
        use_xinerama = false;

    if (xinerama_screeninfo)
	XFree(xinerama_screeninfo);
#endif // QT_NO_XINERAMA

}

// the QDesktopWidget itself will be created on the default screen
// as qt_x11_create_desktop_on_screen defaults to -1
QDesktopWidget::QDesktopWidget()
    : QWidget( 0, "desktop", WType_Desktop )
{
    d = new QDesktopWidgetPrivate();

    /*
      we don't call d->init() here, since the initial resize event
      will end up calling init() a second time, which is inefficient.
      instead, for the sending of all posted event to the desktop
      widget (including the initial resize event, which calls
      d->init()).
    */
    QApplication::sendPostedEvents( this, 0 );
}

QDesktopWidget::~QDesktopWidget()
{
    delete d;
}

bool QDesktopWidget::isVirtualDesktop() const
{
    return d->use_xinerama;
}

int QDesktopWidget::primaryScreen() const
{
    return d->defaultScreen;
}

int QDesktopWidget::numScreens() const
{
    return d->screenCount;
}

QWidget *QDesktopWidget::screen( int screen )
{
    if (d->use_xinerama)
	return this;

    if ( screen < 0 || screen >= d->screenCount )
	screen = d->defaultScreen;

    if ( ! d->screens ) {
	d->screens = (QWidget**) calloc( d->screenCount, sizeof(QWidget*));
	d->screens[ d->defaultScreen ] = this;
    }

    if ( ! d->screens[screen] ||               // not created yet
	 ! d->screens[screen]->isDesktop() ) { // reparented away
	qt_x11_create_desktop_on_screen = screen;
	d->screens[screen] = new QSingleDesktopWidget;
	qt_x11_create_desktop_on_screen = -1;
    }

    return d->screens[screen];
}

const QRect& QDesktopWidget::availableGeometry( int screen ) const
{
    if ( qt_desktopwidget_workarea_dirty ) {
	// the workareas are dirty, invalidate them
	for ( int i = 0; i < d->screenCount; ++i )
	    d->workareas[i] = QRect();
	qt_desktopwidget_workarea_dirty = FALSE;
    }

    if ( screen < 0 || screen >= d->screenCount )
	screen = d->defaultScreen;

    if ( d->workareas[screen].isValid() )
	return d->workareas[screen];

    if ( ! isVirtualDesktop() && qt_net_supports( qt_net_workarea ) ) {
	Atom ret;
	int format, e;
	unsigned char *data = 0;
	unsigned long nitems, after;

	e = XGetWindowProperty( QPaintDevice::x11AppDisplay(),
				QPaintDevice::x11AppRootWindow( screen ),
				qt_net_workarea, 0, 4, False, XA_CARDINAL,
				&ret, &format, &nitems, &after, &data );

	if (e == Success && ret == XA_CARDINAL &&
	    format == 32 && nitems == 4) {
	    long *workarea = (long *) data;
	    d->workareas[screen].setRect( workarea[0], workarea[1],
					  workarea[2], workarea[3] );
	} else {
	    d->workareas[screen] = screenGeometry(screen);
	}
	if ( data )
	    XFree( data );
    } else {
	d->workareas[screen] = screenGeometry(screen);
    }

    return d->workareas[screen];
}

const QRect& QDesktopWidget::screenGeometry( int screen ) const
{
    if ( screen < 0 || screen >= d->screenCount )
	screen = d->defaultScreen;

    return d->rects[ screen ];
}

int QDesktopWidget::screenNumber( QWidget *widget ) const
{
    if ( !widget )
	return d->defaultScreen;

#ifndef QT_NO_XINERAMA
    if (d->use_xinerama) {
	// this is how we do it for xinerama
	QRect frame = widget->frameGeometry();
	if ( !widget->isTopLevel() )
	    frame.moveTopLeft( widget->mapToGlobal( QPoint( 0, 0 ) ) );

	int maxSize = -1;
	int maxScreen = -1;

	for ( int i = 0; i < d->screenCount; ++i ) {
	    QRect sect = d->rects[i].intersect( frame );
	    int size = sect.width() * sect.height();
	    if ( size > maxSize && sect.width() > 0 && sect.height() > 0 ) {
		maxSize = size;
		maxScreen = i;
	    }
	}
	return maxScreen;
    }
#endif // QT_NO_XINERAMA

    return widget->x11Screen();
}

int QDesktopWidget::screenNumber( const QPoint &point ) const
{
    for ( int i = 0; i < d->screenCount; ++i ) {
	if ( d->rects[i].contains( point ) )
	    return i;
    }
    return -1;
}

void QDesktopWidget::resizeEvent( QResizeEvent *event )
{
    d->init();
    qt_desktopwidget_workarea_dirty = TRUE;
    QWidget::resizeEvent( event );
}