diff options
author | Lars Knoll <lars.knoll@nokia.com> | 2009-03-23 09:18:55 (GMT) |
---|---|---|
committer | Simon Hausmann <simon.hausmann@nokia.com> | 2009-03-23 09:18:55 (GMT) |
commit | e5fcad302d86d316390c6b0f62759a067313e8a9 (patch) | |
tree | c2afbf6f1066b6ce261f14341cf6d310e5595bc1 /src/gui/embedded/qwindowsystem_qws.cpp | |
download | Qt-e5fcad302d86d316390c6b0f62759a067313e8a9.zip Qt-e5fcad302d86d316390c6b0f62759a067313e8a9.tar.gz Qt-e5fcad302d86d316390c6b0f62759a067313e8a9.tar.bz2 |
Long live Qt 4.5!
Diffstat (limited to 'src/gui/embedded/qwindowsystem_qws.cpp')
-rw-r--r-- | src/gui/embedded/qwindowsystem_qws.cpp | 4947 |
1 files changed, 4947 insertions, 0 deletions
diff --git a/src/gui/embedded/qwindowsystem_qws.cpp b/src/gui/embedded/qwindowsystem_qws.cpp new file mode 100644 index 0000000..dffebf2 --- /dev/null +++ b/src/gui/embedded/qwindowsystem_qws.cpp @@ -0,0 +1,4947 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the QtGui module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain +** additional rights. These rights are described in the Nokia Qt LGPL +** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qwindowsystem_qws.h" +#include "qwsevent_qws.h" +#include "qwscommand_qws_p.h" +#include "qtransportauth_qws_p.h" +#include "qwsutils_qws.h" +#include "qwscursor_qws.h" +#include "qwsdisplay_qws.h" +#include "qmouse_qws.h" +#include "qcopchannel_qws.h" +#include "qwssocket_qws.h" + +#include "qapplication.h" +#include "private/qapplication_p.h" +#include "qsocketnotifier.h" +#include "qpolygon.h" +#include "qimage.h" +#include "qcursor.h" +#include <private/qpaintengine_raster_p.h> +#include "qscreen_qws.h" +#include "qwindowdefs.h" +#include "private/qlock_p.h" +#include "qwslock_p.h" +#include "qfile.h" +#include "qtimer.h" +#include "qpen.h" +#include "qdesktopwidget.h" +#include "qevent.h" +#include "qinputcontext.h" +#include "qpainter.h" + +#include <qdebug.h> + +#include <unistd.h> +#include <stdlib.h> +#include <stdio.h> +#include <errno.h> + +#ifndef QT_NO_QWS_MULTIPROCESS +#include <sys/types.h> +#include <sys/socket.h> +#include <sys/ipc.h> +#include <sys/shm.h> +#ifndef Q_OS_DARWIN +# include <sys/sem.h> +#endif +#include <sys/param.h> +#include <sys/mount.h> +#endif +#include <signal.h> +#include <fcntl.h> + +#if !defined(QT_NO_SOUND) && !defined(Q_OS_DARWIN) +#ifdef QT_USE_OLD_QWS_SOUND +#include <sys/types.h> +#include <sys/stat.h> +#include <sys/ioctl.h> +#include <sys/soundcard.h> +#else +#include "qsoundqss_qws.h" +#endif +#endif + +#include "qkbddriverfactory_qws.h" +#include "qmousedriverfactory_qws.h" + +#include <qbuffer.h> +#include <qdir.h> + +#include <private/qwindowsurface_qws_p.h> +#include <private/qfontengine_qpf_p.h> + +#include "qwindowsystem_p.h" + +//#define QWS_DEBUG_FONTCLEANUP + +QT_BEGIN_NAMESPACE + +QWSServer Q_GUI_EXPORT *qwsServer=0; +static QWSServerPrivate *qwsServerPrivate=0; + +#define MOUSE 0 +#define KEY 1 +//#define EVENT_BLOCK_DEBUG + +QWSScreenSaver::~QWSScreenSaver() +{ +} + +extern QByteArray qws_display_spec; +extern void qt_init_display(); //qapplication_qws.cpp +extern QString qws_qtePipeFilename(); + +extern void qt_client_enqueue(const QWSEvent *); //qapplication_qws.cpp +extern QList<QWSCommand*> *qt_get_server_queue(); + +Q_GLOBAL_STATIC_WITH_ARGS(QString, defaultMouse, (QLatin1String("Auto"))) +Q_GLOBAL_STATIC_WITH_ARGS(QString, defaultKeyboard, (QLatin1String("TTY"))) +static const int FontCleanupInterval = 60 * 1000; + +static int qws_keyModifiers = 0; + +static QWSWindow *keyboardGrabber; +static bool keyboardGrabbing; + +static int get_object_id(int count = 1) +{ + static int next=1000; + int n = next; + next += count; + return n; +} +#ifndef QT_NO_QWS_INPUTMETHODS +static QWSInputMethod *current_IM = 0; + +static QWSWindow *current_IM_composing_win = 0; +static int current_IM_winId = -1; +static bool force_reject_strokeIM = false; +#endif + +static void cleanupFontsDir(); + +//#define QWS_REGION_DEBUG + +/*! + \class QWSScreenSaver + \ingroup qws + + \brief The QWSScreenSaver class is a base class for screensavers + in Qt for Embedded Linux. + + When running \l{Qt for Embedded Linux} applications, it is the server + application that installs and controls the screensaver. + \l{Qt for Embedded Linux} supports multilevel screen saving; i.e., it is possible to + specify several different levels of screen responsiveness. For + example, you can choose to first turn off the light before you + fully activate the screensaver. + + Note that there exists no default screensaver implementation. + + To create a custom screensaver, derive from this class and + reimplement the restore() and save() functions. These functions + are called whenever the screensaver is activated or deactivated, + respectively. Once an instance of your custom screensaver is + created, you can use the QWSServer::setScreenSaver() function to + install it. + + \sa QWSServer, QScreen, {Qt for Embedded Linux} +*/ + +/*! + \fn QWSScreenSaver::~QWSScreenSaver() + + Reimplement this function to destroy the screensaver. +*/ + +/*! + \fn QWSScreenSaver::restore() + + Implement this function to deactivate the screensaver, restoring + the previously saved screen. + + \sa save(), QWSServer::screenSaverActivate() +*/ + +/*! + \fn QWSScreenSaver::save(int level) + + Implement this function to activate the screensaver, saving the + current screen. + + \l{Qt for Embedded Linux} supports multilevel screen saving; i.e., it is + possible to specify several different levels of screen + responsiveness. For example, you can choose to first turn off the + light before you fully activate the screensaver. Use the + QWSServer::setScreenSaverIntervals() to specify the time intervals + between the different levels. + + This function should return true if the screensaver successfully + enters the given \a level; otherwise it should return false. + + \sa restore(), QWSServer::screenSaverActivate() +*/ + +class QWSWindowPrivate +{ +public: + QWSWindowPrivate(); + +#ifdef QT_QWS_CLIENTBLIT + QRegion directPaintRegion; +#endif + QRegion allocatedRegion; +#ifndef QT_NO_QWSEMBEDWIDGET + QList<QWSWindow*> embedded; + QWSWindow *embedder; +#endif + QWSWindow::State state; + Qt::WindowFlags windowFlags; + QRegion dirtyOnScreen; + bool painted; +}; + +QWSWindowPrivate::QWSWindowPrivate() + : +#ifndef QT_NO_QWSEMBEDWIDGET + embedder(0), state(QWSWindow::NoState), +#endif + painted(false) +{ +} + +/*! + \class QWSWindow + \ingroup qws + + \brief The QWSWindow class encapsulates a top-level window in + Qt for Embedded Linux. + + When you run a \l{Qt for Embedded Linux} application, it either runs as a + server or connects to an existing server. As applications add and + remove windows, the server process maintains information about + each window. In \l{Qt for Embedded Linux}, top-level windows are + encapsulated as QWSWindow objects. Note that you should never + construct the QWSWindow class yourself; the current top-level + windows can be retrieved using the QWSServer::clientWindows() + function. + + With a window at hand, you can retrieve its caption, name, opacity + and ID using the caption(), name(), opacity() and winId() + functions, respectively. Use the client() function to retrieve a + pointer to the client that owns the window. + + Use the isVisible() function to find out if the window is + visible. You can find out if the window is completely obscured by + another window or by the bounds of the screen, using the + isFullyObscured() function. The isOpaque() function returns true + if the window has an alpha channel equal to 255. Finally, the + requestedRegion() function returns the region of the display the + window wants to draw on. + + \sa QWSServer, QWSClient, {Qt for Embedded Linux Architecture} +*/ + +/*! + \fn int QWSWindow::winId() const + + Returns the window's ID. + + \sa name(), caption() +*/ + +/*! + \fn const QString &QWSWindow::name() const + + Returns the window's name, which is taken from the \l {QWidget::}{objectName()} + at the time of \l {QWidget::}{show()}. + + \sa caption(), winId() +*/ + +/*! + \fn const QString &QWSWindow::caption() const + + Returns the window's caption. + + \sa name(), winId() +*/ + +/*! + \fn QWSClient* QWSWindow::client() const + + Returns a reference to the QWSClient object that owns this window. + + \sa requestedRegion() +*/ + +/*! + \fn QRegion QWSWindow::requestedRegion() const + + Returns the region that the window has requested to draw onto, + including any window decorations. + + \sa client() +*/ + +/*! + \fn bool QWSWindow::isVisible() const + + Returns true if the window is visible; otherwise returns false. + + \sa isFullyObscured() +*/ + +/*! + \fn bool QWSWindow::isOpaque() const + + Returns true if the window is opaque, i.e., if its alpha channel + equals 255; otherwise returns false. + + \sa opacity() +*/ + +/*! + \fn uint QWSWindow::opacity () const + + Returns the window's alpha channel value. + + \sa isOpaque() +*/ + +/*! + \fn bool QWSWindow::isPartiallyObscured() const + \internal + + Returns true if the window is partially obsured by another window + or by the bounds of the screen; otherwise returns false. +*/ + +/*! + \fn bool QWSWindow::isFullyObscured() const + + Returns true if the window is completely obsured by another window + or by the bounds of the screen; otherwise returns false. + + \sa isVisible() +*/ + +/*! + \fn QWSWindowSurface* QWSWindow::windowSurface() const + \internal +*/ + +QWSWindow::QWSWindow(int i, QWSClient* client) + : id(i), modified(false), + onTop(false), c(client), last_focus_time(0), _opacity(255), + opaque(true), d(new QWSWindowPrivate) +{ + surface = 0; +} + + +/*! + \enum QWSWindow::State + + This enum describes the state of a window. Most of the + transitional states are set just before a call to + QScreen::exposeRegion() and reset immediately afterwards. + + \value NoState Initial state before the window is properly initialized. + \value Hidden The window is not visible. + \value Showing The window is being shown. + \value Visible The window is visible, and not in a transition. + \value Hiding The window is being hidden. + \value Raising The windoe is being raised. + \value Lowering The window is being raised. + \value Moving The window is being moved. + \value ChangingGeometry The window's geometry is being changed. + \value Destroyed The window is destroyed. + + \sa state(), QScreen::exposeRegion() +*/ + +/*! + Returns the current state of the window. + + \since 4.3 +*/ +QWSWindow::State QWSWindow::state() const +{ + return d->state; +} + +/*! + Returns the window flags of the window. This value is only available + after the first paint event. + + \since 4.3 +*/ +Qt::WindowFlags QWSWindow::windowFlags() const +{ + return d->windowFlags; +} + +/*! + Returns the region that has been repainted since the previous + QScreen::exposeRegion(), and needs to be copied to the screen. + \since 4.3 +*/ +QRegion QWSWindow::dirtyOnScreen() const +{ + return d->dirtyOnScreen; +} + +void QWSWindow::createSurface(const QString &key, const QByteArray &data) +{ +#ifndef QT_NO_QWS_MULTIPROCESS + if (surface && !surface->isBuffered()) + c->removeUnbufferedSurface(); +#endif + + delete surface; + surface = qt_screen->createSurface(key); + surface->setPermanentState(data); + +#ifndef QT_NO_QWS_MULTIPROCESS + if (!surface->isBuffered()) + c->addUnbufferedSurface(); +#endif +} + +/*! + \internal + Raises the window above all other windows except "Stay on top" windows. +*/ +void QWSWindow::raise() +{ + qwsServerPrivate->raiseWindow(this); +#ifndef QT_NO_QWSEMBEDWIDGET + const int n = d->embedded.size(); + for (int i = 0; i < n; ++i) + d->embedded.at(i)->raise(); +#endif +} + +/*! + \internal + Lowers the window below other windows. +*/ +void QWSWindow::lower() +{ + qwsServerPrivate->lowerWindow(this); +#ifndef QT_NO_QWSEMBEDWIDGET + const int n = d->embedded.size(); + for (int i = 0; i < n; ++i) + d->embedded.at(i)->lower(); +#endif +} + +/*! + \internal + Shows the window. +*/ +void QWSWindow::show() +{ + operation(QWSWindowOperationEvent::Show); +#ifndef QT_NO_QWSEMBEDWIDGET + const int n = d->embedded.size(); + for (int i = 0; i < n; ++i) + d->embedded.at(i)->show(); +#endif +} + +/*! + \internal + Hides the window. +*/ +void QWSWindow::hide() +{ + operation(QWSWindowOperationEvent::Hide); +#ifndef QT_NO_QWSEMBEDWIDGET + const int n = d->embedded.size(); + for (int i = 0; i < n; ++i) + d->embedded.at(i)->hide(); +#endif +} + +/*! + \internal + Make this the active window (i.e., sets the keyboard focus to this + window). +*/ +void QWSWindow::setActiveWindow() +{ + qwsServerPrivate->setFocus(this, true); +#ifndef QT_NO_QWSEMBEDWIDGET + const int n = d->embedded.size(); + for (int i = 0; i < n; ++i) + d->embedded.at(i)->setActiveWindow(); +#endif +} + +void QWSWindow::setName(const QString &n) +{ + rgnName = n; +} + +/*! + \internal + Sets the window's caption to \a c. +*/ +void QWSWindow::setCaption(const QString &c) +{ + rgnCaption = c; +} + + +static int global_focus_time_counter=100; + +void QWSWindow::focus(bool get) +{ + if (get) + last_focus_time = global_focus_time_counter++; + if (c) { + QWSFocusEvent event; + event.simpleData.window = id; + event.simpleData.get_focus = get; + c->sendEvent(&event); + } +} + +void QWSWindow::operation(QWSWindowOperationEvent::Operation o) +{ + if (!c) + return; + QWSWindowOperationEvent event; + event.simpleData.window = id; + event.simpleData.op = o; + c->sendEvent(&event); +} + +/*! + \internal + Destructor. +*/ +QWSWindow::~QWSWindow() +{ +#ifndef QT_NO_QWS_INPUTMETHODS + if (current_IM_composing_win == this) + current_IM_composing_win = 0; +#endif +#ifndef QT_NO_QWSEMBEDWIDGET + QWSWindow *embedder = d->embedder; + if (embedder) { + embedder->d->embedded.removeAll(this); + d->embedder = 0; + } + while (!d->embedded.isEmpty()) + stopEmbed(d->embedded.first()); +#endif + +#ifndef QT_NO_QWS_MULTIPROCESS + if (surface && !surface->isBuffered()) { + if (c && c->d_func()) // d_func() will be 0 if client is deleted + c->removeUnbufferedSurface(); + } +#endif + + delete surface; + delete d; +} + +/*! + \internal + + Returns the region that the window is allowed to draw onto, + including any window decorations but excluding regions covered by + other windows. + + \sa paintedRegion(), requestedRegion() +*/ +QRegion QWSWindow::allocatedRegion() const +{ + return d->allocatedRegion; +} + +#ifdef QT_QWS_CLIENTBLIT +QRegion QWSWindow::directPaintRegion() const +{ + return d->directPaintRegion; +} + +inline void QWSWindow::setDirectPaintRegion(const QRegion &r) +{ + d->directPaintRegion = r; +} +#endif + +/*! + \internal + + Returns the region that the window is known to have drawn into. + + \sa allocatedRegion(), requestedRegion() +*/ +QRegion QWSWindow::paintedRegion() const +{ + return (d->painted ? d->allocatedRegion : QRegion()); +} + +inline void QWSWindow::setAllocatedRegion(const QRegion ®ion) +{ + d->allocatedRegion = region; +} + +#ifndef QT_NO_QWSEMBEDWIDGET +inline void QWSWindow::startEmbed(QWSWindow *w) +{ + d->embedded.append(w); + w->d->embedder = this; +} + +inline void QWSWindow::stopEmbed(QWSWindow *w) +{ + w->d->embedder = 0; + w->client()->sendEmbedEvent(w->winId(), QWSEmbedEvent::Region, QRegion()); + d->embedded.removeAll(w); +} +#endif // QT_NO_QWSEMBEDWIDGET + +/********************************************************************* + * + * Class: QWSClient + * + *********************************************************************/ + +class QWSClientPrivate : public QObjectPrivate +{ + Q_DECLARE_PUBLIC(QWSClient) + +public: + QWSClientPrivate(); + ~QWSClientPrivate(); + + void setLockId(int id); + void unlockCommunication(); + +private: +#ifndef QT_NO_QWS_MULTIPROCESS + QWSLock *clientLock; + bool shutdown; + int numUnbufferedSurfaces; +#endif + QSet<QByteArray> usedFonts; + friend class QWSServerPrivate; +}; + +QWSClientPrivate::QWSClientPrivate() +{ +#ifndef QT_NO_QWS_MULTIPROCESS + clientLock = 0; + shutdown = false; + numUnbufferedSurfaces = 0; +#endif +} + +QWSClientPrivate::~QWSClientPrivate() +{ +#ifndef QT_NO_QWS_MULTIPROCESS + delete clientLock; +#endif +} + +void QWSClientPrivate::setLockId(int id) +{ +#ifdef QT_NO_QWS_MULTIPROCESS + Q_UNUSED(id); +#else + clientLock = new QWSLock(id); +#endif +} + +void QWSClientPrivate::unlockCommunication() +{ +#ifndef QT_NO_QWS_MULTIPROCESS + if (clientLock) + clientLock->unlock(QWSLock::Communication); +#endif +} + +/*! + \class QWSClient + \ingroup qws + + \brief The QWSClient class encapsulates a client process in Qt for Embedded Linux. + + When you run a \l{Qt for Embedded Linux} application, it either runs as a + server or connects to an existing server. The server and client + processes have different responsibilities: The client process + performs all application specific operations. The server process + is responsible for managing the clients as well as taking care of + the pointer handling, character input, and screen output. In + addition, the server provides functionality to handle input + methods. + + As applications add and remove windows, the server process + maintains information about each window. In \l{Qt for Embedded Linux}, + top-level windows are encapsulated as QWSWindow objects. A list of + the current windows can be retrieved using the + QWSServer::clientWindows() function, and each window can tell + which client that owns it through its QWSWindow::client() + function. + + A QWSClient object has an unique ID that can be retrieved using + its clientId() function. QWSClient also provides the identity() + function which typically returns the name of this client's running + application. + + \sa QWSServer, QWSWindow, {Qt for Embedded Linux Architecture} +*/ + +/*! + \internal +*/ +//always use frame buffer +QWSClient::QWSClient(QObject* parent, QWS_SOCK_BASE* sock, int id) + : QObject(*new QWSClientPrivate, parent), command(0), cid(id) +{ +#ifdef QT_NO_QWS_MULTIPROCESS + Q_UNUSED(sock); + isClosed = false; +#else + csocket = 0; + if (!sock) { + socketDescriptor = -1; + isClosed = false; + } else { + csocket = static_cast<QWSSocket*>(sock); //### + isClosed = false; + + csocket->flush(); + socketDescriptor = csocket->socketDescriptor(); + connect(csocket, SIGNAL(readyRead()), this, SIGNAL(readyRead())); + connect(csocket, SIGNAL(disconnected()), this, SLOT(closeHandler())); + connect(csocket, SIGNAL(error(QAbstractSocket::SocketError)), this, SLOT(errorHandler())); + } +#endif //QT_NO_QWS_MULTIPROCESS +} + +/*! + \internal +*/ +QWSClient::~QWSClient() +{ + qDeleteAll(cursors); + delete command; +#ifndef QT_NO_QWS_MULTIPROCESS + delete csocket; +#endif +} + +#ifndef QT_NO_QWS_MULTIPROCESS +void QWSClient::removeUnbufferedSurface() +{ + Q_D(QWSClient); + --d->numUnbufferedSurfaces; +} + +void QWSClient::addUnbufferedSurface() +{ + Q_D(QWSClient); + ++d->numUnbufferedSurfaces; +} +#endif // QT_NO_QWS_MULTIPROCESS + +/*! + \internal +*/ +void QWSClient::setIdentity(const QString& i) +{ + id = i; +} + +void QWSClient::closeHandler() +{ + isClosed = true; + emit connectionClosed(); +} + +void QWSClient::errorHandler() +{ +#if defined(QWS_SOCKET_DEBUG) + qDebug("Client %p error %s", this, csocket ? csocket->errorString().toLatin1().constData() : "(no socket)"); +#endif + isClosed = true; +//####Do we need to clean out the pipes? + + emit connectionClosed(); +} + +/*! + \internal +*/ +int QWSClient::socket() const +{ + return socketDescriptor; +} + +/*! + \internal +*/ +void QWSClient::sendEvent(QWSEvent* event) +{ +#ifndef QT_NO_QWS_MULTIPROCESS + if (csocket) { + // qDebug() << "QWSClient::sendEvent type " << event->type << " socket state " << csocket->state(); + if ((QAbstractSocket::SocketState)(csocket->state()) == QAbstractSocket::ConnectedState) { + event->write(csocket); + } + } + else +#endif + { + qt_client_enqueue(event); + } +} + +/*! + \internal +*/ +void QWSClient::sendRegionEvent(int winid, QRegion rgn, int type +#ifdef QT_QWS_CLIENTBLIT + , int id +#endif + ) +{ +#ifndef QT_NO_QWS_MULTIPROCESS + Q_D(QWSClient); + if (d->clientLock) + d->clientLock->lock(QWSLock::RegionEvent); +#endif + + QWSRegionEvent event; + event.setData(winid, rgn, type); +#ifdef QT_QWS_CLIENTBLIT + event.simpleData.id = id; +#endif + +// qDebug() << "Sending Region event to" << winid << "rgn" << rgn << "type" << type; + + sendEvent(&event); +} + +extern int qt_servershmid; + +/*! + \internal +*/ +void QWSClient::sendConnectedEvent(const char *display_spec) +{ + QWSConnectedEvent event; + event.simpleData.window = 0; + event.simpleData.len = strlen(display_spec) + 1; + event.simpleData.clientId = cid; + event.simpleData.servershmid = qt_servershmid; + char * tmp=(char *)display_spec; + event.setData(tmp, event.simpleData.len); + sendEvent(&event); +} + +/*! + \internal +*/ +void QWSClient::sendMaxWindowRectEvent(const QRect &rect) +{ + QWSMaxWindowRectEvent event; + event.simpleData.window = 0; + event.simpleData.rect = rect; + sendEvent(&event); +} + +/*! + \internal +*/ +#ifndef QT_NO_QWS_PROPERTIES +void QWSClient::sendPropertyNotifyEvent(int property, int state) +{ + QWSPropertyNotifyEvent event; + event.simpleData.window = 0; // not used yet + event.simpleData.property = property; + event.simpleData.state = state; + sendEvent(&event); +} + +/*! + \internal +*/ +void QWSClient::sendPropertyReplyEvent(int property, int len, const char *data) +{ + QWSPropertyReplyEvent event; + event.simpleData.window = 0; // not used yet + event.simpleData.property = property; + event.simpleData.len = len; + event.setData(data, len); + sendEvent(&event); +} +#endif //QT_NO_QWS_PROPERTIES + +/*! + \internal +*/ +void QWSClient::sendSelectionClearEvent(int windowid) +{ + QWSSelectionClearEvent event; + event.simpleData.window = windowid; + sendEvent(&event); +} + +/*! + \internal +*/ +void QWSClient::sendSelectionRequestEvent(QWSConvertSelectionCommand *cmd, int windowid) +{ + QWSSelectionRequestEvent event; + event.simpleData.window = windowid; + event.simpleData.requestor = cmd->simpleData.requestor; + event.simpleData.property = cmd->simpleData.selection; + event.simpleData.mimeTypes = cmd->simpleData.mimeTypes; + sendEvent(&event); +} + +#ifndef QT_NO_QWSEMBEDWIDGET +/*! + \internal +*/ +void QWSClient::sendEmbedEvent(int windowid, QWSEmbedEvent::Type type, + const QRegion ®ion) +{ + QWSEmbedEvent event; + event.setData(windowid, type, region); + sendEvent(&event); +} +#endif // QT_NO_QWSEMBEDWIDGET + +/*! + \fn void QWSClient::connectionClosed() + \internal +*/ + +/*! + \fn void QWSClient::readyRead(); + \internal +*/ + +/*! + \fn int QWSClient::clientId () const + + Returns an integer uniquely identfying this client. +*/ + +/*! + \fn QString QWSClient::identity () const + + Returns the name of this client's running application. +*/ +/********************************************************************* + * + * Class: QWSServer + * + *********************************************************************/ + +/*! + \class QWSServer + \brief The QWSServer class encapsulates a server process in Qt for Embedded Linux. + + \ingroup qws + + When you run a \l{Qt for Embedded Linux} application, it either runs as a + server or connects to an existing server. The server and client + processes have different responsibilities: The client process + performs all application specific operations. The server process + is responsible for managing the clients as well as taking care of + the pointer handling, character input, and screen output. In + addition, the server provides functionality to handle input + methods. + + In \l{Qt for Embedded Linux}, all system generated events are passed to the + server application which then propagates the event to the + appropriate client. See the \l{Qt for Embedded Linux Architecture} + documentation for details. + + Note that this class is instantiated by QApplication for + \l{Qt for Embedded Linux} server processes; you should never construct this + class yourself. Use the instance() function to retrieve a pointer + to the server object. + + Note that the static functions of the QWSServer class can only be + used in the server process. + + \tableofcontents + + \section1 Client Administration + + As applications add and remove windows, the server process + maintains information about each window. In \l{Qt for Embedded Linux}, + top-level windows are encapsulated as QWSWindow objects. Each + window can tell which client that owns it through its + QWSWindow::client() function. Use the clientWindows() function to + retrieve a list of the current top-level windows. Given a + particular position on the display, the window containing it can + be retrieved using the windowAt() function. + + QWSServer also provides the windowEvent() signal which is emitted + whenever something happens to a top level window; the WindowEvent + enum describes the various types of events that the signal + recognizes. In addition, the server class provides the + markedText() signal which is emitted whenever some text has been + selected in any of the windows, passing the selection as + parameter. + + The QCopChannel class and the QCOP communication protocol enable + transfer of messages between clients. QWSServer provides the + newChannel() and removedChannel() signals that is emitted whenever + a new QCopChannel object is created or destroyed, respectively. + + See also: QWSWindow, QWSClient and QCopChannel. + + + \section1 Mouse Handling + + The mouse driver (represented by an instance of the + QWSMouseHandler class) is loaded by the server application when it + starts running, using Qt's \l {How to Create Qt Plugins}{plugin + system}. A mouse driver receives mouse events from the device and + encapsulates each event with an instance of the QWSEvent class + which it then passes to the server. + + The openMouse() function opens the mouse devices specified by the + QWS_MOUSE_PROTO environment variable, and the setMouseHandler() + functions sets the primary mouse driver. Alternatively, the static + setDefaultMouse() function provides means of specifying the mouse + driver to use if the QWS_MOUSE_PROTO variable is not defined (note + that the default is otherwise platform dependent). The primary + mouse driver can be retrieved using the static mouseHandler() + function. Use the closeMouse() function to delete the mouse + drivers. + + In addition, the QWSServer class can control the flow of mouse + input using the suspendMouse() and resumeMouse() functions. + + See also: QWSMouseHandler and \l{Qt for Embedded Linux Pointer Handling}. + + \section1 Keyboard Handling + + The keyboard driver (represented by an instance of the + QWSKeyboardHandler class) is loaded by the server application when + it starts running, using Qt's \l {How to Create Qt Plugins}{plugin + system}. A keyboard driver receives keyboard events from the + device and encapsulates each event with an instance of the + QWSEvent class which it then passes to the server. + + The openKeyboard() function opens the keyboard devices specified + by the QWS_KEYBOARD environment variable, and the + setKeyboardHandler() functions sets the primary keyboard + driver. Alternatively, the static setDefaultKeyboard() function + provides means of specifying the keyboard driver to use if the + QWS_KEYBOARD variable is not defined (note again that the default + is otherwise platform dependent). The primary keyboard driver can + be retrieved using the static keyboardHandler() function. Use the + closeKeyboard() function to delete the keyboard drivers. + + In addition, the QWSServer class can handle key events from both + physical and virtual keyboards using the processKeyEvent() and + sendKeyEvent() functions, respectively. Use the + addKeyboardFilter() function to filter the key events from + physical keyboard drivers, the most recently added filter can be + removed and deleted using the removeKeyboardFilter() function. + + See also: QWSKeyboardHandler and \l{Qt for Embedded Linux Character Input}. + + \section1 Display Handling + + When a screen update is required, the server runs through all the + top-level windows that intersect with the region that is about to + be updated, and ensures that the associated clients have updated + their memory buffer. Then the server uses the screen driver + (represented by an instance of the QScreen class) to copy the + content of the memory to the screen. + + In addition, the QWSServer class provides some means of managing + the screen output: Use the refresh() function to refresh the + entire display, or alternatively a specified region of it. The + enablePainting() function can be used to disable (and enable) + painting onto the screen. QWSServer also provide the + setMaxWindowRect() function restricting the area of the screen + which \l{Qt for Embedded Linux} applications will consider to be the + maximum area to use for windows. To set the brush used as the + background in the absence of obscuring windows, QWSServer provides + the static setBackground() function. The corresponding + backgroundBrush() function returns the currently set brush. + + QWSServer also controls the screen saver: Use the setScreenSaver() + to install a custom screen saver derived from the QWSScreenSaver + class. Once installed, the screensaver can be activated using the + screenSaverActivate() function, and the screenSaverActive() + function returns its current status. Use the + setScreenSaverInterval() function to specify the timeout interval. + \l{Qt for Embedded Linux} also supports multilevel screen saving, use the + setScreenSaverIntervals() function to specify the various levels + and their timeout intervals. + + Finally, the QWSServer class controls the cursor's appearance, + i.e., use the setCursorVisible() function to hide or show the + cursor, and the isCursorVisible() function to determine whether + the cursor is visible on the display or not. + + See also: QScreen and \l{Qt for Embedded Linux Display Management}. + + \section1 Input Method Handling + + Whenever the server receives an event, it queries its stack of + top-level windows to find the window containing the event's + position (each window can identify the client application that + created it). Then the server forwards the event to the appropriate + client. If an input method is installed, it is used as a filter + between the server and the client application. + + Derive from the QWSInputMethod class to create custom input + methods, and use the server's setCurrentInputMethod() function to + install it. Use the sendIMEvent() and sendIMQuery() functions to + send input method events and queries. + + QWSServer provides the IMMouse enum describing the various mouse + events recognized by the QWSInputMethod::mouseHandler() + function. The latter function allows subclasses of QWSInputMethod + to handle mouse events within the preedit text. + + \sa QWSInputMethod +*/ + +/*! + \enum QWSServer::IMState + \obsolete + + This enum describes the various states of an input method. + + \value IMCompose Composing. + \value IMStart Equivalent to IMCompose. + \value IMEnd Finished composing. + + \sa QWSInputMethod::sendIMEvent() +*/ + +/*! + \enum QWSServer::IMMouse + + This enum describes the various types of mouse events recognized + by the QWSInputMethod::mouseHandler() function. + + \value MousePress An event generated by pressing a mouse button. + \value MouseRelease An event generated by relasing a mouse button. + \value MouseMove An event generated by moving the mouse cursor. + \value MouseOutside This value is only reserved, i.e., it is not used in + current implementations. + + \sa QWSInputMethod, setCurrentInputMethod() +*/ + +/*! + \enum QWSServer::ServerFlags + \internal + + This enum is used to pass various options to the window system + server. + + \value DisableKeyboard Ignore all keyboard input. + \value DisableMouse Ignore all mouse input. +*/ + +/*! + \enum QWSServer::WindowEvent + + This enum specifies the various events that can occur in a + top-level window. + + \value Create A new window has been created (by the QWidget constructor). + \value Destroy The window has been closed and deleted (by the QWidget destructor). + \value Hide The window has been hidden using the QWidget::hide() function. + \value Show The window has been shown using the QWidget::show() function or similar. + \value Raise The window has been raised to the top of the desktop. + \value Lower The window has been lowered. + \value Geometry The window has changed size or position. + \value Active The window has become the active window (i.e., it has keyboard focus). + \value Name The window has been named. + + \sa windowEvent() +*/ + +/*! + \fn void QWSServer::markedText(const QString &selection) + + This signal is emitted whenever some text is selected in any of + the running applications, passing the selected text in the \a + selection parameter. + + \sa windowEvent() +*/ + +/*! + \fn const QList<QWSWindow*> &QWSServer::clientWindows() + + Returns the list of current top-level windows. + + Note that the collection of top-level windows changes as + applications add and remove widgets so it should not be stored for + future use. The windows are sorted in stacking order from top-most + to bottom-most. + + Use the QWSWindow::client() function to retrieve the client + application that owns a given window. + + \sa windowAt(), instance() +*/ + +/*! + \fn void QWSServer::newChannel(const QString& channel) + + This signal is emitted whenever a new QCopChannel object is + created, passing the channel's name in the \a channel parameter. + + \sa removedChannel() +*/ + +/*! + \fn void QWSServer::removedChannel(const QString& channel) + + This signal is emitted immediately after the given the QCopChannel + object specified by \a channel, is destroyed. + + Note that a channel is not destroyed until all its listeners have + been unregistered. + + \sa newChannel() +*/ + +/*! + \fn QWSServer::QWSServer(int flags, QObject *parent) + \internal + + Construct a QWSServer object with the given \a parent. The \a + flags are used for keyboard and mouse settings. + + \warning This class is instantiated by QApplication for + \l{Qt for Embedded Linux} server processes. You should never construct + this class yourself. + + \sa {Running Applications} +*/ + +/*! + \fn static QWSServer* QWSServer::instance() + \since 4.2 + + Returns a pointer to the server instance. + + Note that the pointer will be 0 if the application is not the + server, i.e., if the QApplication::type() function doesn't return + QApplication::GuiServer. + + \sa clientWindows(), windowAt() +*/ + +struct QWSCommandStruct +{ + QWSCommandStruct(QWSCommand *c, QWSClient *cl) :command(c),client(cl){} + ~QWSCommandStruct() { delete command; } + + QWSCommand *command; + QWSClient *client; + +}; + +QWSServer::QWSServer(int flags, QObject *parent) : + QObject(*new QWSServerPrivate, parent) +{ + Q_D(QWSServer); + d->initServer(flags); +} + +#ifdef QT3_SUPPORT +/*! + Use the two-argument overload and call the + QObject::setObjectName() function instead. +*/ +QWSServer::QWSServer(int flags, QObject *parent, const char *name) : + QObject(*new QWSServerPrivate, parent) +{ + Q_D(QWSServer); + setObjectName(QString::fromAscii(name)); + d->initServer(flags); +} +#endif + + +#ifndef QT_NO_QWS_MULTIPROCESS +static void ignoreSignal(int) {} // Used to eat SIGPIPE signals below +#endif + +bool QWSServerPrivate::screensaverblockevent( int index, int *screensaverinterval, bool isDown ) +{ + static bool ignoreEvents[2] = { false, false }; + if ( isDown ) { + if ( !ignoreEvents[index] ) { + bool wake = false; + if ( screensaverintervals ) { + if ( screensaverinterval != screensaverintervals ) { + wake = true; + } + } + if ( screensaverblockevents && wake ) { +#ifdef EVENT_BLOCK_DEBUG + qDebug( "waking the screen" ); +#endif + ignoreEvents[index] = true; + } else if ( !screensaverblockevents ) { +#ifdef EVENT_BLOCK_DEBUG + qDebug( "the screen was already awake" ); +#endif + ignoreEvents[index] = false; + } + } + } else { + if ( ignoreEvents[index] ) { +#ifdef EVENT_BLOCK_DEBUG + qDebug( "mouseup?" ); +#endif + ignoreEvents[index] = false; + return true; + } + } + return ignoreEvents[index]; +} + +void QWSServerPrivate::initServer(int flags) +{ + Q_Q(QWSServer); + Q_ASSERT(!qwsServer); + qwsServer = q; + qwsServerPrivate = this; + disablePainting = false; +#ifndef QT_NO_QWS_MULTIPROCESS + ssocket = new QWSServerSocket(qws_qtePipeFilename(), q); + QObject::connect(ssocket, SIGNAL(newConnection()), q, SLOT(_q_newConnection())); + + if ( !ssocket->isListening()) { + perror("QWSServerPrivate::initServer: server socket not listening"); + qFatal("Failed to bind to %s", qws_qtePipeFilename().toLatin1().constData()); + } + + struct linger tmp; + tmp.l_onoff=1; + tmp.l_linger=0; + setsockopt(ssocket->socketDescriptor(),SOL_SOCKET,SO_LINGER,(char *)&tmp,sizeof(tmp)); + + + signal(SIGPIPE, ignoreSignal); //we get it when we read +#endif + focusw = 0; + mouseGrabber = 0; + mouseGrabbing = false; + inputMethodMouseGrabbed = false; + keyboardGrabber = 0; + keyboardGrabbing = false; +#ifndef QT_NO_QWS_CURSOR + haveviscurs = false; + cursor = 0; + nextCursor = 0; +#endif + +#ifndef QT_NO_QWS_MULTIPROCESS + + if (!geteuid()) { +#if !defined(Q_OS_FREEBSD) && !defined(Q_OS_SOLARIS) && !defined(Q_OS_DARWIN) && !defined(QT_LINUXBASE) + if(mount(0,"/var/shm", "shm", 0, 0)) { + /* This just confuses people with 2.2 kernels + if (errno != EBUSY) + qDebug("Failed mounting shm fs on /var/shm: %s",strerror(errno)); + */ + } +#endif + } +#endif + + // no selection yet + selectionOwner.windowid = -1; + selectionOwner.time.set(-1, -1, -1, -1); + + cleanupFontsDir(); + + // initialize the font database + // from qfontdatabase_qws.cpp + extern void qt_qws_init_fontdb(); + qt_qws_init_fontdb(); + + openDisplay(); + + screensavertimer = new QTimer(q); + screensavertimer->setSingleShot(true); + QObject::connect(screensavertimer, SIGNAL(timeout()), q, SLOT(_q_screenSaverTimeout())); + _q_screenSaverWake(); + + clientMap[-1] = new QWSClient(q, 0, 0); + + if (!bgBrush) + bgBrush = new QBrush(QColor(0x20, 0xb0, 0x50)); + + initializeCursor(); + + // input devices + if (!(flags&QWSServer::DisableMouse)) { + q->openMouse(); + } +#ifndef QT_NO_QWS_KEYBOARD + if (!(flags&QWSServer::DisableKeyboard)) { + q->openKeyboard(); + } +#endif + +#if !defined(QT_NO_SOUND) && !defined(QT_EXTERNAL_SOUND_SERVER) && !defined(Q_OS_DARWIN) + soundserver = new QWSSoundServer(q); +#endif +} + +/*! + \internal + Destructs this server. +*/ +QWSServer::~QWSServer() +{ + closeMouse(); +#ifndef QT_NO_QWS_KEYBOARD + closeKeyboard(); +#endif + d_func()->cleanupFonts(/*force =*/true); +} + +/*! + \internal + */ +void QWSServer::timerEvent(QTimerEvent *e) +{ + Q_D(QWSServer); + if (e->timerId() == d->fontCleanupTimer.timerId()) { + d->cleanupFonts(); + d->fontCleanupTimer.stop(); + } else { + QObject::timerEvent(e); + } +} + +const QList<QWSWindow*> &QWSServer::clientWindows() +{ + Q_D(QWSServer); + return d->windows; +} + +/*! + \internal +*/ +void QWSServerPrivate::releaseMouse(QWSWindow* w) +{ + if (w && mouseGrabber == w) { + mouseGrabber = 0; + mouseGrabbing = false; +#ifndef QT_NO_QWS_CURSOR + if (nextCursor) { + // Not grabbing -> set the correct cursor + setCursor(nextCursor); + nextCursor = 0; + } +#endif + } +} + +/*! + \internal +*/ +void QWSServerPrivate::releaseKeyboard(QWSWindow* w) +{ + if (keyboardGrabber == w) { + keyboardGrabber = 0; + keyboardGrabbing = false; + } +} + +void QWSServerPrivate::handleWindowClose(QWSWindow *w) +{ + w->shuttingDown(); + if (focusw == w) + setFocus(w,false); + if (mouseGrabber == w) + releaseMouse(w); + if (keyboardGrabber == w) + releaseKeyboard(w); +} + + +#ifndef QT_NO_QWS_MULTIPROCESS +/*! + \internal +*/ +void QWSServerPrivate::_q_newConnection() +{ + Q_Q(QWSServer); + while (QWS_SOCK_BASE *sock = ssocket->nextPendingConnection()) { + int socket = sock->socketDescriptor(); + sock->setParent(0); + + QWSClient *client = new QWSClient(q,sock, get_object_id()); + clientMap[socket] = client; + +#ifndef QT_NO_SXE +#ifdef QTRANSPORTAUTH_DEBUG + qDebug( "Transport auth connected: unix stream socket %d", socket ); +#endif + // get a handle to the per-process authentication service + QTransportAuth *a = QTransportAuth::getInstance(); + + // assert that this transport is trusted + QTransportAuth::Data *d = a->connectTransport( + QTransportAuth::UnixStreamSock | + QTransportAuth::Trusted, socket ); + + QAuthDevice *ad = a->recvBuf( d, sock ); + ad->setClient(client); + + QObject::connect(ad, SIGNAL(readyRead()), + q, SLOT(_q_doClient())); + + QObject::connect(client, SIGNAL(connectionClosed()), + q, SLOT(_q_clientClosed())); +#else + QObject::connect(client, SIGNAL(readyRead()), + q, SLOT(_q_doClient())); + QObject::connect(client, SIGNAL(connectionClosed()), + q, SLOT(_q_clientClosed())); +#endif // QT_NO_SXE + + client->sendConnectedEvent(qws_display_spec.constData()); + + if (clientMap.contains(socket)) { + QList<QScreen*> screens = qt_screen->subScreens(); + if (screens.isEmpty()) + screens.append(qt_screen); + for (int i = 0; i < screens.size(); ++i) { + const QApplicationPrivate *ap = QApplicationPrivate::instance(); + QScreen *screen = screens.at(i); + const QRect rect = ap->maxWindowRect(screen); + if (!rect.isEmpty()) + client->sendMaxWindowRectEvent(rect); + if (screen->isTransformed()) { + QWSScreenTransformationEvent event; + event.simpleData.screen = i; + event.simpleData.transformation = screen->transformOrientation(); + client->sendEvent(&event); + } + } + } + + // pre-provide some object id's + QWSCreateCommand cmd(30); + invokeCreate(&cmd, client); + } +} +/*! + \internal +*/ +void QWSServerPrivate::_q_clientClosed() +{ + Q_Q(QWSServer); + QWSClient* cl = (QWSClient*)q->sender(); + + // Remove any queued commands for this client + int i = 0; + while (i < commandQueue.size()) { + QWSCommandStruct *cs = commandQueue.at(i); + if (cs->client == cl) { + commandQueue.removeAt(i); + delete cs; + } else { + ++i; + } + } + +#ifndef QT_NO_COP + // Enfore unsubscription from all channels. + QCopChannel::detach(cl); +#endif + + // Shut down all windows for this client + for (int i = 0; i < windows.size(); ++i) { + QWSWindow* w = windows.at(i); + if (w->forClient(cl)) + w->shuttingDown(); + } + + // Delete all windows for this client + QRegion exposed; + i = 0; + while (i < windows.size()) { + QWSWindow* w = windows.at(i); + if (w->forClient(cl)) { + windows.takeAt(i); + w->c = 0; //so we don't send events to it anymore + releaseMouse(w); + releaseKeyboard(w); + exposed += w->allocatedRegion(); +// rgnMan->remove(w->allocationIndex()); + if (focusw == w) + setFocus(focusw,0); + if (mouseGrabber == w) + releaseMouse(w); + if (i < nReserved) + --nReserved; +#ifndef QT_NO_QWS_PROPERTIES + propertyManager.removeProperties(w->winId()); +#endif + emit q->windowEvent(w, QWSServer::Destroy); + w->d->state = QWSWindow::Destroyed; //??? + deletedWindows.append(w); + } else { + ++i; + } + } + if (deletedWindows.count()) + QTimer::singleShot(0, q, SLOT(_q_deleteWindowsLater())); + + QWSClientPrivate *clientPrivate = cl->d_func(); + if (!clientPrivate->shutdown) { +#if defined(QWS_DEBUG_FONTCLEANUP) + qDebug() << "client" << cl->clientId() << "crashed"; +#endif + // this would be the place to emit a signal to notify about the + // crash of a client + crashedClientIds.append(cl->clientId()); + fontCleanupTimer.start(10, q_func()); + } + clientPrivate->shutdown = true; + + while (!clientPrivate->usedFonts.isEmpty()) { + const QByteArray font = *clientPrivate->usedFonts.begin(); +#if defined(QWS_DEBUG_FONTCLEANUP) + qDebug() << "dereferencing font" << font << "from disconnected client"; +#endif + dereferenceFont(clientPrivate, font); + } + clientPrivate->usedFonts.clear(); + + //qDebug("removing client %d with socket %d", cl->clientId(), cl->socket()); + clientMap.remove(cl->socket()); + if (cl == cursorClient) + cursorClient = 0; + if (qt_screen->clearCacheFunc) + (qt_screen->clearCacheFunc)(qt_screen, cl->clientId()); // remove any remaining cache entries. + cl->deleteLater(); + + update_regions(); + exposeRegion(exposed); +} + +void QWSServerPrivate::_q_deleteWindowsLater() +{ + qDeleteAll(deletedWindows); + deletedWindows.clear(); +} + +#endif //QT_NO_QWS_MULTIPROCESS + +void QWSServerPrivate::referenceFont(QWSClientPrivate *client, const QByteArray &font) +{ + if (!client->usedFonts.contains(font)) { + client->usedFonts.insert(font); + + ++fontReferenceCount[font]; +#if defined(QWS_DEBUG_FONTCLEANUP) + qDebug() << "Client" << client->q_func()->clientId() << "added font" << font; + qDebug() << "Refcount is" << fontReferenceCount[font]; +#endif + } +} + +void QWSServerPrivate::dereferenceFont(QWSClientPrivate *client, const QByteArray &font) +{ + if (client->usedFonts.contains(font)) { + client->usedFonts.remove(font); + + Q_ASSERT(fontReferenceCount[font]); + if (!--fontReferenceCount[font] && !fontCleanupTimer.isActive()) + fontCleanupTimer.start(FontCleanupInterval, q_func()); + +#if defined(QWS_DEBUG_FONTCLEANUP) + qDebug() << "Client" << client->q_func()->clientId() << "removed font" << font; + qDebug() << "Refcount is" << fontReferenceCount[font]; +#endif + } +} + +static void cleanupFontsDir() +{ + static bool dontDelete = !qgetenv("QWS_KEEP_FONTS").isEmpty(); + if (dontDelete) + return; + + extern QString qws_fontCacheDir(); + QDir dir(qws_fontCacheDir(), QLatin1String("*.qsf")); + for (uint i = 0; i < dir.count(); ++i) { +#if defined(QWS_DEBUG_FONTCLEANUP) + qDebug() << "removing stale font file" << dir[i]; +#endif + dir.remove(dir[i]); + } +} + +void QWSServerPrivate::cleanupFonts(bool force) +{ + static bool dontDelete = !qgetenv("QWS_KEEP_FONTS").isEmpty(); + if (dontDelete) + return; + +#if defined(QWS_DEBUG_FONTCLEANUP) + qDebug() << "cleanupFonts()"; +#endif + QMap<QByteArray, int>::Iterator it = fontReferenceCount.begin(); + while (it != fontReferenceCount.end()) { + if (it.value() && !force) { + ++it; + continue; + } + + const QByteArray &fontName = it.key(); +#if defined(QWS_DEBUG_FONTCLEANUP) + qDebug() << "removing unused font file" << fontName; +#endif + QFile::remove(QFile::decodeName(fontName)); + sendFontRemovedEvent(fontName); + + it = fontReferenceCount.erase(it); + } + + if (crashedClientIds.isEmpty()) + return; + + QList<QByteArray> removedFonts; +#if !defined(QT_NO_QWS_QPF2) && !defined(QT_FONTS_ARE_RESOURCES) + removedFonts = QFontEngineQPF::cleanUpAfterClientCrash(crashedClientIds); +#endif + crashedClientIds.clear(); + + for (int i = 0; i < removedFonts.count(); ++i) + sendFontRemovedEvent(removedFonts.at(i)); +} + +void QWSServerPrivate::sendFontRemovedEvent(const QByteArray &font) +{ + QWSFontEvent event; + event.simpleData.type = QWSFontEvent::FontRemoved; + event.setData(font.constData(), font.length(), false); + + QMap<int,QWSClient*>::const_iterator it = clientMap.constBegin(); + for (; it != clientMap.constEnd(); ++it) + (*it)->sendEvent(&event); +} + +/*! + \internal +*/ +QWSCommand* QWSClient::readMoreCommand() +{ +#ifndef QT_NO_QWS_MULTIPROCESS + QIODevice *socket = 0; +#endif +#ifndef QT_NO_SXE + if (socketDescriptor != -1) // not server socket + socket = QTransportAuth::getInstance()->passThroughByClient( this ); +#if QTRANSPORTAUTH_DEBUG + if (socket) { + char displaybuf[1024]; + qint64 bytes = socket->bytesAvailable(); + if ( bytes > 511 ) bytes = 511; + hexstring( displaybuf, ((unsigned char *)(reinterpret_cast<QAuthDevice*>(socket)->buffer().constData())), bytes ); + qDebug( "readMoreCommand: %lli bytes - %s", socket->bytesAvailable(), displaybuf ); + } +#endif +#endif // QT_NO_SXE + +#ifndef QT_NO_QWS_MULTIPROCESS + if (!socket) + socket = csocket; // server socket + if (socket) { + // read next command + if (!command) { + int command_type = qws_read_uint(socket); + + if (command_type >= 0) + command = QWSCommand::factory(command_type); + } + if (command) { + if (command->read(socket)) { + // Finished reading a whole command. + QWSCommand* result = command; + command = 0; + return result; + } + } + + // Not finished reading a whole command. + return 0; + } else +#endif // QT_NO_QWS_MULTIPROCESS + { + QList<QWSCommand*> *serverQueue = qt_get_server_queue(); + return serverQueue->isEmpty() ? 0 : serverQueue->takeFirst(); + } +} + + +/*! + \internal +*/ +void QWSServer::processEventQueue() +{ + if (qwsServerPrivate) + qwsServerPrivate->doClient(qwsServerPrivate->clientMap.value(-1)); +} + + +#ifndef QT_NO_QWS_MULTIPROCESS +void QWSServerPrivate::_q_doClient() +{ + Q_Q(QWSServer); + + QWSClient* client; +#ifndef QT_NO_SXE + QAuthDevice *ad = qobject_cast<QAuthDevice*>(q->sender()); + if (ad) + client = (QWSClient*)ad->client(); + else +#endif + client = (QWSClient*)q->sender(); + + if (doClientIsActive) { + pendingDoClients.append(client); + return; + } + doClientIsActive = true; + + doClient(client); + + while (!pendingDoClients.isEmpty()) { + doClient(pendingDoClients.takeFirst()); + } + + doClientIsActive = false; +} +#endif // QT_NO_QWS_MULTIPROCESS + +void QWSServerPrivate::doClient(QWSClient *client) +{ + QWSCommand* command=client->readMoreCommand(); + + while (command) { + QWSCommandStruct *cs = new QWSCommandStruct(command, client); + commandQueue.append(cs); + // Try for some more... + command=client->readMoreCommand(); + } + + while (!commandQueue.isEmpty()) { + QWSCommandStruct *cs = commandQueue.takeAt(0); + switch (cs->command->type) { + case QWSCommand::Identify: + invokeIdentify((QWSIdentifyCommand*)cs->command, cs->client); + break; + case QWSCommand::Create: + invokeCreate((QWSCreateCommand*)cs->command, cs->client); + break; +#ifndef QT_NO_QWS_MULTIPROCESS + case QWSCommand::Shutdown: + cs->client->d_func()->shutdown = true; + break; +#endif + case QWSCommand::RegionName: + invokeRegionName((QWSRegionNameCommand*)cs->command, cs->client); + break; + case QWSCommand::Region: + invokeRegion((QWSRegionCommand*)cs->command, cs->client); + cs->client->d_func()->unlockCommunication(); + break; + case QWSCommand::RegionMove: + invokeRegionMove((QWSRegionMoveCommand*)cs->command, cs->client); + cs->client->d_func()->unlockCommunication(); + break; + case QWSCommand::RegionDestroy: + invokeRegionDestroy((QWSRegionDestroyCommand*)cs->command, cs->client); + break; +#ifndef QT_NO_QWS_PROPERTIES + case QWSCommand::AddProperty: + invokeAddProperty((QWSAddPropertyCommand*)cs->command); + break; + case QWSCommand::SetProperty: + invokeSetProperty((QWSSetPropertyCommand*)cs->command); + break; + case QWSCommand::RemoveProperty: + invokeRemoveProperty((QWSRemovePropertyCommand*)cs->command); + break; + case QWSCommand::GetProperty: + invokeGetProperty((QWSGetPropertyCommand*)cs->command, cs->client); + break; +#endif + case QWSCommand::SetSelectionOwner: + invokeSetSelectionOwner((QWSSetSelectionOwnerCommand*)cs->command); + break; + case QWSCommand::RequestFocus: + invokeSetFocus((QWSRequestFocusCommand*)cs->command, cs->client); + break; + case QWSCommand::ChangeAltitude: + invokeSetAltitude((QWSChangeAltitudeCommand*)cs->command, + cs->client); + cs->client->d_func()->unlockCommunication(); + break; + case QWSCommand::SetOpacity: + invokeSetOpacity((QWSSetOpacityCommand*)cs->command, + cs->client); + break; + +#ifndef QT_NO_QWS_CURSOR + case QWSCommand::DefineCursor: + invokeDefineCursor((QWSDefineCursorCommand*)cs->command, cs->client); + break; + case QWSCommand::SelectCursor: + invokeSelectCursor((QWSSelectCursorCommand*)cs->command, cs->client); + break; + case QWSCommand::PositionCursor: + invokePositionCursor((QWSPositionCursorCommand*)cs->command, cs->client); + break; +#endif + case QWSCommand::GrabMouse: + invokeGrabMouse((QWSGrabMouseCommand*)cs->command, cs->client); + break; + case QWSCommand::GrabKeyboard: + invokeGrabKeyboard((QWSGrabKeyboardCommand*)cs->command, cs->client); + break; +#if !defined(QT_NO_SOUND) && !defined(Q_OS_DARWIN) + case QWSCommand::PlaySound: + invokePlaySound((QWSPlaySoundCommand*)cs->command, cs->client); + break; +#endif +#ifndef QT_NO_COP + case QWSCommand::QCopRegisterChannel: + invokeRegisterChannel((QWSQCopRegisterChannelCommand*)cs->command, + cs->client); + break; + case QWSCommand::QCopSend: + invokeQCopSend((QWSQCopSendCommand*)cs->command, cs->client); + break; +#endif +#ifndef QT_NO_QWS_INPUTMETHODS + case QWSCommand::IMUpdate: + invokeIMUpdate((QWSIMUpdateCommand*)cs->command, cs->client); + break; + case QWSCommand::IMResponse: + invokeIMResponse((QWSIMResponseCommand*)cs->command, cs->client); + break; + case QWSCommand::IMMouse: + { + if (current_IM) { + QWSIMMouseCommand *cmd = (QWSIMMouseCommand *) cs->command; + current_IM->mouseHandler(cmd->simpleData.index, + cmd->simpleData.state); + } + } + break; +#endif + case QWSCommand::Font: + invokeFont((QWSFontCommand *)cs->command, cs->client); + break; + case QWSCommand::RepaintRegion: + invokeRepaintRegion((QWSRepaintRegionCommand*)cs->command, + cs->client); + cs->client->d_func()->unlockCommunication(); + break; +#ifndef QT_NO_QWSEMBEDWIDGET + case QWSCommand::Embed: + invokeEmbed(static_cast<QWSEmbedCommand*>(cs->command), + cs->client); + break; +#endif + case QWSCommand::ScreenTransform: + invokeScreenTransform(static_cast<QWSScreenTransformCommand*>(cs->command), + cs->client); + break; + } + delete cs; + } +} + + +void QWSServerPrivate::showCursor() +{ +#ifndef QT_NO_QWS_CURSOR + if (qt_screencursor) + qt_screencursor->show(); +#endif +} + +void QWSServerPrivate::hideCursor() +{ +#ifndef QT_NO_QWS_CURSOR + if (qt_screencursor) + qt_screencursor->hide(); +#endif +} + +/*! + \fn void QWSServer::enablePainting(bool enable) + + Enables painting onto the screen if \a enable is true; otherwise + painting is disabled. + + \sa {Qt for Embedded Linux Architecture#Drawing on Screen}{Qt for Embedded Linux + Architecture} +*/ +void QWSServer::enablePainting(bool enable) +{ + Q_D(QWSServer); + + if (d->disablePainting == !enable) + return; + + d->disablePainting = !enable; + + if (enable) { + // Reset the server side allocated regions to ensure update_regions() + // will send out region events. + for (int i = 0; i < d->windows.size(); ++i) { + QWSWindow *w = d->windows.at(i); + w->setAllocatedRegion(QRegion()); +#ifdef QT_QWS_CLIENTBLIT + w->setDirectPaintRegion(QRegion()); +#endif + } + d->update_regions(); + d->showCursor(); + } else { + // Disable painting by clients by taking away their allocated region. + // To ensure mouse events are still delivered to the correct windows, + // the allocated regions are not modified on the server. + for (int i = 0; i < d->windows.size(); ++i) { + QWSWindow *w = d->windows.at(i); + w->client()->sendRegionEvent(w->winId(), QRegion(), + QWSRegionEvent::Allocation); +#ifdef QT_QWS_CLIENTBLIT + w->client()->sendRegionEvent(w->winId(), QRegion(), + QWSRegionEvent::DirectPaint); +#endif + } + d->hideCursor(); + } +} + +/*! + Refreshes the display by making the screen driver update the + entire display. + + \sa QScreen::exposeRegion() +*/ +void QWSServer::refresh() +{ + Q_D(QWSServer); + d->exposeRegion(QScreen::instance()->region()); +//### send repaint to non-buffered windows +} + +/*! + \fn void QWSServer::refresh(QRegion & region) + \overload + + Refreshes the given \a region of the display. +*/ +void QWSServer::refresh(QRegion & r) +{ + Q_D(QWSServer); + d->exposeRegion(r); +//### send repaint to non-buffered windows +} + +/*! + \fn void QWSServer::setMaxWindowRect(const QRect& rectangle) + + Sets the maximum area of the screen that \l{Qt for Embedded Linux} + applications can use, to be the given \a rectangle. + + Note that this function can only be used in the server process. + + \sa QWidget::showMaximized() +*/ +void QWSServer::setMaxWindowRect(const QRect &rect) +{ + QList<QScreen*> subScreens = qt_screen->subScreens(); + if (subScreens.isEmpty() && qt_screen != 0) + subScreens.append(qt_screen); + + for (int i = 0; i < subScreens.size(); ++i) { + const QScreen *screen = subScreens.at(i); + const QRect r = (screen->region() & rect).boundingRect(); + if (r.isEmpty()) + continue; + + QApplicationPrivate *ap = QApplicationPrivate::instance(); + if (ap->maxWindowRect(screen) != r) { + ap->setMaxWindowRect(screen, i, r); + qwsServerPrivate->sendMaxWindowRectEvents(r); + } + } +} + +/*! + \internal +*/ +void QWSServerPrivate::sendMaxWindowRectEvents(const QRect &rect) +{ + QMap<int,QWSClient*>::const_iterator it = clientMap.constBegin(); + for (; it != clientMap.constEnd(); ++it) + (*it)->sendMaxWindowRectEvent(rect); +} + +/*! + \fn void QWSServer::setDefaultMouse(const char *mouseDriver) + + Sets the mouse driver that will be used if the QWS_MOUSE_PROTO + environment variable is not defined, to be the given \a + mouseDriver. + + Note that the default is platform-dependent. This function can + only be used in the server process. + + + \sa setMouseHandler(), {Qt for Embedded Linux Pointer Handling} +*/ +void QWSServer::setDefaultMouse(const char *m) +{ + *defaultMouse() = QString::fromAscii(m); +} + +/*! + \fn void QWSServer::setDefaultKeyboard(const char *keyboardDriver) + + Sets the keyboard driver that will be used if the QWS_KEYBOARD + environment variable is not defined, to be the given \a + keyboardDriver. + + Note that the default is platform-dependent. This function can + only be used in the server process. + + \sa setKeyboardHandler(), {Qt for Embedded Linux Character Input} +*/ +void QWSServer::setDefaultKeyboard(const char *k) +{ + *defaultKeyboard() = QString::fromAscii(k); +} + +#ifndef QT_NO_QWS_CURSOR +static bool prevWin; +#endif + + +extern int *qt_last_x,*qt_last_y; + + +/*! + \internal + + Send a mouse event. \a pos is the screen position where the mouse + event occurred and \a state is a mask indicating which buttons are + pressed. + + \a pos is in device coordinates +*/ +void QWSServer::sendMouseEvent(const QPoint& pos, int state, int wheel) +{ + bool block = qwsServerPrivate->screensaverblockevent(MOUSE, qwsServerPrivate->screensaverinterval, state); +#ifdef EVENT_BLOCK_DEBUG + qDebug() << "sendMouseEvent" << pos.x() << pos.y() << state << (block?"block":"pass"); +#endif + + if (state || wheel) + qwsServerPrivate->_q_screenSaverWake(); + + if ( block ) + return; + + QPoint tpos; + // transformations + if (qt_screen->isTransformed()) { + QSize s = QSize(qt_screen->deviceWidth(), qt_screen->deviceHeight()); + tpos = qt_screen->mapFromDevice(pos, s); + } else { + tpos = pos; + } + + if (qt_last_x) { + *qt_last_x = tpos.x(); + *qt_last_y = tpos.y(); + } + QWSServer::mousePosition = tpos; + qwsServerPrivate->mouseState = state; + +#ifndef QT_NO_QWS_INPUTMETHODS + const int btnMask = Qt::LeftButton | Qt::RightButton | Qt::MidButton; + int stroke_count; // number of strokes to keep shown. + if (force_reject_strokeIM || !current_IM) + { + stroke_count = 0; + } else { + stroke_count = current_IM->filter(tpos, state, wheel); + } + + if (stroke_count == 0) { + if (state&btnMask) + force_reject_strokeIM = true; + QWSServerPrivate::sendMouseEventUnfiltered(tpos, state, wheel); + } + // stop force reject after stroke ends. + if (state&btnMask && force_reject_strokeIM) + force_reject_strokeIM = false; + // on end of stroke, force_rejct + // and once a stroke is rejected, do not try again till pen is lifted +#else + QWSServerPrivate::sendMouseEventUnfiltered(tpos, state, wheel); +#endif // end QT_NO_QWS_FSIM +} + +void QWSServerPrivate::sendMouseEventUnfiltered(const QPoint &pos, int state, int wheel) +{ + const int btnMask = Qt::LeftButton | Qt::RightButton | Qt::MidButton; + QWSMouseEvent event; + + QWSWindow *win = qwsServer->windowAt(pos); + + QWSClient *serverClient = qwsServerPrivate->clientMap.value(-1); + QWSClient *winClient = win ? win->client() : 0; + + + bool imMouse = false; +#ifndef QT_NO_QWS_INPUTMETHODS + // check for input method window + if (current_IM && current_IM_winId != -1) { + QWSWindow *kbw = keyboardGrabber ? keyboardGrabber : + qwsServerPrivate->focusw; + + imMouse = kbw == win; + if ( !imMouse ) { + QWidget *target = winClient == serverClient ? + QApplication::widgetAt(pos) : 0; + imMouse = target && (target->testAttribute(Qt::WA_InputMethodTransparent)); + } + } +#endif + + //If grabbing window disappears, grab is still active until + //after mouse release. + if ( qwsServerPrivate->mouseGrabber && (!imMouse || qwsServerPrivate->inputMethodMouseGrabbed)) { + win = qwsServerPrivate->mouseGrabber; + winClient = win ? win->client() : 0; + } + event.simpleData.window = win ? win->id : 0; + +#ifndef QT_NO_QWS_CURSOR + if (qt_screencursor) + qt_screencursor->move(pos.x(),pos.y()); + + // Arrow cursor over desktop + // prevWin remembers if the last event was over a window + if (!win && prevWin) { + if (!qwsServerPrivate->mouseGrabber) + qwsServerPrivate->setCursor(QWSCursor::systemCursor(Qt::ArrowCursor)); + else + qwsServerPrivate->nextCursor = QWSCursor::systemCursor(Qt::ArrowCursor); + prevWin = false; + } + // reset prevWin + if (win && !prevWin) + prevWin = true; +#endif + + if ((state&btnMask) && !qwsServerPrivate->mouseGrabbing) { + qwsServerPrivate->mouseGrabber = win; + if (imMouse) + qwsServerPrivate->inputMethodMouseGrabbed = true; + } + if (!(state&btnMask)) + qwsServerPrivate->inputMethodMouseGrabbed = false; + + event.simpleData.x_root=pos.x(); + event.simpleData.y_root=pos.y(); + event.simpleData.state=state | qws_keyModifiers; + event.simpleData.delta = wheel; + event.simpleData.time=qwsServerPrivate->timer.elapsed(); + + static int oldstate = 0; + +#ifndef QT_NO_QWS_INPUTMETHODS + //tell the input method if we click on a different window that is not IM transparent + bool isPress = state > oldstate; + if (isPress && !imMouse && current_IM && current_IM_winId != -1) + current_IM->mouseHandler(-1, QWSServer::MouseOutside); +#endif + + if (serverClient) + serverClient->sendEvent(&event); + if (winClient && winClient != serverClient) + winClient->sendEvent(&event); + + if ( !imMouse ) { + // Make sure that if we leave a window, that window gets one last mouse + // event so that it knows the mouse has left. + QWSClient *oldClient = qwsServer->d_func()->cursorClient; + if (oldClient && oldClient != winClient && oldClient != serverClient) { + event.simpleData.state = oldstate | qws_keyModifiers; + oldClient->sendEvent(&event); + } + } + + oldstate = state; + if ( !imMouse ) + qwsServer->d_func()->cursorClient = winClient; + + if (!(state&btnMask) && !qwsServerPrivate->mouseGrabbing) + qwsServerPrivate->releaseMouse(qwsServerPrivate->mouseGrabber); +} + +/*! + Returns the primary mouse driver. + + Note that this function can only be used in the server process. + + \sa setMouseHandler(), openMouse(), closeMouse() +*/ +QWSMouseHandler *QWSServer::mouseHandler() +{ + if (qwsServerPrivate->mousehandlers.empty()) + return 0; + return qwsServerPrivate->mousehandlers.first(); +} + +/*! + \since 4.5 + + Returns list of all mouse handlers + + Note that this function can only be used in the server process. + + \sa mouseHandler(), setMouseHandler(), openMouse(), closeMouse() +*/ +const QList<QWSMouseHandler*>& QWSServer::mouseHandlers() +{ + return qwsServerPrivate->mousehandlers; +} + + +// called by QWSMouseHandler constructor, not user code. +/*! + \fn void QWSServer::setMouseHandler(QWSMouseHandler* driver) + + Sets the primary mouse driver to be the given \a driver. + + \l{Qt for Embedded Linux} provides several ready-made mouse drivers, and + custom drivers are typically added using Qt's plugin + mechanism. See the \l{Qt for Embedded Linux Pointer Handling} documentation + for details. + + Note that this function can only be used in the server process. + + \sa mouseHandler(), setDefaultMouse() +*/ +void QWSServer::setMouseHandler(QWSMouseHandler* mh) +{ + if (!mh) + return; + qwsServerPrivate->mousehandlers.removeAll(mh); + qwsServerPrivate->mousehandlers.prepend(mh); +} + +/*! + \internal + \obsolete + Caller owns data in list, and must delete contents +*/ +QList<QWSInternalWindowInfo*> * QWSServer::windowList() +{ + QList<QWSInternalWindowInfo*> * ret=new QList<QWSInternalWindowInfo*>; + for (int i=0; i < qwsServerPrivate->windows.size(); ++i) { + QWSWindow *window = qwsServerPrivate->windows.at(i); + QWSInternalWindowInfo * qwi=new QWSInternalWindowInfo(); + qwi->winid=window->winId(); + qwi->clientid=window->client()->clientId(); + ret->append(qwi); + } + return ret; +} + +#ifndef QT_NO_COP +/*! + \internal +*/ +void QWSServerPrivate::sendQCopEvent(QWSClient *c, const QString &ch, + const QString &msg, const QByteArray &data, + bool response) +{ + Q_ASSERT(c); + + QWSQCopMessageEvent event; + event.channel = ch.toLatin1(); + event.message = msg.toLatin1(); + event.data = data; + event.simpleData.is_response = response; + event.simpleData.lchannel = ch.length(); + event.simpleData.lmessage = msg.length(); + event.simpleData.ldata = data.size(); + int l = event.simpleData.lchannel + event.simpleData.lmessage + + event.simpleData.ldata; + + // combine channel, message and data into one block of raw bytes + char *tmp = new char [l]; + char *d = tmp; + memcpy(d, event.channel.constData(), event.simpleData.lchannel); + d += event.simpleData.lchannel; + memcpy(d, event.message.constData(), event.simpleData.lmessage); + d += event.simpleData.lmessage; + memcpy(d, data.constData(), event.simpleData.ldata); + + event.setDataDirect(tmp, l); + + c->sendEvent(&event); +} +#endif + +/*! + \fn QWSWindow *QWSServer::windowAt(const QPoint& position) + + Returns the window containing the given \a position. + + Note that if there is no window under the specified point this + function returns 0. + + \sa clientWindows(), instance() +*/ +QWSWindow *QWSServer::windowAt(const QPoint& pos) +{ + Q_D(QWSServer); + for (int i=0; i<d->windows.size(); ++i) { + QWSWindow* w = d->windows.at(i); + if (w->allocatedRegion().contains(pos)) + return w; + } + return 0; +} + +#ifndef QT_NO_QWS_KEYBOARD +static int keyUnicode(int keycode) +{ + int code = 0xffff; + + if (keycode >= Qt::Key_A && keycode <= Qt::Key_Z) + code = keycode - Qt::Key_A + 'a'; + else if (keycode >= Qt::Key_0 && keycode <= Qt::Key_9) + code = keycode - Qt::Key_0 + '0'; + + return code; +} +#endif + +/*! + Sends the given key event. The key is identified by its \a unicode + value and the given \a keycode, \a modifiers, \a isPress and \a + autoRepeat parameters. + + Use this function to send key events generated by "virtual + keyboards" (note that the processKeyEvent() function is + impelemented using this function). + + The \a keycode parameter is the Qt keycode value as defined by the + Qt::Key enum. The \a modifiers is an OR combination of + Qt::KeyboardModifier values, indicating whether \gui + Shift/Alt/Ctrl keys are pressed. The \a isPress parameter is true + if the event is a key press event and \a autoRepeat is true if the + event is caused by an auto-repeat mechanism and not an actual key + press. + + Note that this function can only be used in the server process. + + \sa processKeyEvent(), {Qt for Embedded Linux Character Input} +*/ +void QWSServer::sendKeyEvent(int unicode, int keycode, Qt::KeyboardModifiers modifiers, + bool isPress, bool autoRepeat) +{ + qws_keyModifiers = modifiers; + + if (isPress) { + if (keycode != Qt::Key_F34 && keycode != Qt::Key_F35) + qwsServerPrivate->_q_screenSaverWake(); + } + +#ifndef QT_NO_QWS_INPUTMETHODS + + if (!current_IM || !current_IM->filter(unicode, keycode, modifiers, isPress, autoRepeat)) + QWSServerPrivate::sendKeyEventUnfiltered(unicode, keycode, modifiers, isPress, autoRepeat); +#else + QWSServerPrivate::sendKeyEventUnfiltered(unicode, keycode, modifiers, isPress, autoRepeat); +#endif +} + +void QWSServerPrivate::sendKeyEventUnfiltered(int unicode, int keycode, Qt::KeyboardModifiers modifiers, + bool isPress, bool autoRepeat) +{ + + QWSKeyEvent event; + QWSWindow *win = keyboardGrabber ? keyboardGrabber : + qwsServerPrivate->focusw; + + event.simpleData.window = win ? win->winId() : 0; + + event.simpleData.unicode = +#ifndef QT_NO_QWS_KEYBOARD + unicode < 0 ? keyUnicode(keycode) : +#endif + unicode; + event.simpleData.keycode = keycode; + event.simpleData.modifiers = modifiers; + event.simpleData.is_press = isPress; + event.simpleData.is_auto_repeat = autoRepeat; + + QWSClient *serverClient = qwsServerPrivate->clientMap.value(-1); + QWSClient *winClient = win ? win->client() : 0; + if (serverClient) + serverClient->sendEvent(&event); + if (winClient && winClient != serverClient) + winClient->sendEvent(&event); +} + +/*! + \internal +*/ +void QWSServer::beginDisplayReconfigure() +{ + qwsServer->enablePainting(false); +#ifndef QT_NO_QWS_CURSOR + if (qt_screencursor) + qt_screencursor->hide(); +#endif + QWSDisplay::grab(true); + qt_screen->disconnect(); +} + +/*! + \internal +*/ +void QWSServer::endDisplayReconfigure() +{ + qt_screen->connect(QString()); + qwsServerPrivate->swidth = qt_screen->deviceWidth(); + qwsServerPrivate->sheight = qt_screen->deviceHeight(); + + QWSDisplay::ungrab(); +#ifndef QT_NO_QWS_CURSOR + if (qt_screencursor) + qt_screencursor->show(); +#endif + QApplicationPrivate *ap = QApplicationPrivate::instance(); + ap->setMaxWindowRect(qt_screen, 0, + QRect(0, 0, qt_screen->width(), qt_screen->height())); + QSize olds = qApp->desktop()->size(); + qApp->desktop()->resize(qt_screen->width(), qt_screen->height()); + qApp->postEvent(qApp->desktop(), new QResizeEvent(qApp->desktop()->size(), olds)); + qwsServer->enablePainting(true); + qwsServer->refresh(); + qDebug("Desktop size: %dx%d", qApp->desktop()->width(), qApp->desktop()->height()); +} + +void QWSServerPrivate::resetEngine() +{ +#ifndef QT_NO_QWS_CURSOR + if (!qt_screencursor) + return; + qt_screencursor->hide(); + qt_screencursor->show(); +#endif +} + + +#ifndef QT_NO_QWS_CURSOR +/*! + \fn void QWSServer::setCursorVisible(bool visible) + + Shows the cursor if \a visible is true: otherwise the cursor is + hidden. + + Note that this function can only be used in the server process. + + \sa isCursorVisible() +*/ +void QWSServer::setCursorVisible(bool vis) +{ + if (qwsServerPrivate && qwsServerPrivate->haveviscurs != vis) { + QWSCursor* c = qwsServerPrivate->cursor; + qwsServerPrivate->setCursor(QWSCursor::systemCursor(Qt::BlankCursor)); + qwsServerPrivate->haveviscurs = vis; + qwsServerPrivate->setCursor(c); + } +} + +/*! + Returns true if the cursor is visible; otherwise returns false. + + Note that this function can only be used in the server process. + + \sa setCursorVisible() +*/ +bool QWSServer::isCursorVisible() +{ + return qwsServerPrivate ? qwsServerPrivate->haveviscurs : true; +} +#endif + +#ifndef QT_NO_QWS_INPUTMETHODS + + +/*! + \fn void QWSServer::sendIMEvent(const QInputMethodEvent *event) + + Sends the given input method \a event. + + The \c QInputMethodEvent class is derived from QWSEvent, i.e., it + is a QWSEvent object of the QWSEvent::IMEvent type. + + If there is a window actively composing the preedit string, the + event is sent to that window. Otherwise, the event is sent to the + window currently in focus. + + \sa sendIMQuery(), QWSInputMethod::sendEvent() +*/ +void QWSServer::sendIMEvent(const QInputMethodEvent *ime) +{ + QWSIMEvent event; + + QWSWindow *win = keyboardGrabber ? keyboardGrabber : + qwsServerPrivate->focusw; + + //if currently composing then event must go to the composing window + + if (current_IM_composing_win) + win = current_IM_composing_win; + + event.simpleData.window = win ? win->winId() : 0; + event.simpleData.replaceFrom = ime->replacementStart();; + event.simpleData.replaceLength = ime->replacementLength(); + + QBuffer buffer; + buffer.open(QIODevice::WriteOnly); + QDataStream out(&buffer); + + out << ime->preeditString(); + out << ime->commitString(); + + const QList<QInputMethodEvent::Attribute> &attributes = ime->attributes(); + for (int i = 0; i < attributes.count(); ++i) { + const QInputMethodEvent::Attribute &a = attributes.at(i); + out << a.type << a.start << a.length << a.value; + } + event.setData(buffer.data(), buffer.size()); + QWSClient *serverClient = qwsServerPrivate->clientMap.value(-1); + if (serverClient) + serverClient->sendEvent(&event); + if (win && win->client() && win->client() != serverClient) + win->client()->sendEvent(&event); + + current_IM_composing_win = ime->preeditString().isEmpty() ? 0 : win; + current_IM_winId = win ? win->winId() : 0; +} + + +/*! + Sends an input method query for the given \a property. + + To receive responses to input method queries, the virtual + QWSInputMethod::queryResponse() function must be reimplemented in + a QWSInputMethod subclass that is activated using the + setCurrentInputMethod() function. + + \sa sendIMEvent(), setCurrentInputMethod() +*/ +void QWSServer::sendIMQuery(int property) +{ + QWSIMQueryEvent event; + + QWSWindow *win = keyboardGrabber ? keyboardGrabber : + qwsServerPrivate->focusw; + if (current_IM_composing_win) + win = current_IM_composing_win; + + event.simpleData.window = win ? win->winId() : 0; + event.simpleData.property = property; + if (win && win->client()) + win->client()->sendEvent(&event); +} + + + +/*! + \fn void QWSServer::setCurrentInputMethod(QWSInputMethod *method) + + Sets the current input method to be the given \a method. + + Note that this function can only be used in the server process. + + \sa sendIMQuery(), sendIMEvent() +*/ +void QWSServer::setCurrentInputMethod(QWSInputMethod *im) +{ + if (current_IM) + current_IM->reset(); //??? send an update event instead ? + current_IM = im; +} + +/*! + \fn static void QWSServer::resetInputMethod() + + \internal +*/ + +#endif //QT_NO_QWS_INPUTMETHODS + +#ifndef QT_NO_QWS_PROPERTIES +/*! + \internal +*/ +void QWSServer::sendPropertyNotifyEvent(int property, int state) +{ + Q_D(QWSServer); + QWSServerPrivate::ClientIterator it = d->clientMap.begin(); + while (it != d->clientMap.end()) { + QWSClient *cl = *it; + ++it; + cl->sendPropertyNotifyEvent(property, state); + } +} +#endif + +void QWSServerPrivate::invokeIdentify(const QWSIdentifyCommand *cmd, QWSClient *client) +{ + client->setIdentity(cmd->id); +#ifndef QT_NO_QWS_MULTIPROCESS + if (client->clientId() > 0) + client->d_func()->setLockId(cmd->simpleData.idLock); +#endif +} + +void QWSServerPrivate::invokeCreate(QWSCreateCommand *cmd, QWSClient *client) +{ + QWSCreationEvent event; + event.simpleData.objectid = get_object_id(cmd->count); + event.simpleData.count = cmd->count; + client->sendEvent(&event); +} + +void QWSServerPrivate::invokeRegionName(const QWSRegionNameCommand *cmd, QWSClient *client) +{ + Q_Q(QWSServer); + QWSWindow* changingw = findWindow(cmd->simpleData.windowid, client); + if (changingw && (changingw->name() != cmd->name || changingw->caption() !=cmd->caption)) { + changingw->setName(cmd->name); + changingw->setCaption(cmd->caption); + emit q->windowEvent(changingw, QWSServer::Name); + } +} + +void QWSServerPrivate::invokeRegion(QWSRegionCommand *cmd, QWSClient *client) +{ +#ifdef QWS_REGION_DEBUG + qDebug("QWSServer::invokeRegion %d rects (%d)", + cmd->simpleData.nrectangles, cmd->simpleData.windowid); +#endif + + QWSWindow* changingw = findWindow(cmd->simpleData.windowid, 0); + if (!changingw) { + qWarning("Invalid window handle %08x",cmd->simpleData.windowid); + return; + } + if (!changingw->forClient(client)) { + qWarning("Disabled: clients changing other client's window region"); + return; + } + + request_region(cmd->simpleData.windowid, cmd->surfaceKey, cmd->surfaceData, + cmd->region); +} + +void QWSServerPrivate::invokeRegionMove(const QWSRegionMoveCommand *cmd, QWSClient *client) +{ + Q_Q(QWSServer); + QWSWindow* changingw = findWindow(cmd->simpleData.windowid, 0); + if (!changingw) { + qWarning("invokeRegionMove: Invalid window handle %d",cmd->simpleData.windowid); + return; + } + if (!changingw->forClient(client)) { + qWarning("Disabled: clients changing other client's window region"); + return; + } + +// changingw->setNeedAck(true); + moveWindowRegion(changingw, cmd->simpleData.dx, cmd->simpleData.dy); + emit q->windowEvent(changingw, QWSServer::Geometry); +} + +void QWSServerPrivate::invokeRegionDestroy(const QWSRegionDestroyCommand *cmd, QWSClient *client) +{ + Q_Q(QWSServer); + QWSWindow* changingw = findWindow(cmd->simpleData.windowid, 0); + if (!changingw) { + qWarning("invokeRegionDestroy: Invalid window handle %d",cmd->simpleData.windowid); + return; + } + if (!changingw->forClient(client)) { + qWarning("Disabled: clients changing other client's window region"); + return; + } + + setWindowRegion(changingw, QRegion()); +// rgnMan->remove(changingw->allocationIndex()); + for (int i = 0; i < windows.size(); ++i) { + if (windows.at(i) == changingw) { + windows.takeAt(i); + if (i < nReserved) + --nReserved; + break; + } + } + + handleWindowClose(changingw); +#ifndef QT_NO_QWS_PROPERTIES + propertyManager.removeProperties(changingw->winId()); +#endif + emit q->windowEvent(changingw, QWSServer::Destroy); + delete changingw; +} + +void QWSServerPrivate::invokeSetFocus(const QWSRequestFocusCommand *cmd, QWSClient *client) +{ + int winId = cmd->simpleData.windowid; + int gain = cmd->simpleData.flag; + + if (gain != 0 && gain != 1) { + qWarning("Only 0(lose) and 1(gain) supported"); + return; + } + + QWSWindow* changingw = findWindow(winId, 0); + if (!changingw) + return; + + if (!changingw->forClient(client)) { + qWarning("Disabled: clients changing other client's focus"); + return; + } + + setFocus(changingw, gain); +} + +void QWSServerPrivate::setFocus(QWSWindow* changingw, bool gain) +{ + Q_Q(QWSServer); +#ifndef QT_NO_QWS_INPUTMETHODS + /* + This is the logic: + QWSWindow *loser = 0; + if (gain && focusw != changingw) + loser = focusw; + else if (!gain && focusw == changingw) + loser = focusw; + But these five lines can be reduced to one: + */ + if (current_IM) { + QWSWindow *loser = (!gain == (focusw==changingw)) ? focusw : 0; + if (loser && loser->winId() == current_IM_winId) + current_IM->updateHandler(QWSInputMethod::FocusOut); + } +#endif + if (gain) { + if (focusw != changingw) { + if (focusw) focusw->focus(0); + focusw = changingw; + focusw->focus(1); + emit q->windowEvent(focusw, QWSServer::Active); + } + } else if (focusw == changingw) { + if (changingw->client()) + changingw->focus(0); + focusw = 0; + // pass focus to window which most recently got it... + QWSWindow* bestw=0; + for (int i=0; i<windows.size(); ++i) { + QWSWindow* w = windows.at(i); + if (w != changingw && !w->hidden() && + (!bestw || bestw->focusPriority() < w->focusPriority())) + bestw = w; + } + if (!bestw && changingw->focusPriority()) { // accept focus back? + bestw = changingw; // must be the only one + } + focusw = bestw; + if (focusw) { + focusw->focus(1); + emit q->windowEvent(focusw, QWSServer::Active); + } + } +} + + + +void QWSServerPrivate::invokeSetOpacity(const QWSSetOpacityCommand *cmd, QWSClient *client) +{ + Q_UNUSED( client ); + int winId = cmd->simpleData.windowid; + int opacity = cmd->simpleData.opacity; + + QWSWindow* changingw = findWindow(winId, 0); + + if (!changingw) { + qWarning("invokeSetOpacity: Invalid window handle %d", winId); + return; + } + + int altitude = windows.indexOf(changingw); + const bool wasOpaque = changingw->isOpaque(); + changingw->_opacity = opacity; + if (wasOpaque != changingw->isOpaque()) + update_regions(); + exposeRegion(changingw->allocatedRegion(), altitude); +} + +void QWSServerPrivate::invokeSetAltitude(const QWSChangeAltitudeCommand *cmd, + QWSClient *client) +{ + Q_UNUSED(client); + + int winId = cmd->simpleData.windowid; + int alt = cmd->simpleData.altitude; + bool fixed = cmd->simpleData.fixed; +#if 0 + qDebug("QWSServer::invokeSetAltitude winId %d alt %d)", winId, alt); +#endif + + if (alt < -1 || alt > 1) { + qWarning("QWSServer::invokeSetAltitude Only lower, raise and stays-on-top supported"); + return; + } + + QWSWindow* changingw = findWindow(winId, 0); + if (!changingw) { + qWarning("invokeSetAltitude: Invalid window handle %d", winId); + return; + } + + if (fixed && alt >= 1) { + changingw->onTop = true; + } + if (alt == QWSChangeAltitudeCommand::Lower) + changingw->lower(); + else + changingw->raise(); + +// if (!changingw->forClient(client)) { +// refresh(); +// } +} + +#ifndef QT_NO_QWS_PROPERTIES +void QWSServerPrivate::invokeAddProperty(QWSAddPropertyCommand *cmd) +{ + propertyManager.addProperty(cmd->simpleData.windowid, cmd->simpleData.property); +} + +void QWSServerPrivate::invokeSetProperty(QWSSetPropertyCommand *cmd) +{ + Q_Q(QWSServer); + if (propertyManager.setProperty(cmd->simpleData.windowid, + cmd->simpleData.property, + cmd->simpleData.mode, + cmd->data, + cmd->rawLen)) { + q->sendPropertyNotifyEvent(cmd->simpleData.property, + QWSPropertyNotifyEvent::PropertyNewValue); +#ifndef QT_NO_QWS_INPUTMETHODS + if (cmd->simpleData.property == QT_QWS_PROPERTY_MARKEDTEXT) { + QString s((const QChar*)cmd->data, cmd->rawLen/2); + emit q->markedText(s); + } +#endif + } +} + +void QWSServerPrivate::invokeRemoveProperty(QWSRemovePropertyCommand *cmd) +{ + Q_Q(QWSServer); + if (propertyManager.removeProperty(cmd->simpleData.windowid, + cmd->simpleData.property)) { + q->sendPropertyNotifyEvent(cmd->simpleData.property, + QWSPropertyNotifyEvent::PropertyDeleted); + } +} + + +bool QWSServerPrivate:: get_property(int winId, int property, const char *&data, int &len) +{ + return propertyManager.getProperty(winId, property, data, len); +} + + +void QWSServerPrivate::invokeGetProperty(QWSGetPropertyCommand *cmd, QWSClient *client) +{ + const char *data; + int len; + + if (propertyManager.getProperty(cmd->simpleData.windowid, + cmd->simpleData.property, + data, len)) { + client->sendPropertyReplyEvent(cmd->simpleData.property, len, data); + } else { + client->sendPropertyReplyEvent(cmd->simpleData.property, -1, 0); + } +} +#endif //QT_NO_QWS_PROPERTIES + +void QWSServerPrivate::invokeSetSelectionOwner(QWSSetSelectionOwnerCommand *cmd) +{ + qDebug("QWSServer::invokeSetSelectionOwner"); + + SelectionOwner so; + so.windowid = cmd->simpleData.windowid; + so.time.set(cmd->simpleData.hour, cmd->simpleData.minute, + cmd->simpleData.sec, cmd->simpleData.ms); + + if (selectionOwner.windowid != -1) { + QWSWindow *win = findWindow(selectionOwner.windowid, 0); + if (win) + win->client()->sendSelectionClearEvent(selectionOwner.windowid); + else + qDebug("couldn't find window %d", selectionOwner.windowid); + } + + selectionOwner = so; +} + +void QWSServerPrivate::invokeConvertSelection(QWSConvertSelectionCommand *cmd) +{ + qDebug("QWSServer::invokeConvertSelection"); + + if (selectionOwner.windowid != -1) { + QWSWindow *win = findWindow(selectionOwner.windowid, 0); + if (win) + win->client()->sendSelectionRequestEvent(cmd, selectionOwner.windowid); + else + qDebug("couldn't find window %d", selectionOwner.windowid); + } +} + +#ifndef QT_NO_QWS_CURSOR +void QWSServerPrivate::invokeDefineCursor(QWSDefineCursorCommand *cmd, QWSClient *client) +{ + if (cmd->simpleData.height > 64 || cmd->simpleData.width > 64) { + qDebug("Cannot define cursor size > 64x64"); + return; + } + + delete client->cursors.take(cmd->simpleData.id); + + int dataLen = cmd->simpleData.height * ((cmd->simpleData.width+7) / 8); + + if (dataLen > 0 && cmd->data) { + QWSCursor *curs = new QWSCursor(cmd->data, cmd->data + dataLen, + cmd->simpleData.width, cmd->simpleData.height, + cmd->simpleData.hotX, cmd->simpleData.hotY); + client->cursors.insert(cmd->simpleData.id, curs); + } +} + +void QWSServerPrivate::invokeSelectCursor(QWSSelectCursorCommand *cmd, QWSClient *client) +{ + int id = cmd->simpleData.id; + QWSCursor *curs = 0; + if (id <= Qt::LastCursor) { + curs = QWSCursor::systemCursor(id); + } + else { + QWSCursorMap cursMap = client->cursors; + QWSCursorMap::Iterator it = cursMap.find(id); + if (it != cursMap.end()) { + curs = it.value(); + } + } + if (curs == 0) { + curs = QWSCursor::systemCursor(Qt::ArrowCursor); + } + + QWSWindow* win = findWindow(cmd->simpleData.windowid, 0); + if (mouseGrabber) { + // If the mouse is being grabbed, we don't want just anyone to + // be able to change the cursor. We do want the cursor to be set + // correctly once mouse grabbing is stopped though. + if (win != mouseGrabber) + nextCursor = curs; + else + setCursor(curs); + } else if (win && win->allocatedRegion().contains(QWSServer::mousePosition)) { //##################### cursor + // A non-grabbing window can only set the cursor shape if the + // cursor is within its allocated region. + setCursor(curs); + } +} + +void QWSServerPrivate::invokePositionCursor(QWSPositionCursorCommand *cmd, QWSClient *) +{ + Q_Q(QWSServer); + QPoint newPos(cmd->simpleData.newX, cmd->simpleData.newY); + if (newPos != QWSServer::mousePosition) + q->sendMouseEvent(newPos, qwsServer->d_func()->mouseState); +} +#endif + +void QWSServerPrivate::invokeGrabMouse(QWSGrabMouseCommand *cmd, QWSClient *client) +{ + QWSWindow* win = findWindow(cmd->simpleData.windowid, 0); + if (!win) + return; + + if (cmd->simpleData.grab) { + if (!mouseGrabber || mouseGrabber->client() == client) { + mouseGrabbing = true; + mouseGrabber = win; + } + } else { + releaseMouse(mouseGrabber); + } +} + +void QWSServerPrivate::invokeGrabKeyboard(QWSGrabKeyboardCommand *cmd, QWSClient *client) +{ + QWSWindow* win = findWindow(cmd->simpleData.windowid, 0); + if (!win) + return; + + if (cmd->simpleData.grab) { + if (!keyboardGrabber || (keyboardGrabber->client() == client)) { + keyboardGrabbing = true; + keyboardGrabber = win; + } + } else { + releaseKeyboard(keyboardGrabber); + } +} + +#if !defined(QT_NO_SOUND) +void QWSServerPrivate::invokePlaySound(QWSPlaySoundCommand *cmd, QWSClient *) +{ +#if !defined(QT_EXTERNAL_SOUND_SERVER) && !defined(Q_OS_DARWIN) + soundserver->playFile( 1, cmd->filename ); +#else + Q_UNUSED(cmd); +#endif +} +#endif + +#ifndef QT_NO_COP +void QWSServerPrivate::invokeRegisterChannel(QWSQCopRegisterChannelCommand *cmd, + QWSClient *client) +{ + // QCopChannel will force us to emit the newChannel signal if this channel + // didn't already exist. + QCopChannel::registerChannel(cmd->channel, client); +} + +void QWSServerPrivate::invokeQCopSend(QWSQCopSendCommand *cmd, QWSClient *client) +{ + QCopChannel::answer(client, cmd->channel, cmd->message, cmd->data); +} + +#endif + +#ifndef QT_NO_QWS_INPUTMETHODS +void QWSServer::resetInputMethod() +{ + if (current_IM && qwsServer) { + current_IM->reset(); + } +} + +void QWSServerPrivate::invokeIMResponse(const QWSIMResponseCommand *cmd, + QWSClient *) +{ + if (current_IM) + current_IM->queryResponse(cmd->simpleData.property, cmd->result); +} + +void QWSServerPrivate::invokeIMUpdate(const QWSIMUpdateCommand *cmd, + QWSClient *) +{ + if (cmd->simpleData.type == QWSInputMethod::FocusIn) + current_IM_winId = cmd->simpleData.windowid; + + if (current_IM && (current_IM_winId == cmd->simpleData.windowid || cmd->simpleData.windowid == -1)) + current_IM->updateHandler(cmd->simpleData.type); +} + +#endif + +void QWSServerPrivate::invokeFont(const QWSFontCommand *cmd, QWSClient *client) +{ + QWSClientPrivate *priv = client->d_func(); + if (cmd->simpleData.type == QWSFontCommand::StartedUsingFont) { + referenceFont(priv, cmd->fontName); + } else if (cmd->simpleData.type == QWSFontCommand::StoppedUsingFont) { + dereferenceFont(priv, cmd->fontName); + } +} + +void QWSServerPrivate::invokeRepaintRegion(QWSRepaintRegionCommand * cmd, + QWSClient *) +{ + QRegion r; + r.setRects(cmd->rectangles,cmd->simpleData.nrectangles); + repaint_region(cmd->simpleData.windowid, cmd->simpleData.windowFlags, cmd->simpleData.opaque, r); +} + +#ifndef QT_NO_QWSEMBEDWIDGET +void QWSServerPrivate::invokeEmbed(QWSEmbedCommand *cmd, QWSClient *client) +{ + // Should find these two windows in a single loop + QWSWindow *embedder = findWindow(cmd->simpleData.embedder, client); + QWSWindow *embedded = findWindow(cmd->simpleData.embedded); + + if (!embedder) { + qWarning("QWSServer: Embed command from window %i failed: No such id.", + static_cast<int>(cmd->simpleData.embedder)); + return; + } + + if (!embedded) { + qWarning("QWSServer: Embed command on window %i failed: No such id.", + static_cast<int>(cmd->simpleData.embedded)); + return; + } + + switch (cmd->simpleData.type) { + case QWSEmbedEvent::StartEmbed: + embedder->startEmbed(embedded); + windows.removeAll(embedded); + windows.insert(windows.indexOf(embedder), embedded); + break; + case QWSEmbedEvent::StopEmbed: + embedder->stopEmbed(embedded); + break; + case QWSEmbedEvent::Region: + break; + } + + embedded->client()->sendEmbedEvent(embedded->winId(), + cmd->simpleData.type, cmd->region); + const QRegion oldAllocated = embedded->allocatedRegion(); + update_regions(); + exposeRegion(oldAllocated - embedded->allocatedRegion(), + windows.indexOf(embedded)); +} +#endif // QT_NO_QWSEMBEDWIDGET + +void QWSServerPrivate::invokeScreenTransform(const QWSScreenTransformCommand *cmd, + QWSClient *client) +{ + Q_UNUSED(client); + + QWSScreenTransformationEvent event; + event.simpleData.screen = cmd->simpleData.screen; + event.simpleData.transformation = cmd->simpleData.transformation; + + QMap<int, QWSClient*>::const_iterator it = clientMap.constBegin(); + for (; it != clientMap.constEnd(); ++it) + (*it)->sendEvent(&event); +} + +QWSWindow* QWSServerPrivate::newWindow(int id, QWSClient* client) +{ + Q_Q(QWSServer); + // Make a new window, put it on top. + QWSWindow* w = new QWSWindow(id,client); + + // insert after "stays on top" windows + bool added = false; + for (int i = nReserved; i < windows.size(); ++i) { + QWSWindow *win = windows.at(i); + if (!win->onTop) { + windows.insert(i, w); + added = true; + break; + } + } + if (!added) + windows.append(w); + emit q->windowEvent(w, QWSServer::Create); + return w; +} + +QWSWindow* QWSServerPrivate::findWindow(int windowid, QWSClient* client) +{ + for (int i=0; i<windows.size(); ++i) { + QWSWindow* w = windows.at(i); + if (w->winId() == windowid) + return w; + } + if (client) + return newWindow(windowid,client); + else + return 0; +} + +void QWSServerPrivate::raiseWindow(QWSWindow *changingw, int /*alt*/) +{ + Q_Q(QWSServer); + if (changingw == windows.first()) + return; + QWSWindow::State oldstate = changingw->d->state; + changingw->d->state = QWSWindow::Raising; + // Expose regions previously overlapped by transparent windows + const QRegion bound = changingw->allocatedRegion(); + QRegion expose; + int windowPos = 0; + + //change position in list: + for (int i = 0; i < windows.size(); ++i) { + QWSWindow *w = windows.at(i); + if (w == changingw) { + windowPos = i; + windows.takeAt(i); + break; + } + if (!w->isOpaque()) + expose += (w->allocatedRegion() & bound); + } + + bool onTop = changingw->onTop; + +#ifndef QT_NO_QWSEMBEDWIDGET + // an embedded window is on top if the embedder is on top + QWSWindow *embedder = changingw->d->embedder; + while (!onTop && embedder) { + onTop = embedder->onTop; + embedder = embedder->d->embedder; + } +#endif + + int newPos = -1; + if (onTop) { + windows.insert(nReserved, changingw); + newPos = nReserved; + } else { + // insert after "stays on top" windows + bool in = false; + for (int i = nReserved; i < windows.size(); ++i) { + QWSWindow *w = windows.at(i); + if (!w->onTop) { + windows.insert(i, changingw); + in = true; + newPos = i; + break; + } + } + if (!in) { + windows.append(changingw); + newPos = windows.size()-1; + } + } + + if (windowPos != newPos) { + update_regions(); + if (!expose.isEmpty()) + exposeRegion(expose, newPos); + } + changingw->d->state = oldstate; + emit q->windowEvent(changingw, QWSServer::Raise); +} + +void QWSServerPrivate::lowerWindow(QWSWindow *changingw, int /*alt*/) +{ + Q_Q(QWSServer); + if (changingw == windows.last()) + return; + QWSWindow::State oldstate = changingw->d->state; + changingw->d->state = QWSWindow::Lowering; + + int i = windows.indexOf(changingw); + int newIdx = windows.size()-1; + windows.move(i, newIdx); + + const QRegion bound = changingw->allocatedRegion(); + + update_regions(); + + // Expose regions previously overlapped by transparent window + if (!changingw->isOpaque()) { + QRegion expose; + for (int j = i; j < windows.size() - 1; ++j) + expose += (windows.at(j)->allocatedRegion() & bound); + if (!expose.isEmpty()) + exposeRegion(expose, newIdx); + } + + changingw->d->state = oldstate; + emit q->windowEvent(changingw, QWSServer::Lower); +} + +void QWSServerPrivate::update_regions() +{ + if (disablePainting) + return; + + QRegion available = QRect(0, 0, qt_screen->width(), qt_screen->height()); + QRegion transparentRegion; + + // only really needed if there are unbuffered surfaces... + const bool doLock = (clientMap.size() > 1); + if (doLock) + QWSDisplay::grab(true); + + for (int i = 0; i < windows.count(); ++i) { + QWSWindow *w = windows.at(i); + QRegion r = (w->requested_region & available); + +#ifndef QT_NO_QWSEMBEDWIDGET + // Subtract regions needed for embedded windows + const int n = w->d->embedded.size(); + for (int i = 0; i < n; ++i) + r -= w->d->embedded.at(i)->allocatedRegion(); + + // Limited to the embedder region + if (w->d->embedder) + r &= w->d->embedder->requested_region; +#endif // QT_NO_QWSEMBEDWIDGET + + QWSWindowSurface *surface = w->windowSurface(); + const bool opaque = w->isOpaque() + && (w->d->painted || !surface || !surface->isBuffered()); + + if (!opaque) { + transparentRegion += r; + } else { + if (surface && (surface->isRegionReserved() || !surface->isBuffered())) + r -= transparentRegion; + available -= r; + } + + if (r != w->allocatedRegion()) { + w->setAllocatedRegion(r); + w->client()->sendRegionEvent(w->winId(), r, + QWSRegionEvent::Allocation); + } + +#ifdef QT_QWS_CLIENTBLIT +#ifdef QT_NO_QWS_CURSOR + // This optimization only really works when there isn't a crazy cursor + // wizzing around. + QRegion directPaint = (r - transparentRegion); // in gloal coords + if(directPaint != w->directPaintRegion()) { + w->setDirectPaintRegion(directPaint); + static int id = 0; + surface->setDirectRegion(directPaint, ++id); + w->client()->sendRegionEvent(w->winId(), directPaint, + QWSRegionEvent::DirectPaint, id); + } +#endif +#endif + } + + if (doLock) + QWSDisplay::ungrab(); +} + +void QWSServerPrivate::moveWindowRegion(QWSWindow *changingw, int dx, int dy) +{ + if (!changingw) + return; + + QWSWindow::State oldState = changingw->d->state; + changingw->d->state = QWSWindow::Moving; + const QRegion oldRegion(changingw->allocatedRegion()); + changingw->requested_region.translate(dx, dy); + + // hw: Even if the allocated region doesn't change, the requested region + // region has changed and we need to send region events. + // Resetting the allocated region to force update_regions to send events. + changingw->setAllocatedRegion(QRegion()); + update_regions(); + const QRegion newRegion(changingw->allocatedRegion()); + + QWSWindowSurface *surface = changingw->windowSurface(); + QRegion expose; + if (surface) + expose = surface->move(QPoint(dx, dy), changingw->allocatedRegion()); + else + expose = oldRegion + newRegion; + + if (!changingw->d->painted && !expose.isEmpty()) + expose = oldRegion - newRegion; + + int idx = windows.indexOf(changingw); + exposeRegion(expose, idx); + changingw->d->state = oldState; +} + +/*! + Changes the requested region of window \a changingw to \a r + If \a changingw is 0, the server's reserved region is changed. +*/ +void QWSServerPrivate::setWindowRegion(QWSWindow* changingw, const QRegion &r) +{ + if (!changingw) { + qWarning("Not implemented in this release"); + return; + } + + if (changingw->requested_region == r) + return; + + const QRegion oldRegion(changingw->allocatedRegion()); + changingw->requested_region = r; + update_regions(); + const QRegion newRegion(changingw->allocatedRegion()); + + int idx = windows.indexOf(changingw); + exposeRegion(oldRegion - newRegion, idx); +} + + +void QWSServerPrivate::exposeRegion(const QRegion &r, int changing) +{ + if (disablePainting) + return; + + if (r.isEmpty()) + return; + + static bool initial = true; + if (initial) { + changing = 0; + initial = false; + qt_screen->exposeRegion(qt_screen->region(), changing); + } else { + qt_screen->exposeRegion(r, changing); + } +} + +/*! + Closes all pointer devices (specified by the QWS_MOUSE_PROTO + environment variable) by deleting the associated mouse drivers. + + \sa openMouse(), mouseHandler() +*/ +void QWSServer::closeMouse() +{ + Q_D(QWSServer); + qDeleteAll(d->mousehandlers); + d->mousehandlers.clear(); +} + +/*! + Opens the mouse devices specified by the QWS_MOUSE_PROTO + environment variable. Be advised that closeMouse() is called first + to delete all the existing mouse handlers. This behaviour could be + the cause of problems if you were not expecting it. + + \sa closeMouse(), mouseHandler() +*/ +void QWSServer::openMouse() +{ + Q_D(QWSServer); + QString mice = QString::fromLatin1(qgetenv("QWS_MOUSE_PROTO")); +#if defined(QT_QWS_CASSIOPEIA) + if (mice.isEmpty()) + mice = QLatin1String("TPanel:/dev/tpanel"); +#endif + if (mice.isEmpty()) + mice = *defaultMouse(); + closeMouse(); + bool needviscurs = true; + if (mice != QLatin1String("None")) { + const QStringList mouse = mice.split(QLatin1Char(' ')); + for (int i = mouse.size() - 1; i >= 0; --i) { + QWSMouseHandler *handler = d->newMouseHandler(mouse.at(i)); + setMouseHandler(handler); + /* XXX handle mouse cursor visibility sensibly + if (!h->inherits("QCalibratedMouseHandler")) + needviscurs = true; + */ + } + } +#ifndef QT_NO_QWS_CURSOR + setCursorVisible(needviscurs); +#else + Q_UNUSED(needviscurs) +#endif +} + +/*! + Suspends pointer handling by deactivating all the mouse drivers + registered by the QWS_MOUSE_PROTO environment variable. + + + \sa resumeMouse(), QWSMouseHandler::suspend() +*/ +void QWSServer::suspendMouse() +{ + Q_D(QWSServer); + for (int i=0; i < d->mousehandlers.size(); ++i) + d->mousehandlers.at(i)->suspend(); +} + +/*! + Resumes pointer handling by reactivating all the mouse drivers + registered by the QWS_MOUSE_PROTO environment variable. + + \sa suspendMouse(), QWSMouseHandler::resume() +*/ +void QWSServer::resumeMouse() +{ + Q_D(QWSServer); + for (int i=0; i < d->mousehandlers.size(); ++i) + d->mousehandlers.at(i)->resume(); +} + + + +QWSMouseHandler* QWSServerPrivate::newMouseHandler(const QString& spec) +{ + int c = spec.indexOf(QLatin1Char(':')); + QString mouseProto; + QString mouseDev; + if (c >= 0) { + mouseProto = spec.left(c); + mouseDev = spec.mid(c+1); + } else { + mouseProto = spec; + } + + int screen = -1; + const QList<QRegExp> regexps = QList<QRegExp>() + << QRegExp(QLatin1String(":screen=(\\d+)\\b")) + << QRegExp(QLatin1String("\\bscreen=(\\d+):")); + for (int i = 0; i < regexps.size(); ++i) { + QRegExp regexp = regexps.at(i); + if (regexp.indexIn(mouseDev) == -1) + continue; + screen = regexp.cap(1).toInt(); + mouseDev.remove(regexp.pos(0), regexp.matchedLength()); + break; + } + + QWSMouseHandler *handler = 0; + handler = QMouseDriverFactory::create(mouseProto, mouseDev); + if (screen != -1) + handler->setScreen(qt_screen->subScreens().at(screen)); + + return handler; +} + +#ifndef QT_NO_QWS_KEYBOARD + +/*! + Closes all the keyboard devices (specified by the QWS_KEYBOARD + environment variable) by deleting the associated keyboard + drivers. + + \sa openKeyboard(), keyboardHandler() +*/ +void QWSServer::closeKeyboard() +{ + Q_D(QWSServer); + qDeleteAll(d->keyboardhandlers); + d->keyboardhandlers.clear(); +} + +/*! + Returns the primary keyboard driver. + + Note that this function can only be used in the server process. + + \sa setKeyboardHandler(), openKeyboard(), closeKeyboard() +*/ +QWSKeyboardHandler* QWSServer::keyboardHandler() +{ + return qwsServerPrivate->keyboardhandlers.first(); +} + +/*! + \fn void QWSServer::setKeyboardHandler(QWSKeyboardHandler* driver) + + Sets the primary keyboard driver to be the given \a driver. + + \l{Qt for Embedded Linux} provides several ready-made keyboard drivers, and + custom drivers are typically added using Qt's plugin + mechanism. See the \l{Qt for Embedded Linux Character Input} documentation + for details. + + Note that this function can only be used in the server process. + + \sa keyboardHandler(), setDefaultKeyboard() +*/ +void QWSServer::setKeyboardHandler(QWSKeyboardHandler* kh) +{ + if (!kh) + return; + qwsServerPrivate->keyboardhandlers.removeAll(kh); + qwsServerPrivate->keyboardhandlers.prepend(kh); +} + +/*! + Opens the keyboard devices specified by the QWS_KEYBOARD + environment variable. + + \sa closeKeyboard(), keyboardHandler() +*/ +void QWSServer::openKeyboard() +{ + QString keyboards = QString::fromLatin1(qgetenv("QWS_KEYBOARD")); +#if defined(QT_QWS_CASSIOPEIA) + if (keyboards.isEmpty()) + keyboards = QLatin1String("Buttons"); +#endif + if (keyboards.isEmpty()) + keyboards = *defaultKeyboard(); + + closeKeyboard(); + if (keyboards == QLatin1String("None")) + return; + + QString device; + QString type; + QStringList keyboard = keyboards.split(QLatin1Char(' ')); + for (int i = keyboard.size() - 1; i >= 0; --i) { + const QString spec = keyboard.at(i); + int colon=spec.indexOf(QLatin1Char(':')); + if (colon>=0) { + type = spec.left(colon); + device = spec.mid(colon+1); + } else { + type = spec; + device = QString(); + } + QWSKeyboardHandler *handler = QKbdDriverFactory::create(type, device); + setKeyboardHandler(handler); + } +} + +#endif //QT_NO_QWS_KEYBOARD + +QPoint QWSServer::mousePosition; +QBrush *QWSServerPrivate::bgBrush = 0; + +void QWSServerPrivate::move_region(const QWSRegionMoveCommand *cmd) +{ + QWSClient *serverClient = clientMap.value(-1); + invokeRegionMove(cmd, serverClient); +} + +void QWSServerPrivate::set_altitude(const QWSChangeAltitudeCommand *cmd) +{ + QWSClient *serverClient = clientMap.value(-1); + invokeSetAltitude(cmd, serverClient); +} + +void QWSServerPrivate::set_opacity(const QWSSetOpacityCommand *cmd) +{ + QWSClient *serverClient = clientMap.value(-1); + invokeSetOpacity(cmd, serverClient); +} + + +void QWSServerPrivate::request_focus(const QWSRequestFocusCommand *cmd) +{ + invokeSetFocus(cmd, clientMap.value(-1)); +} + +void QWSServerPrivate::set_identity(const QWSIdentifyCommand *cmd) +{ + invokeIdentify(cmd, clientMap.value(-1)); +} + +void QWSServerPrivate::repaint_region(int wid, int windowFlags, bool opaque, + const QRegion ®ion) +{ + QWSWindow* changingw = findWindow(wid, 0); + if (!changingw) { + return; + } + + const bool isOpaque = changingw->opaque; + const bool wasPainted = changingw->d->painted; + changingw->opaque = opaque; + changingw->d->windowFlags = QFlag(windowFlags); + changingw->d->dirtyOnScreen |= region; + changingw->d->painted = true; + if (isOpaque != opaque || !wasPainted) + update_regions(); + + int level = windows.indexOf(changingw); + exposeRegion(region, level); + changingw->d->dirtyOnScreen = QRegion(); +} + +QRegion QWSServerPrivate::reserve_region(QWSWindow *win, const QRegion ®ion) +{ + QRegion r = region; + + int oldPos = windows.indexOf(win); + int newPos = oldPos < nReserved ? nReserved - 1 : nReserved; + for (int i = 0; i < nReserved; ++i) { + if (i != oldPos) { + QWSWindow *w = windows.at(i); + r -= w->requested_region; + } + } + windows.move(oldPos, newPos); + nReserved = newPos + 1; + + return r; +} + +void QWSServerPrivate::request_region(int wid, const QString &surfaceKey, + const QByteArray &surfaceData, + const QRegion ®ion) +{ + QWSWindow *changingw = findWindow(wid, 0); + if (!changingw) + return; + + Q_Q(QWSServer); + QWSWindow::State windowState = QWSWindow::NoState; + + if (region.isEmpty()) { + windowState = QWSWindow::Hiding; + emit q->windowEvent(changingw, QWSServer::Hide); + } + + const bool wasOpaque = changingw->opaque; + + changingw->createSurface(surfaceKey, surfaceData); + QWSWindowSurface *surface = changingw->windowSurface(); + + changingw->opaque = surface->isOpaque(); + + QRegion r; + if (surface->isRegionReserved()) + r = reserve_region(changingw, region); + else + r = region; + + if (!region.isEmpty()) { + if (changingw->isVisible()) + windowState = QWSWindow::ChangingGeometry; + else + windowState = QWSWindow::Showing; + } + changingw->d->state = windowState; + + if (!r.isEmpty() && wasOpaque != changingw->opaque && surface->isBuffered()) + changingw->requested_region = QRegion(); // XXX: force update_regions + + const QRegion oldAllocated = changingw->allocatedRegion(); + setWindowRegion(changingw, r); + if (oldAllocated == changingw->allocatedRegion()) { + // Always send region event to the requesting window even if the + // region didn't change. This is necessary as the client will reset + // the clip region until an event is received. + changingw->client()->sendRegionEvent(wid, changingw->allocatedRegion(), + QWSRegionEvent::Allocation); + } + + surface->QWindowSurface::setGeometry(r.boundingRect()); + + if (windowState == QWSWindow::Showing) + emit q->windowEvent(changingw, QWSServer::Show); + else if (windowState == QWSWindow::ChangingGeometry) + emit q->windowEvent(changingw, QWSServer::Geometry); + if (windowState == QWSWindow::Hiding) { + handleWindowClose(changingw); + changingw->d->state = QWSWindow::Hidden; + changingw->d->painted = false; + } else { + changingw->d->state = QWSWindow::Visible; + } +} + +void QWSServerPrivate::destroy_region(const QWSRegionDestroyCommand *cmd) +{ + invokeRegionDestroy(cmd, clientMap.value(-1)); +} + +void QWSServerPrivate::name_region(const QWSRegionNameCommand *cmd) +{ + invokeRegionName(cmd, clientMap.value(-1)); +} + +#ifndef QT_NO_QWS_INPUTMETHODS +void QWSServerPrivate::im_response(const QWSIMResponseCommand *cmd) + { + invokeIMResponse(cmd, clientMap.value(-1)); +} + +void QWSServerPrivate::im_update(const QWSIMUpdateCommand *cmd) +{ + invokeIMUpdate(cmd, clientMap.value(-1)); +} + +void QWSServerPrivate::send_im_mouse(const QWSIMMouseCommand *cmd) +{ + if (current_IM) + current_IM->mouseHandler(cmd->simpleData.index, cmd->simpleData.state); +} +#endif + +void QWSServerPrivate::openDisplay() +{ + qt_init_display(); + +// rgnMan = qt_fbdpy->regionManager(); + swidth = qt_screen->deviceWidth(); + sheight = qt_screen->deviceHeight(); +} + +void QWSServerPrivate::closeDisplay() +{ + qt_screen->shutdownDevice(); +} + +/*! + Returns the brush used as background in the absence of obscuring + windows. + + \sa setBackground() +*/ +const QBrush &QWSServer::backgroundBrush() const +{ + return *QWSServerPrivate::bgBrush; +} + +/*! + Sets the brush used as background in the absence of obscuring + windows, to be the given \a brush. + + Note that this function can only be used in the server process. + + \sa backgroundBrush() +*/ +void QWSServer::setBackground(const QBrush &brush) +{ + if (!QWSServerPrivate::bgBrush) + QWSServerPrivate::bgBrush = new QBrush(brush); + else + *QWSServerPrivate::bgBrush = brush; + if (!qwsServer) + return; + qt_screen->exposeRegion(QRect(0,0,qt_screen->width(), qt_screen->height()), 0); +} + + +#ifdef QT3_SUPPORT +/*! + \fn void QWSServer::setDesktopBackground(const QImage &image) + + Sets the image used as background in the absence of obscuring + windows, to be the given \a image. + + Use the setBackground() function instead. + + \oldcode + QImage image; + setDesktopBackground(image); + \newcode + QImage image; + setBackground(QBrush(image)); + \endcode +*/ +void QWSServer::setDesktopBackground(const QImage &img) +{ + if (img.isNull()) + setBackground(Qt::NoBrush); + else + setBackground(QBrush(QPixmap::fromImage(img))); +} + +/*! + \fn void QWSServer::setDesktopBackground(const QColor &color) + \overload + + Sets the color used as background in the absence of obscuring + windows, to be the given \a color. + + Use the setBackground() function instead. + + \oldcode + QColor color; + setDesktopBackground(color); + \newcode + QColor color; + setBackground(QBrush(color)); + \endcode +*/ +void QWSServer::setDesktopBackground(const QColor &c) +{ + setDesktopBackground(QBrush(c)); +} +#endif //QT3_SUPPORT + +/*! + \internal + */ +void QWSServer::startup(int flags) +{ + if (qwsServer) + return; + unlink(qws_qtePipeFilename().toLatin1().constData()); + (void)new QWSServer(flags); +} + +/*! + \internal +*/ + +void QWSServer::closedown() +{ + unlink(qws_qtePipeFilename().toLatin1().constData()); + delete qwsServer; + qwsServer = 0; +} + +void QWSServerPrivate::emergency_cleanup() +{ +#ifndef QT_NO_QWS_KEYBOARD + if (qwsServer) + qwsServer->closeKeyboard(); +#endif +} + +#ifndef QT_NO_QWS_KEYBOARD +static QList<QWSServer::KeyboardFilter*> *keyFilters = 0; + +/*! + Processes the given key event. The key is identified by its \a + unicode value and the given \a keycode, \a modifiers, \a isPress + and \a autoRepeat parameters. + + The \a keycode parameter is the Qt keycode value as defined by the + Qt::Key enum. The \a modifiers is an OR combination of + Qt::KeyboardModifier values, indicating whether \gui + Shift/Alt/Ctrl keys are pressed. The \a isPress parameter is true + if the event is a key press event and \a autoRepeat is true if the + event is caused by an auto-repeat mechanism and not an actual key + press. + + This function is typically called internally by keyboard drivers. + Note that this function can only be used in the server process. + + \sa sendKeyEvent(), {Qt for Embedded Linux Character Input} +*/ +void QWSServer::processKeyEvent(int unicode, int keycode, Qt::KeyboardModifiers modifiers, + bool isPress, bool autoRepeat) +{ + bool block; + // Don't block the POWER or LIGHT keys + if ( keycode == Qt::Key_F34 || keycode == Qt::Key_F35 ) + block = false; + else + block = qwsServerPrivate->screensaverblockevent(KEY, qwsServerPrivate->screensaverinterval, isPress); + +#ifdef EVENT_BLOCK_DEBUG + qDebug() << "processKeyEvent" << unicode << keycode << modifiers << isPress << autoRepeat << (block?"block":"pass"); +#endif + + // If we press a key and it's going to be blocked, wake up the screen + if ( block && isPress ) + qwsServerPrivate->_q_screenSaverWake(); + + if ( block ) + return; + + if (keyFilters) { + for (int i = 0; i < keyFilters->size(); ++i) { + QWSServer::KeyboardFilter *keyFilter = keyFilters->at(i); + if (keyFilter->filter(unicode, keycode, modifiers, isPress, autoRepeat)) + return; + } + } + sendKeyEvent(unicode, keycode, modifiers, isPress, autoRepeat); +} + +/*! + \fn void QWSServer::addKeyboardFilter(KeyboardFilter *filter) + + Activates the given keyboard \a filter all key events generated by + physical keyboard drivers (i.e., events sent using the + processKeyEvent() function). + + Note that the filter is not invoked for keys generated by \e + virtual keyboard drivers (i.e., events sent using the + sendKeyEvent() function). + + Note that this function can only be used in the server process. + + \sa removeKeyboardFilter() +*/ +void QWSServer::addKeyboardFilter(KeyboardFilter *f) +{ + if (!keyFilters) + keyFilters = new QList<QWSServer::KeyboardFilter*>; + if (f) { + keyFilters->prepend(f); + } +} + +/* +//####### + We should probably obsolete the whole keyboard filter thing since + it's not useful for input methods anyway + + We could do removeKeyboardFilter(KeyboardFilter *f), but + the "remove and delete the filter" concept does not match "user + remembers the pointer". +*/ + +/*! + Removes and deletes the most recently added filter. + + Note that the programmer is responsible for removing each added + keyboard filter. + + Note that this function can only be used in the server process. + + \sa addKeyboardFilter() +*/ +void QWSServer::removeKeyboardFilter() +{ + if (!keyFilters || keyFilters->isEmpty()) + return; + delete keyFilters->takeAt(0); +} +#endif // QT_NO_QWS_KEYBOARD + +/*! + \fn void QWSServer::setScreenSaverIntervals(int* intervals) + + Specifies the time \a intervals (in milliseconds) between the + different levels of screen responsiveness. + + \l{Qt for Embedded Linux} supports multilevel screen saving, i.e., it is + possible to specify several different levels of screen + responsiveness by implementing the QWSScreenSaver::save() + function. For example, you can choose to first turn off the light + before you fully activate the screensaver. See the QWSScreenSaver + documentation for details. + + Note that an interval of 0 milliseconds will turn off the + screensaver, and that the \a intervals array must be 0-terminated. + This function can only be used in the server process. + + \sa setScreenSaverInterval(), setScreenSaverBlockLevel() +*/ +void QWSServer::setScreenSaverIntervals(int* ms) +{ + if (!qwsServerPrivate) + return; + + delete [] qwsServerPrivate->screensaverintervals; + if (ms) { + int* t=ms; + int n=0; + while (*t++) n++; + if (n) { + n++; // the 0 + qwsServerPrivate->screensaverintervals = new int[n]; + memcpy(qwsServerPrivate->screensaverintervals, ms, n*sizeof(int)); + } else { + qwsServerPrivate->screensaverintervals = 0; + } + } else { + qwsServerPrivate->screensaverintervals = 0; + } + qwsServerPrivate->screensaverinterval = 0; + + qwsServerPrivate->screensavertimer->stop(); + qt_screen->blank(false); + qwsServerPrivate->_q_screenSaverWake(); +} + +/*! + \fn void QWSServer::setScreenSaverInterval(int milliseconds) + + Sets the timeout interval for the screensaver to the specified \a + milliseconds. To turn off the screensaver, set the timout interval + to 0. + + Note that this function can only be used in the server process. + + \sa setScreenSaverIntervals(), setScreenSaverBlockLevel() +*/ +void QWSServer::setScreenSaverInterval(int ms) +{ + int v[2]; + v[0] = ms; + v[1] = 0; + setScreenSaverIntervals(v); +} + +/*! + Block the key or mouse event that wakes the system from level \a eventBlockLevel or higher. + To completely disable event blocking (the default behavior), set \a eventBlockLevel to -1. + + The algorithm blocks the "down", "up" as well as any "repeat" events for the same key + but will not block other key events after the initial "down" event. For mouse events, the + algorithm blocks all mouse events until an event with no buttons pressed is received. + + There are 2 keys that are never blocked, Qt::Key_F34 (POWER) and Qt::Key_F35 (LIGHT). + + Example usage: + + \snippet doc/src/snippets/code/src_gui_embedded_qwindowsystem_qws.cpp 0 + + Note that this function can only be used in the server process. + + \sa setScreenSaverIntervals(), setScreenSaverInterval() +*/ +void QWSServer::setScreenSaverBlockLevel(int eventBlockLevel) +{ + if (!qwsServerPrivate) + return; + qwsServerPrivate->screensavereventblocklevel = eventBlockLevel; +#ifdef EVENT_BLOCK_DEBUG + qDebug() << "QWSServer::setScreenSaverBlockLevel() " << eventBlockLevel; +#endif +} + +extern bool qt_disable_lowpriority_timers; //in qeventloop_unix.cpp + +void QWSServerPrivate::_q_screenSaverWake() +{ + if (screensaverintervals) { + if (screensaverinterval != screensaverintervals) { + if (saver) saver->restore(); + screensaverinterval = screensaverintervals; + screensaverblockevents = false; + } else { + if (!screensavertimer->isActive()) { + qt_screen->blank(false); + if (saver) saver->restore(); + } + } + screensavertimer->start(*screensaverinterval); + screensavertime.start(); + } + qt_disable_lowpriority_timers=false; +} + +void QWSServerPrivate::_q_screenSaverSleep() +{ + qt_screen->blank(true); +#if !defined(QT_QWS_IPAQ) && !defined(QT_QWS_EBX) + screensavertimer->stop(); +#else + if (screensaverinterval) { + screensavertimer->start(*screensaverinterval); + screensavertime.start(); + } else { + screensavertimer->stop(); + } +#endif + qt_disable_lowpriority_timers=true; +} + +/*! + \fn void QWSServer::setScreenSaver(QWSScreenSaver* screenSaver) + + Installs the given \a screenSaver, deleting the current screen + saver. + + Note that this function can only be used in the server process. + + \sa screenSaverActivate(), setScreenSaverInterval(), setScreenSaverIntervals(), setScreenSaverBlockLevel() +*/ +void QWSServer::setScreenSaver(QWSScreenSaver* ss) +{ + QWSServerPrivate *qd = qwsServer->d_func(); + delete qd->saver; + qd->saver = ss; +} + +void QWSServerPrivate::screenSave(int level) +{ + if (saver) { + // saver->save() may call QCoreApplication::processEvents, + // block event before calling saver->save(). + bool oldScreensaverblockevents = screensaverblockevents; + if (*screensaverinterval >= 1000) { + screensaverblockevents = (screensavereventblocklevel >= 0 && screensavereventblocklevel <= level); +#ifdef EVENT_BLOCK_DEBUG + if (screensaverblockevents) + qDebug("ready to block events"); +#endif + } + int *oldScreensaverinterval = screensaverinterval; + if (saver->save(level)) { + // only update screensaverinterval if it hasn't already changed + if (oldScreensaverinterval == screensaverinterval) { + if (screensaverinterval && screensaverinterval[1]) { + screensavertimer->start(*++screensaverinterval); + screensavertime.start(); + } else { + screensaverinterval = 0; + } + } + } else { + // restore previous state + screensaverblockevents = oldScreensaverblockevents; + + // for some reason, the saver don't want us to change to the + // next level, so we'll stay at this level for another interval + if (screensaverinterval && *screensaverinterval) { + screensavertimer->start(*screensaverinterval); + screensavertime.start(); + } + } + } else { + screensaverinterval = 0;//screensaverintervals; + screensaverblockevents = false; + _q_screenSaverSleep(); + } +} + +void QWSServerPrivate::_q_screenSaverTimeout() +{ + if (screensaverinterval) { + if (screensavertime.elapsed() > *screensaverinterval*2) { + // bogus (eg. unsuspend, system time changed) + _q_screenSaverWake(); // try again + return; + } + screenSave(screensaverinterval - screensaverintervals); + } +} + +/*! + Returns true if the screen saver is active; otherwise returns + false. + + Note that this function can only be used in the server process. + + \sa screenSaverActivate() +*/ +bool QWSServer::screenSaverActive() +{ + return qwsServerPrivate->screensaverinterval + && !qwsServerPrivate->screensavertimer->isActive(); +} + +/*! + \internal +*/ +void QWSServer::updateWindowRegions() const +{ + qwsServerPrivate->update_regions(); +} + +/*! + Activates the screen saver if \a activate is true; otherwise it is + deactivated. + + Note that this function can only be used in the server process. + + \sa screenSaverActive(), setScreenSaver() +*/ +void QWSServer::screenSaverActivate(bool activate) +{ + if (activate) + qwsServerPrivate->_q_screenSaverSleep(); + else + qwsServerPrivate->_q_screenSaverWake(); +} + +void QWSServerPrivate::disconnectClient(QWSClient *c) +{ + QTimer::singleShot(0, c, SLOT(closeHandler())); +} + +void QWSServerPrivate::updateClientCursorPos() +{ + Q_Q(QWSServer); + QWSWindow *win = qwsServerPrivate->mouseGrabber ? qwsServerPrivate->mouseGrabber : qwsServer->windowAt(QWSServer::mousePosition); + QWSClient *winClient = win ? win->client() : 0; + if (winClient && winClient != cursorClient) + q->sendMouseEvent(QWSServer::mousePosition, mouseState); +} + +#ifndef QT_NO_QWS_INPUTMETHODS + +/*! + \class QWSInputMethod + \preliminary + \ingroup qws + + \brief The QWSInputMethod class provides international input methods + in Qt for Embedded Linux. + + Note that this class is only available in \l{Qt for Embedded Linux}. + + A \l{Qt for Embedded Linux} application requires a server application to be + running, or to be the server application itself. All system + generated events, including keyboard and mouse events, are passed + to the server application which then propagates the event to the + appropriate client. + + An input method consists of a filter and optionally a graphical + interface, and is used to filter input events between the server + and the client application. + + \tableofcontents + + \section1 Creating Custom Input Methods + + To implement a custom input method, derive from the QWSInputMethod + class, and use the server's \l + {QWSServer::}{setCurrentInputMethod()} function to install it. + + When subclassing QWSInputMethod, you can reimplement the filter() + functions to handle input from both physical and virtual keyboards + as well as mouse devices. Note that the default implementations do + nothing. Use the setInputResolution() function to control the + number of bits shifted when filtering mouse input, i.e., when + going from pointer resolution to screen resolution (the current + resolution can be retrieved using the inputResolutionShift() + function). + + Reimplement the reset() function to restore the state of the input + method. Note that the default implementation calls the sendEvent() + function with empty preedit and commit strings if the input method + is in compose mode (i.e., if the input method is actively + composing a preedit string). + + To receive replies to an input method query (sent using the + sendQuery() function), you must reimplement the queryResponse() + function, while the mouseHandler() function must be reimplemented + if you want to handle mouse events within the preedit + text. Reimplement the updateHandler() function to handle update + events including resets and focus changes. The UpdateType enum + describes the various types of update events recognized by the + input method. + + \section1 Using Input Methods + + In addition to the filter(), reset(), queryResponse(), + mouseHandler() and updateHandler() function mentioned in the + previous section, the QWSInputMethod provides several other + functions helping the window system to manage the installed input + methods. + + The sendEvent() function sends the given event to the focus + widget, while the sendPreeditString() function sends the given + preedit text (encapsulated by an event). QWSInputMethod also + provides the sendCommitString() convenience function which sends + an event encapsulating the given commit string to the current + focus widget, and the sendMouseEvent() function which sends the + given mouse event. + + Finally, the QWSInputMethod class provides the sendQuery() + function for sending input method queries. This function + encapsulates the event with a QWSEvent instance of the \l + {QWSEvent::}{IMQuery} type. + + \sa QWSServer, {Qt for Embedded Linux Architecture} +*/ + +/*! + Constructs a new input method. + + Use the QWSServer::setCurrentInputMethod() function to install it. +*/ + +QWSInputMethod::QWSInputMethod() +{ + +} + +/*! + Destroys this input method, uninstalling it if it is installed. +*/ +QWSInputMethod::~QWSInputMethod() +{ + if (current_IM == this) + current_IM = 0; +} + +/*! + Filters the key input identified by the given \a unicode, \a + keycode, \a modifiers, \a isPress and \a autoRepeat parameters. + + Note that the default implementation does nothing; reimplement + this function to handle input from both physical and virtual + devices. + + The \a keycode is a Qt::Key value, and the \a modifiers is an OR + combination of Qt::KeyboardModifiers. The \a isPress parameter is + telling whether the input is a key press or key release, and the + \a autoRepeat parameter determines whether the input is + autorepeated ( i.e., in which case the + QWSKeyboardHandler::beginAutoRepeat() function has been called). + + To block the event from further processing, return true when + reimplementing this function; the default implementation returns + false. + + \sa setInputResolution(), inputResolutionShift() +*/ +bool QWSInputMethod::filter(int unicode, int keycode, int modifiers, bool isPress, bool autoRepeat) +{ + Q_UNUSED(unicode); + Q_UNUSED(keycode); + Q_UNUSED(modifiers); + Q_UNUSED(isPress); + Q_UNUSED(autoRepeat); + return false; +} + +/*! + \overload + + Filters the mouse input identified by the given \a position, \a + state, and \a wheel parameters. +*/ +bool QWSInputMethod::filter(const QPoint &position, int state, int wheel) +{ + Q_UNUSED(position); + Q_UNUSED(state); + Q_UNUSED(wheel); + return false; +} + +/*! + Resets the state of the input method. + + If the input method is in compose mode, i.e., the input method is + actively composing a preedit string, the default implementation + calls sendEvent() with empty preedit and commit strings; otherwise + it does nothing. Reimplement this function to alter this behavior. + + \sa sendEvent() +*/ +void QWSInputMethod::reset() +{ + if (current_IM_composing_win) { + QInputMethodEvent ime; + sendEvent(&ime); + } +} + +/*! + \enum QWSInputMethod::UpdateType + + This enum describes the various types of update events recognized + by the input method. + + \value Update The input widget is updated in some way; use sendQuery() with + Qt::ImMicroFocus as an argument for more information. + \value FocusIn A new input widget receives focus. + \value FocusOut The input widget loses focus. + \value Reset The input method should be reset. + \value Destroyed The input widget is destroyed. + + \sa updateHandler() +*/ + +/*! + Handles update events including resets and focus changes. The + update events are specified by the given \a type which is one of + the UpdateType enum values. + + Note that reimplementations of this function must call the base + implementation for all cases that it does not handle itself. + + \sa UpdateType +*/ +void QWSInputMethod::updateHandler(int type) +{ + switch (type) { + case FocusOut: + case Reset: + reset(); + break; + + default: + break; + } +} + + +/*! + Receive replies to an input method query. + + Note that the default implementation does nothing; reimplement + this function to receive such replies. + + Internally, an input method query is passed encapsulated by an \l + {QWSEvent::IMQuery}{IMQuery} event generated by the sendQuery() + function. The queried property and the result is passed in the \a + property and \a result parameters. + + \sa sendQuery(), QWSServer::sendIMQuery() +*/ +void QWSInputMethod::queryResponse(int property, const QVariant &result) +{ + Q_UNUSED(property); + Q_UNUSED(result); +} + + + +/*! + \fn void QWSInputMethod::mouseHandler(int offset, int state) + + Handles mouse events within the preedit text. + + Note that the default implementation resets the input method on + all mouse presses; reimplement this function to alter this + behavior. + + The \a offset parameter specifies the position of the mouse event + within the string, and \a state specifies the type of the mouse + event as described by the QWSServer::IMMouse enum. If \a state is + less than 0, the mouse event is inside the associated widget, but + outside the preedit text. When clicking in a different widget, the + \a state is QWSServer::MouseOutside. + + \sa sendPreeditString(), reset() +*/ +void QWSInputMethod::mouseHandler(int, int state) +{ + if (state == QWSServer::MousePress || state == QWSServer::MouseOutside) + reset(); +} + + +/*! + Sends an event encapsulating the given \a preeditString, to the + focus widget. + + The specified \a selectionLength is the number of characters to be + marked as selected (starting at the given \a cursorPosition). If + \a selectionLength is negative, the text \e before \a + cursorPosition is marked. + + The preedit string is marked with QInputContext::PreeditFormat, + and the selected part is marked with + QInputContext::SelectionFormat. + + Sending an input method event with a non-empty preedit string will + cause the input method to enter compose mode. Sending an input + method event with an empty preedit string will cause the input + method to leave compose mode, i.e., the input method will no longer + be actively composing the preedit string. + + Internally, the event is represented by a QWSEvent object of the + \l {QWSEvent::IMEvent}{IMEvent} type. + + \sa sendEvent(), sendCommitString() +*/ + +void QWSInputMethod::sendPreeditString(const QString &preeditString, int cursorPosition, int selectionLength) +{ + QList<QInputMethodEvent::Attribute> attributes; + + int selPos = cursorPosition; + if (selectionLength == 0) { + selPos = 0; + } else if (selectionLength < 0) { + selPos += selectionLength; + selectionLength = -selectionLength; + } + if (selPos > 0) + attributes += QInputMethodEvent::Attribute(QInputMethodEvent::TextFormat, 0, selPos, + QVariant(int(QInputContext::PreeditFormat))); + + if (selectionLength) + attributes += QInputMethodEvent::Attribute(QInputMethodEvent::TextFormat, selPos, selectionLength, + QVariant(int(QInputContext::SelectionFormat))); + + if (selPos + selectionLength < preeditString.length()) + attributes += QInputMethodEvent::Attribute(QInputMethodEvent::TextFormat, + selPos + selectionLength, + preeditString.length() - selPos - selectionLength, + QVariant(int(QInputContext::PreeditFormat))); + + attributes += QInputMethodEvent::Attribute(QInputMethodEvent::Cursor, cursorPosition, 0, QVariant()); + + QInputMethodEvent ime(preeditString, attributes); + qwsServer->sendIMEvent(&ime); +} + +/*! + \fn void QWSInputMethod::sendCommitString(const QString &commitString, int replaceFromPosition, int replaceLength) + + Sends an event encapsulating the given \a commitString, to the + focus widget. + + Note that this will cause the input method to leave compose mode, + i.e., the input method will no longer be actively composing the + preedit string. + + If the specified \a replaceLength is greater than 0, the commit + string will replace the given number of characters of the + receiving widget's previous text, starting at the given \a + replaceFromPosition relative to the start of the current preedit + string. + + Internally, the event is represented by a QWSEvent object of the + \l {QWSEvent::IMEvent}{IMEvent} type. + + \sa sendEvent(), sendPreeditString() +*/ +void QWSInputMethod::sendCommitString(const QString &commitString, int replaceFrom, int replaceLength) +{ + QInputMethodEvent ime; + ime.setCommitString(commitString, replaceFrom, replaceLength); + qwsServer->sendIMEvent(&ime); +} + +/*! + \fn QWSInputMethod::sendIMEvent(QWSServer::IMState state, const QString &text, int cursorPosition, int selectionLength) + \obsolete + + Sends a QInputMethodEvent object to the focus widget. + + If the specified \a state is QWSServer::IMCompose, \a text is a + preedit string, \a cursorPosition is the cursor's position within + the preedit string, and \a selectionLength is the number of + characters (starting at \a cursorPosition) that should be marked + as selected by the input widget receiving the event. If the + specified \a state is QWSServer::IMEnd, \a text is a commit + string. + + Use sendEvent(), sendPreeditString() or sendCommitString() instead. +*/ + +/*! + \fn QWSInputMethod::sendEvent(const QInputMethodEvent *event) + + Sends the given \a event to the focus widget. + + The \c QInputMethodEvent class is derived from QWSEvent, i.e., the + given \a event is a QWSEvent object of the \l + {QWSEvent::IMEvent}{IMEvent} type. + + \sa sendPreeditString(), sendCommitString(), reset() +*/ + + +/*! + \fn void QWSInputMethod::sendQuery(int property) + + Sends an input method query (internally encapsulated by a QWSEvent + of the \l {QWSEvent::IMQuery}{IMQuery} type) for the specified \a + property. + + To receive responses to input method queries, the virtual + queryResponse() function must be reimplemented. + + \sa queryResponse(), QWSServer::sendIMQuery() +*/ + +/*! + Sets and returns the number of bits shifted to go from pointer + resolution to screen resolution when filtering mouse input. + + If \a isHigh is true and the device has a pointer device + resolution twice or more of the screen resolution, the positions + passed to the filter() function will be presented at the higher + resolution; otherwise the resolution will be equal to that of the + screen resolution. + + \sa inputResolutionShift(), filter() +*/ +uint QWSInputMethod::setInputResolution(bool isHigh) +{ + mIResolution = isHigh; + return inputResolutionShift(); +} + +/*! + Returns the number of bits shifted to go from pointer resolution + to screen resolution when filtering mouse input. + + \sa setInputResolution(), filter() +*/ +uint QWSInputMethod::inputResolutionShift() const +{ + return 0; // default for devices with single resolution. +} + +/*! + \fn void QWSInputMethod::sendMouseEvent( const QPoint &position, int state, int wheel ) + + Sends a mouse event specified by the given \a position, \a state + and \a wheel parameters. + + The given \a position will be transformed if the screen + coordinates do not match the pointer device coordinates. + + Note that the event will be not be tested by the active input + method, but calling the QWSServer::sendMouseEvent() function will + make the current input method filter the event. + + \sa mouseHandler(), sendEvent() +*/ +void QWSInputMethod::sendMouseEvent( const QPoint &pos, int state, int wheel ) +{ + if (qt_last_x) { + *qt_last_x = pos.x(); + *qt_last_y = pos.y(); + } + QWSServer::mousePosition = pos; + qwsServerPrivate->mouseState = state; + QWSServerPrivate::sendMouseEventUnfiltered(pos, state, wheel); +} +#endif // QT_NO_QWS_INPUTMETHODS + +/*! + \fn QWSWindow::QWSWindow(int i, QWSClient * client) + \internal + + Constructs a new top-level window, associated with the client \a + client and giving it the id \a i. +*/ + +/*! + \fn QWSServer::windowEvent(QWSWindow * window, QWSServer::WindowEvent eventType) + + This signal is emitted whenever something happens to a top-level + window (e.g., it's created or destroyed), passing a pointer to the + window and the event's type in the \a window and \a eventType + parameters, respectively. + + \sa markedText() +*/ + +/*! + \class QWSServer::KeyboardFilter + \ingroup qws + + \brief The KeyboardFilter class is a base class for global + keyboard event filters in Qt for Embedded Linux. + + Note that this class is only available in \l{Qt for Embedded Linux}. + + In \l{Qt for Embedded Linux}, all system generated events, including + keyboard events, are passed to the server application which then + propagates the event to the appropriate client. The KeyboardFilter + class is used to implement a global, low-level filter on the + server side. The server applies the filter to all keyboard events + before passing them on to the clients: + + \image qwsserver_keyboardfilter.png + + This feature can, for example, be used to filter things like APM + (advanced power management) suspended from a button without having + to filter for it in all applications. + + To add a new keyboard filter you must first create the filter by + deriving from this class, reimplementing the pure virtual filter() + function. Then you can install the filter on the server using + QWSServer's \l {QWSServer::}{addKeyboardFilter()} + function. QWSServer also provides a \l + {QWSServer::}{removeKeyboardFilter()} function. + + \sa {Qt for Embedded Linux Architecture}, QWSServer, QWSInputMethod +*/ + +/*! + \fn QWSServer::KeyboardFilter::~KeyboardFilter() + + Destroys the keyboard filter. +*/ + +/*! + \fn bool QWSServer::KeyboardFilter::filter(int unicode, int keycode, int modifiers, bool isPress, bool autoRepeat) + + Implement this function to return true if a given key event should + be stopped from being processed any further; otherwise it should + return false. + + A key event can be identified by the given \a unicode value and + the \a keycode, \a modifiers, \a isPress and \a autoRepeat + parameters. + + The \a keycode parameter is the Qt keycode value as defined by the + Qt::Key enum. The \a modifiers is an OR combination of + Qt::KeyboardModifier values, indicating whether \gui + Shift/Alt/Ctrl keys are pressed. The \a isPress parameter is true + if the event is a key press event and \a autoRepeat is true if the + event is caused by an auto-repeat mechanism and not an actual key + press. +*/ + +QT_END_NAMESPACE + +#include "moc_qwindowsystem_qws.cpp" |