diff options
Diffstat (limited to 'src/gui/embedded')
116 files changed, 39139 insertions, 0 deletions
diff --git a/src/gui/embedded/embedded.pri b/src/gui/embedded/embedded.pri new file mode 100644 index 0000000..95c4132 --- /dev/null +++ b/src/gui/embedded/embedded.pri @@ -0,0 +1,226 @@ +# Qt for Embedded Linux + +embedded { + CONFIG -= opengl x11 + LIBS -= -dl + KERNEL_P = kernel + + !mac:HEADERS += embedded/qsoundqss_qws.h + HEADERS += \ + embedded/qcopchannel_qws.h \ + embedded/qdecoration_qws.h \ + embedded/qdecorationfactory_qws.h \ + embedded/qdecorationplugin_qws.h \ + embedded/qdirectpainter_qws.h \ + embedded/qlock_p.h \ + embedded/qscreen_qws.h \ + embedded/qscreenmulti_qws_p.h \ + embedded/qscreenproxy_qws.h \ + embedded/qwindowsystem_qws.h \ + embedded/qwindowsystem_p.h \ + embedded/qwscommand_qws_p.h \ + embedded/qwscursor_qws.h \ + embedded/qwsdisplay_qws.h \ + embedded/qwsdisplay_qws_p.h \ + embedded/qwsevent_qws.h \ + embedded/qwsmanager_qws.h \ + embedded/qwsmanager_p.h \ + embedded/qwsproperty_qws.h \ + embedded/qwsprotocolitem_qws.h \ + embedded/qtransportauth_qws.h \ + embedded/qtransportauth_qws_p.h \ + embedded/qtransportauthdefs_qws.h \ + embedded/qwssocket_qws.h \ + embedded/qwslock_p.h \ + embedded/qwsutils_qws.h \ + embedded/qwssharedmemory_p.h \ + embedded/qwssignalhandler_p.h \ + embedded/qwsembedwidget.h + + !mac:SOURCES += embedded/qsoundqss_qws.cpp + SOURCES += \ + embedded/qcopchannel_qws.cpp \ + embedded/qdecoration_qws.cpp \ + embedded/qdecorationfactory_qws.cpp \ + embedded/qdecorationplugin_qws.cpp \ + embedded/qdirectpainter_qws.cpp \ + embedded/qlock.cpp \ + embedded/qscreen_qws.cpp \ + embedded/qscreenmulti_qws.cpp \ + embedded/qscreenproxy_qws.cpp \ + embedded/qwindowsystem_qws.cpp \ + embedded/qwscommand_qws.cpp \ + embedded/qwscursor_qws.cpp \ + embedded/qwsevent_qws.cpp \ + embedded/qwsmanager_qws.cpp \ + embedded/qwsproperty_qws.cpp \ + embedded/qtransportauth_qws.cpp \ + embedded/qwslock.cpp \ + embedded/qwssharedmemory.cpp \ + embedded/qwssocket_qws.cpp \ + embedded/qwssignalhandler.cpp \ + embedded/qwsembedwidget.cpp + + contains(QT_CONFIG,sxe)|contains(QT_CONFIG,qtopia) { + SOURCES += embedded/qunixsocket.cpp embedded/qunixsocketserver.cpp + HEADERS += embedded/qunixsocket_p.h embedded/qunixsocketserver_p.h + } + +# +# Decorations +# + contains( decorations, default ) { + HEADERS += embedded/qdecorationdefault_qws.h + SOURCES += embedded/qdecorationdefault_qws.cpp + } + contains( decorations, styled ) { + HEADERS += embedded/qdecorationstyled_qws.h + SOURCES += embedded/qdecorationstyled_qws.cpp + } + + contains( decorations, windows ) { + HEADERS += embedded/qdecorationwindows_qws.h + SOURCES += embedded/qdecorationwindows_qws.cpp + } + +# +# Qt for Embedded Linux Drivers +# + HEADERS += embedded/qscreendriverplugin_qws.h \ + embedded/qscreendriverfactory_qws.h \ + embedded/qkbd_qws.h \ + embedded/qkbddriverplugin_qws.h \ + embedded/qkbddriverfactory_qws.h \ + embedded/qmouse_qws.h \ + embedded/qmousedriverplugin_qws.h \ + embedded/qmousedriverfactory_qws.h + + SOURCES += embedded/qscreendriverplugin_qws.cpp \ + embedded/qscreendriverfactory_qws.cpp \ + embedded/qkbd_qws.cpp \ + embedded/qkbddriverplugin_qws.cpp \ + embedded/qkbddriverfactory_qws.cpp \ + embedded/qmouse_qws.cpp \ + embedded/qmousedriverplugin_qws.cpp \ + embedded/qmousedriverfactory_qws.cpp + +# +# Graphics drivers +# + contains( gfx-drivers, linuxfb ) { + HEADERS += embedded/qscreenlinuxfb_qws.h + SOURCES += embedded/qscreenlinuxfb_qws.cpp + } + + contains( gfx-drivers, qvfb ) { + HEADERS += embedded/qscreenvfb_qws.h + SOURCES += embedded/qscreenvfb_qws.cpp + } + + contains( gfx-drivers, vnc ) { + VNCDIR = $$QT_SOURCE_TREE/src/plugins/gfxdrivers/vnc + INCLUDEPATH += $$VNCDIR + HEADERS += $$VNCDIR/qscreenvnc_qws.h \ + $$VNCDIR/qscreenvnc_p.h + SOURCES += $$VNCDIR/qscreenvnc_qws.cpp + } + + contains( gfx-drivers, transformed ) { + HEADERS += embedded/qscreentransformed_qws.h + SOURCES += embedded/qscreentransformed_qws.cpp + } + +# +# Keyboard drivers +# + contains( kbd-drivers, qvfb ) { + HEADERS +=embedded/qkbdvfb_qws.h + SOURCES +=embedded/qkbdvfb_qws.cpp + !contains( kbd-drivers, qvfb ) { + kbd-drivers += qvfb + } + } + + contains( kbd-drivers, sl5000 ) { + HEADERS +=embedded/qkbdsl5000_qws.h + SOURCES +=embedded/qkbdsl5000_qws.cpp + !contains( kbd-drivers, tty ) { + kbd-drivers += tty + } + } + + contains( kbd-drivers, tty ) { + HEADERS +=embedded/qkbdtty_qws.h + SOURCES +=embedded/qkbdtty_qws.cpp + !contains( kbd-drivers, pc101 ) { + kbd-drivers += pc101 + } + } + + contains( kbd-drivers, usb ) { + HEADERS +=embedded/qkbdusb_qws.h + SOURCES +=embedded/qkbdusb_qws.cpp + !contains( kbd-drivers, pc101 ) { + kbd-drivers += pc101 + } + } + + contains( kbd-drivers, um ) { + HEADERS +=embedded/qkbdum_qws.h + SOURCES +=embedded/qkbdum_qws.cpp + } + + contains( kbd-drivers, pc101 ) { + HEADERS +=embedded/qkbdpc101_qws.h + SOURCES +=embedded/qkbdpc101_qws.cpp + } + + contains( kbd-drivers, yopy ) { + HEADERS +=embedded/qkbdyopy_qws.h + SOURCES +=embedded/qkbdyopy_qws.cpp + } + + contains( kbd-drivers, vr41xx ) { + HEADERS +=embedded/qkbdvr41xx_qws.h + SOURCES +=embedded/qkbdvr41xx_qws.cpp + } + +# +# Mouse drivers +# + contains( mouse-drivers, qvfb ) { + HEADERS +=embedded/qmousevfb_qws.h + SOURCES +=embedded/qmousevfb_qws.cpp + } + + contains( mouse-drivers, pc ) { + HEADERS +=embedded/qmousepc_qws.h + SOURCES +=embedded/qmousepc_qws.cpp + } + + contains( mouse-drivers, bus ) { + HEADERS +=embedded/qmousebus_qws.h + SOURCES +=embedded/qmousebus_qws.cpp + } + + contains( mouse-drivers, linuxtp ) { + HEADERS +=embedded/qmouselinuxtp_qws.h + SOURCES +=embedded/qmouselinuxtp_qws.cpp + } + + contains( mouse-drivers, vr41xx ) { + HEADERS +=embedded/qmousevr41xx_qws.h + SOURCES +=embedded/qmousevr41xx_qws.cpp + } + + contains( mouse-drivers, yopy ) { + HEADERS +=embedded/qmouseyopy_qws.h + SOURCES +=embedded/qmouseyopy_qws.cpp + } + + contains( mouse-drivers, tslib ) { + LIBS += -lts + HEADERS +=embedded/qmousetslib_qws.h + SOURCES +=embedded/qmousetslib_qws.cpp + } +} diff --git a/src/gui/embedded/qcopchannel_qws.cpp b/src/gui/embedded/qcopchannel_qws.cpp new file mode 100644 index 0000000..00cf5dc --- /dev/null +++ b/src/gui/embedded/qcopchannel_qws.cpp @@ -0,0 +1,608 @@ +/**************************************************************************** +** +** 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 "qcopchannel_qws.h" + +#ifndef QT_NO_COP + +#include "qwsdisplay_qws.h" +#include "qwscommand_qws_p.h" +#include "qwindowsystem_qws.h" +#include "qwindowsystem_p.h" +#include "qlist.h" +#include "qmap.h" +#include "qdatastream.h" +#include "qpointer.h" +#include "qmutex.h" + +#include "qdebug.h" + +QT_BEGIN_NAMESPACE + +typedef QMap<QString, QList<QWSClient*> > QCopServerMap; +static QCopServerMap *qcopServerMap = 0; + +class QCopServerRegexp +{ +public: + QCopServerRegexp( const QString& channel, QWSClient *client ); + QCopServerRegexp( const QCopServerRegexp& other ); + + QString channel; + QWSClient *client; + QRegExp regexp; +}; + +QCopServerRegexp::QCopServerRegexp( const QString& channel, QWSClient *client ) +{ + this->channel = channel; + this->client = client; + this->regexp = QRegExp( channel, Qt::CaseSensitive, QRegExp::Wildcard ); +} + +QCopServerRegexp::QCopServerRegexp( const QCopServerRegexp& other ) +{ + channel = other.channel; + client = other.client; + regexp = other.regexp; +} + +typedef QList<QCopServerRegexp> QCopServerRegexpList; +static QCopServerRegexpList *qcopServerRegexpList = 0; + +typedef QMap<QString, QList< QPointer<QCopChannel> > > QCopClientMap; +static QCopClientMap *qcopClientMap = 0; + +Q_GLOBAL_STATIC(QMutex, qcopClientMapMutex) + +// Determine if a channel name contains wildcard characters. +static bool containsWildcards( const QString& channel ) +{ + return channel.contains(QLatin1Char('*')); +} + +class QCopChannelPrivate +{ +public: + QString channel; +}; + +/*! + \class QCopChannel + \ingroup qws + + \brief The QCopChannel class provides communication capabilities + between clients in \l{Qt for Embedded Linux}. + + Note that this class is only available in \l{Qt for Embedded Linux}. + + The Qt COmmunication Protocol (QCOP) is a many-to-many protocol + for transferring messages across registered channels. A channel is + registered by name, and anyone who wants to can listen to the + channel as well as send messages through it. The QCOP protocol + allows clients to communicate both within the same address space + and between different processes. + + To send messages to a given channel, QCopChannel provides the + static send() function. Using this function alone, the messages + are queued until Qt re-enters the event loop. To immediately flush + all queued messages to the registered listeners, call the static + flush() function. + + To listen to the traffic on a given channel, you typically + instantiate a QCopChannel object for the given channel and connect + to its received() signal that is emitted whenever there is + incoming data. Use the static isRegistered() function to query + the server for the existence of a given channel. QCopChannel + provides the channel() function returning the name of this + QCopChannel object's channel. + + In additon, QCopChannel provides the virtual receive() function + that can be reimplemented to filter the incoming messages and + data. The default implementation simply emits the received() + signal. + + \sa QWSServer, QWSClient, {Qt for Embedded Linux Architecture} +*/ + +/*! + Constructs a QCopChannel object for the specified \a channel, with + the given \a parent. Once created, the channel is registered by + the server. + + \sa isRegistered(), channel() +*/ + +QCopChannel::QCopChannel(const QString& channel, QObject *parent) : + QObject(parent) +{ + init(channel); +} + +#ifdef QT3_SUPPORT +/*! + Use the two argument overload instead, and call the + QObject::setObjectName() function to \a name the instance. +*/ +QCopChannel::QCopChannel(const QString& channel, QObject *parent, const char *name) : + QObject(parent) +{ + setObjectName(QString::fromAscii(name)); + init(channel); +} +#endif + +void QCopChannel::init(const QString& channel) +{ + d = new QCopChannelPrivate; + d->channel = channel; + + if (!qt_fbdpy) { + qFatal("QCopChannel: Must construct a QApplication " + "before QCopChannel"); + return; + } + + { + QMutexLocker locker(qcopClientMapMutex()); + + if (!qcopClientMap) + qcopClientMap = new QCopClientMap; + + // do we need a new channel list ? + QCopClientMap::Iterator it = qcopClientMap->find(channel); + if (it != qcopClientMap->end()) { + it.value().append(this); + return; + } + + it = qcopClientMap->insert(channel, QList< QPointer<QCopChannel> >()); + it.value().append(QPointer<QCopChannel>(this)); + } + + // inform server about this channel + qt_fbdpy->registerChannel(channel); +} + +/*! + \internal + + Resend all channel registrations + */ +void QCopChannel::reregisterAll() +{ + if(qcopClientMap) + for(QCopClientMap::Iterator iter = qcopClientMap->begin(); + iter != qcopClientMap->end(); + ++iter) + qt_fbdpy->registerChannel(iter.key()); +} + +/*! + Destroys this QCopChannel object. + + The server is notified that this particular listener has closed + its connection. The server will keep the channel open until the + last registered listener detaches. + + \sa isRegistered(), channel() +*/ + +QCopChannel::~QCopChannel() +{ + QMutexLocker locker(qcopClientMapMutex()); + QCopClientMap::Iterator it = qcopClientMap->find(d->channel); + Q_ASSERT(it != qcopClientMap->end()); + it.value().removeAll(this); + // still any clients connected locally ? + if (it.value().isEmpty()) { + QByteArray data; + QDataStream s(&data, QIODevice::WriteOnly); + s << d->channel; + if (qt_fbdpy) + send(QLatin1String(""), QLatin1String("detach()"), data); + qcopClientMap->remove(d->channel); + } + + delete d; +} + +/*! + Returns the name of this object's channel. + + \sa isRegistered() +*/ + +QString QCopChannel::channel() const +{ + return d->channel; +} + +/*! + \fn void QCopChannel::receive(const QString& message, const QByteArray &data) + + Processes the incoming \a message and \a data. + + This function is called by the server when this object's channel + receives new messages. Note that the default implementation simply + emits the received() signal; reimplement this function to process + the incoming \a message and \a data. + + Note that the format of the given \a data has to be well defined + in order to extract the information it contains. In addition, it + is recommended to use the DCOP convention. This is not a + requirement, but you must ensure that the sender and receiver + agree on the argument types. For example: + + \snippet doc/src/snippets/code/src_gui_embedded_qcopchannel_qws.cpp 0 + + The above code assumes that the \c message is a DCOP-style + function signature and the \c data contains the function's + arguments. + + \sa send(), channel(), received() + */ +void QCopChannel::receive(const QString& msg, const QByteArray &data) +{ + emit received(msg, data); +} + +/*! + \fn void QCopChannel::received(const QString& message, const QByteArray &data) + + This signal is emitted whenever this object's channel receives new + messages (i.e., it is emitted by the receive() function), passing + the incoming \a message and \a data as parameters. + + \sa receive(), channel() +*/ + +/*! + Queries the server for the existence of the given \a channel. Returns true + if the channel is registered; otherwise returns false. + + \sa channel(), send() +*/ + +bool QCopChannel::isRegistered(const QString& channel) +{ + QByteArray data; + QDataStream s(&data, QIODevice::WriteOnly); + s << channel; + if (!send(QLatin1String(""), QLatin1String("isRegistered()"), data)) + return false; + + QWSQCopMessageEvent *e = qt_fbdpy->waitForQCopResponse(); + bool known = e->message == "known"; + delete e; + return known; +} + +/*! + \fn bool QCopChannel::send(const QString& channel, const QString& message) + \overload +*/ + +bool QCopChannel::send(const QString& channel, const QString& msg) +{ + QByteArray data; + return send(channel, msg, data); +} + +/*! + \fn bool QCopChannel::send(const QString& channel, const QString& message, + const QByteArray &data) + + Sends the given \a message on the specified \a channel with the + given \a data. The message will be distributed to all clients + subscribed to the channel. Returns true if the message is sent + successfully; otherwise returns false. + + It is recommended to use the DCOP convention. This is not a + requirement, but you must ensure that the sender and receiver + agree on the argument types. + + Note that QDataStream provides a convenient way to fill the byte + array with auxiliary data. For example: + + \snippet doc/src/snippets/code/src_gui_embedded_qcopchannel_qws.cpp 1 + + In the code above the channel is \c "System/Shell". The \c message + is an arbitrary string, but in the example we've used the DCOP + convention of passing a function signature. Such a signature is + formatted as \c "functionname(types)" where \c types is a list of + zero or more comma-separated type names, with no whitespace, no + consts and no pointer or reference marks, i.e. no "*" or "&". + + \sa receive(), isRegistered() +*/ + +bool QCopChannel::send(const QString& channel, const QString& msg, + const QByteArray &data) +{ + if (!qt_fbdpy) { + qFatal("QCopChannel::send: Must construct a QApplication " + "before using QCopChannel"); + return false; + } + + qt_fbdpy->sendMessage(channel, msg, data); + + return true; +} + +/*! + \since 4.2 + + Flushes all queued messages to the registered listeners. + + Note that this function returns false if no QApplication has been + constructed, otherwise it returns true. + + \sa send() + +*/ +bool QCopChannel::flush() +{ + if (!qt_fbdpy) { + qFatal("QCopChannel::flush: Must construct a QApplication " + "before using QCopChannel"); + return false; + } + + qt_fbdpy->flushCommands(); + + return true; +} + +class QWSServerSignalBridge : public QObject { + Q_OBJECT + +public: + void emitNewChannel(const QString& channel); + void emitRemovedChannel(const QString& channel); + + signals: + void newChannel(const QString& channel); + void removedChannel(const QString& channel); +}; + +void QWSServerSignalBridge::emitNewChannel(const QString& channel){ + emit newChannel(channel); +} + +void QWSServerSignalBridge::emitRemovedChannel(const QString& channel) { + emit removedChannel(channel); +} + +/*! + \internal + Server side: subscribe client \a cl on channel \a ch. +*/ + +void QCopChannel::registerChannel(const QString& ch, QWSClient *cl) +{ + if (!qcopServerMap) + qcopServerMap = new QCopServerMap; + + // do we need a new channel list ? + QCopServerMap::Iterator it = qcopServerMap->find(ch); + if (it == qcopServerMap->end()) + it = qcopServerMap->insert(ch, QList<QWSClient*>()); + + // If the channel name contains wildcard characters, then we also + // register it on the server regexp matching list. + if (containsWildcards( ch )) { + QCopServerRegexp item(ch, cl); + if (!qcopServerRegexpList) + qcopServerRegexpList = new QCopServerRegexpList; + qcopServerRegexpList->append( item ); + } + + // If this is the first client in the channel, announce the channel as being created. + if (it.value().count() == 0) { + QWSServerSignalBridge* qwsBridge = new QWSServerSignalBridge(); + connect(qwsBridge, SIGNAL(newChannel(QString)), qwsServer, SIGNAL(newChannel(QString))); + qwsBridge->emitNewChannel(ch); + delete qwsBridge; + } + + it.value().append(cl); +} + +/*! + \internal + Server side: unsubscribe \a cl from all channels. +*/ + +void QCopChannel::detach(QWSClient *cl) +{ + if (!qcopServerMap) + return; + + QCopServerMap::Iterator it = qcopServerMap->begin(); + for (; it != qcopServerMap->end(); ++it) { + if (it.value().contains(cl)) { + it.value().removeAll(cl); + // If this was the last client in the channel, announce the channel as dead. + if (it.value().count() == 0) { + QWSServerSignalBridge* qwsBridge = new QWSServerSignalBridge(); + connect(qwsBridge, SIGNAL(removedChannel(QString)), qwsServer, SIGNAL(removedChannel(QString))); + qwsBridge->emitRemovedChannel(it.key()); + delete qwsBridge; + } + } + } + + if (!qcopServerRegexpList) + return; + + QCopServerRegexpList::Iterator it2 = qcopServerRegexpList->begin(); + while(it2 != qcopServerRegexpList->end()) { + if ((*it2).client == cl) + it2 = qcopServerRegexpList->erase(it2); + else + ++it2; + } +} + +/*! + \internal + Server side: transmit the message to all clients registered to the + specified channel. +*/ + +void QCopChannel::answer(QWSClient *cl, const QString& ch, + const QString& msg, const QByteArray &data) +{ + // internal commands + if (ch.isEmpty()) { + if (msg == QLatin1String("isRegistered()")) { + QString c; + QDataStream s(data); + s >> c; + bool known = qcopServerMap && qcopServerMap->contains(c) + && !((*qcopServerMap)[c]).isEmpty(); + // Yes, it's a typo, it's not user-visible, and we choose not to fix it for compatibility + QLatin1String ans = QLatin1String(known ? "known" : "unkown"); + QWSServerPrivate::sendQCopEvent(cl, QLatin1String(""), + ans, data, true); + return; + } else if (msg == QLatin1String("detach()")) { + QString c; + QDataStream s(data); + s >> c; + Q_ASSERT(qcopServerMap); + QCopServerMap::Iterator it = qcopServerMap->find(c); + if (it != qcopServerMap->end()) { + //Q_ASSERT(it.value().contains(cl)); + it.value().removeAll(cl); + if (it.value().isEmpty()) { + // If this was the last client in the channel, announce the channel as dead + QWSServerSignalBridge* qwsBridge = new QWSServerSignalBridge(); + connect(qwsBridge, SIGNAL(removedChannel(QString)), qwsServer, SIGNAL(removedChannel(QString))); + qwsBridge->emitRemovedChannel(it.key()); + delete qwsBridge; + qcopServerMap->erase(it); + } + } + if (qcopServerRegexpList && containsWildcards(c)) { + // Remove references to a wildcarded channel. + QCopServerRegexpList::Iterator it + = qcopServerRegexpList->begin(); + while(it != qcopServerRegexpList->end()) { + if ((*it).client == cl && (*it).channel == c) + it = qcopServerRegexpList->erase(it); + else + ++it; + } + } + return; + } + qWarning("QCopChannel: unknown internal command %s", qPrintable(msg)); + QWSServerPrivate::sendQCopEvent(cl, QLatin1String(""), + QLatin1String("bad"), data); + return; + } + + if (qcopServerMap) { + QList<QWSClient*> clist = qcopServerMap->value(ch); + for (int i=0; i < clist.size(); ++i) { + QWSClient *c = clist.at(i); + QWSServerPrivate::sendQCopEvent(c, ch, msg, data); + } + } + + if(qcopServerRegexpList && !containsWildcards(ch)) { + // Search for wildcard matches and forward the message on. + QCopServerRegexpList::ConstIterator it = qcopServerRegexpList->constBegin(); + for (; it != qcopServerRegexpList->constEnd(); ++it) { + if ((*it).regexp.exactMatch(ch)) { + QByteArray newData; + { + QDataStream stream + (&newData, QIODevice::WriteOnly | QIODevice::Append); + stream << ch; + stream << msg; + stream << data; + // Stream is flushed and closed at this point. + } + QWSServerPrivate::sendQCopEvent + ((*it).client, (*it).channel, + QLatin1String("forwardedMessage(QString,QString,QByteArray)"), + newData); + } + } + } +} + +/*! + \internal + Client side: distribute received event to the QCop instance managing the + channel. +*/ +void QCopChannel::sendLocally(const QString& ch, const QString& msg, + const QByteArray &data) +{ + Q_ASSERT(qcopClientMap); + + // filter out internal events + if (ch.isEmpty()) + return; + + // feed local clients with received data + QList< QPointer<QCopChannel> > clients; + { + QMutexLocker locker(qcopClientMapMutex()); + clients = (*qcopClientMap)[ch]; + } + for (int i = 0; i < clients.size(); ++i) { + QCopChannel *channel = (QCopChannel *)clients.at(i); + if ( channel ) + channel->receive(msg, data); + } +} + +QT_END_NAMESPACE + +#include "qcopchannel_qws.moc" + +#endif diff --git a/src/gui/embedded/qcopchannel_qws.h b/src/gui/embedded/qcopchannel_qws.h new file mode 100644 index 0000000..d6e6e6b --- /dev/null +++ b/src/gui/embedded/qcopchannel_qws.h @@ -0,0 +1,108 @@ +/**************************************************************************** +** +** 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$ +** +****************************************************************************/ + +#ifndef QCOPCHANNEL_QWS_H +#define QCOPCHANNEL_QWS_H + +#include <QtCore/qobject.h> + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +QT_MODULE(Gui) + +#ifndef QT_NO_COP + +class QWSClient; +class QCopChannelPrivate; + +class Q_GUI_EXPORT QCopChannel : public QObject +{ + Q_OBJECT +public: + explicit QCopChannel(const QString& channel, QObject *parent=0); +#ifdef QT3_SUPPORT + QT3_SUPPORT_CONSTRUCTOR QCopChannel(const QString& channel, QObject *parent, const char *name); +#endif + virtual ~QCopChannel(); + + QString channel() const; + + static bool isRegistered(const QString& channel); + static bool send(const QString& channel, const QString& msg); + static bool send(const QString& channel, const QString& msg, + const QByteArray &data); + + static bool flush(); + + static void sendLocally( const QString& ch, const QString& msg, + const QByteArray &data); + static void reregisterAll(); + + virtual void receive(const QString& msg, const QByteArray &data); + +Q_SIGNALS: + void received(const QString& msg, const QByteArray &data); + +private: + void init(const QString& channel); + + // server side + static void registerChannel(const QString& ch, QWSClient *cl); + static void detach(QWSClient *cl); + static void answer(QWSClient *cl, const QString& ch, + const QString& msg, const QByteArray &data); + // client side + QCopChannelPrivate* d; + + friend class QWSServer; + friend class QWSServerPrivate; + friend class QApplication; +}; + +#endif // QT_NO_COP + +QT_END_NAMESPACE + +QT_END_HEADER + +#endif // QCOPCHANNEL_QWS_H diff --git a/src/gui/embedded/qdecoration_qws.cpp b/src/gui/embedded/qdecoration_qws.cpp new file mode 100644 index 0000000..4ac1f01 --- /dev/null +++ b/src/gui/embedded/qdecoration_qws.cpp @@ -0,0 +1,404 @@ +/**************************************************************************** +** +** 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 "qdecoration_qws.h" + +#include "qapplication.h" +#include "qdrawutil.h" +#include "qpainter.h" +#include "qregion.h" +#include "qwhatsthis.h" + +#include "qmenu.h" +#include "private/qwidget_p.h" +#include "qwsmanager_qws.h" + +QT_BEGIN_NAMESPACE + +/*! + \class QDecoration + \ingroup qws + + \brief The QDecoration class is a base class for window + decorations in Qt for Embedded Linux + + Note that this class is non-portable and only available in + \l{Qt for Embedded Linux}. + + \l{Qt for Embedded Linux} provides window management of top level windows + and several ready made decorations (i.e., \c Default, \c Styled + and \c Windows). Custom decorations can be implemented by + subclassing the QDecoration class and creating a decoration plugin + (derived from QDecorationPlugin). The default + implementation of the QDecorationFactory class will automatically + detect the plugin, and load the decoration into the application at + run-time using Qt's \l {How to Create Qt Plugins}{plugin + system}. To actually apply a decoration, use the + QApplication::qwsSetDecoration() function. + + When creating a custom decoration, implement the paint() function + to paint the border and title decoration, and the region() + function to return the regions the decoration + occupies. Reimplement the regionClicked() and + regionDoubleClicked() functions to respond to mouse clicks (the + default implementations responds to (single) clicks on items in a + widget's system menu and double clicks on a widget's title). + + QDecoration provides the DecorationRegion enum that describes the + various regions of the window decoration, and the regionAt() + function to determine the region containing a given point. The + QDecoration class also provides the DecorationState enum + describing the state of a given region, e.g. whether it is active + or not. + + In addition, it is possible to build the system menu for a given + top level widget using the buildSysMenu() function; whenever an + action in this menu is triggered, the menuTriggered() function is + called automatically. + + Finally, the QDecoration class provides a couple of static + functions, startMove() and startResize(), which start a move or + resize action by making the appropriate decoration region active + and grabbing the mouse input. + + \sa QDecorationFactory, QDecorationPlugin, {Qt for Embedded Linux + Architecture} +*/ + +/*! + \fn QDecoration::QDecoration() + + Constructs a decoration object. +*/ + +/*! + \fn QDecoration::~QDecoration() + + Destroys this decoration object. +*/ + +/*! + \enum QDecoration::DecorationRegion + + This enum describes the various regions of the window decoration. + + \value All The entire region used by the window decoration. + + \value Top The top border used to vertically resize the window. + \value Bottom The bottom border used to vertically resize the window. + \value Left The left border used to horizontally resize the window. + \value Right The right border used to horizontally resize the window. + \value TopLeft The top-left corner of the window used to resize the + window both horizontally and vertically. + \value TopRight The top-right corner of the window used to resize the + window both horizontally and vertically. + \value BottomLeft The bottom-left corner of the window used to resize the + window both horizontally and vertically. + \value BottomRight The bottom-right corner of the window used to resize the + window both horizontally and vertically. + \value Borders All the regions used to describe the window's borders. + + \value Title The region containing the window title, used + to move the window by dragging with the mouse cursor. + \value Close The region occupied by the close button. Clicking in this + region closes the window. + \value Minimize The region occupied by the minimize button. Clicking in + this region minimizes the window. + \value Maximize The region occupied by the maximize button. Clicking in + this region maximizes the window. + \value Normalize The region occupied by a button used to restore a window's + normal size. Clicking in this region restores a maximized + window to its previous size. The region used for this + button is often also the Maximize region. + \value Menu The region occupied by the window's menu button. Clicking + in this region opens the window operations (system) menu. + \value Help The region occupied by the window's help button. Clicking + in this region causes the context-sensitive help function + to be enabled. + \value Resize The region used to resize the window. + \value Move The region used to move the window. + \value None No region. + + \sa region(), regionAt(), DecorationState +*/ + +/*! + \enum QDecoration::DecorationState + + This enum describes the various states of a decoration region. + + \value Normal The region is active + \value Disabled The region is inactive. + \value Hover The cursor is hovering over the region. + \value Pressed The region is pressed. + + \sa paint(), DecorationRegion +*/ + +/*! + \fn QRegion QDecoration::region(const QWidget *widget, const QRect & rectangle, int decorationRegion) + + Implement this function to return the region specified by \a + decorationRegion for the given top level \a widget. + + The \a rectangle parameter specifies the rectangle the decoration + is wrapped around. The \a decorationRegion is a bitmask of the + values described by the DecorationRegion enum. + + \sa regionAt(), paint() +*/ + +/*! + \fn QRegion QDecoration::region(const QWidget *widget, int decorationRegion) + \overload +*/ + +/*! + \fn bool QDecoration::paint(QPainter *painter, const QWidget *widget, int decorationRegion, + DecorationState state) + + Implement this function to paint the border and title decoration + for the specified top level \a widget using the given \a painter + and decoration \a state. The specified \a decorationRegion is a + bitmask of the values described by the DecorationRegion enum. + + Note that \l{Qt for Embedded Linux} expects this function to return true if + any of the widget's decorations are repainted; otherwise it should + return false. + + \sa region() +*/ + +/*! + \fn int QDecoration::regionAt(const QWidget *widget, const QPoint &point) + + Returns the type of the first region of the specified top level \a + widget containing the given \a point. + + The return value is one of the DecorationRegion enum's values. Use + the region() function to retrieve the actual region. If none of + the widget's regions contain the point, this function returns \l + None. + + \sa region() +*/ +int QDecoration::regionAt(const QWidget *w, const QPoint &point) +{ + int regions[] = { + TopLeft, Top, TopRight, Left, Right, BottomLeft, Bottom, BottomRight, // Borders first + Menu, Title, Help, Minimize, Normalize, Maximize, Close, // then buttons + None + }; + +// char *regions_str[] = { +// "TopLeft", "Top", "TopRight", "Left", "Right", "BottomLeft", "Bottom", "BottomRight", +// "Menu", "Title", "Help", "Minimize", "Normalize", "Maximize", "Close", +// "None" +// }; + + // First check to see if within all regions at all + QRegion reg = region(w, w->geometry(), All); + if (!reg.contains(point)) { + return None; + } + + int i = 0; + while (regions[i]) { + reg = region(w, w->geometry(), regions[i]); + if (reg.contains(point)) { +// qDebug("In region %s", regions_str[i]); + return regions[i]; + } + ++i; + } + return None; +} + +#ifndef QT_NO_MENU +/*! + Builds the system menu for the given top level \a widget, adding + \gui Restore, \gui Move, \gui Size, \gui Minimize, \gui Maximize + and \gui Close actions to the given \a menu. + + \sa menuTriggered() +*/ +void QDecoration::buildSysMenu(QWidget *widget, QMenu *menu) +{ + QDecorationAction *act = new QDecorationAction(QLatin1String("Restore"), + menu, Maximize); + act->setEnabled(widget->windowState() & Qt::WindowMaximized); + menu->addAction(act); + act = new QDecorationAction(QLatin1String("Move"), menu, Move); + act->setEnabled(!(widget->windowState() & Qt::WindowMaximized)); + menu->addAction(act); + menu->addAction(new QDecorationAction(QLatin1String("Size"), menu, Resize)); + act = new QDecorationAction(QLatin1String("Minimize"), menu, Minimize); + menu->addAction(act); + act = new QDecorationAction(QLatin1String("Maximize"), menu, Maximize); + act->setDisabled(widget->windowState() & Qt::WindowMaximized); + menu->addAction(act); + menu->addSeparator(); + menu->addAction(new QDecorationAction(QLatin1String("Close"), menu, Close)); +} + +/*! + This function is called whenever an action in a top level widget's + menu is triggered, and simply calls the regionClicked() function + passing the \a widget and \a action parameters as arguments. + + \sa buildSysMenu() +*/ +void QDecoration::menuTriggered(QWidget *widget, QAction *action) +{ + QDecorationAction *decAction = static_cast<QDecorationAction *>(action); + regionClicked(widget, decAction->reg); +} +#endif // QT_NO_MENU + +/*! + \fn void QDecoration::regionClicked(QWidget *widget, int region) + + Handles the event that the specified \a region in the given top + level \a widget is activated by a single click (the \a region + parameter is described using the DecorationRegion enum). + + This function is called whenever a region in a top level widget is + clicked; the default implementation responds to clicks on items in + the system menu, performing the requested actions. + + \sa regionDoubleClicked(), region() +*/ +void QDecoration::regionClicked(QWidget *widget, int reg) +{ + switch(reg) { + case Move: + startMove(widget); + break; + case Resize: + startResize(widget); + break; + case Help: +#ifndef QT_NO_WHATSTHIS + if (QWhatsThis::inWhatsThisMode()) + QWhatsThis::leaveWhatsThisMode(); + else + QWhatsThis::enterWhatsThisMode(); +#endif + break; + case Close: + widget->close(); + break; + case Normalize: + widget->showNormal(); + break; + case Maximize: + if (widget->windowState() & Qt::WindowMaximized) + widget->showNormal(); + else + widget->showMaximized(); + break; + } +} + +/*! + \fn void QDecoration::regionDoubleClicked(QWidget *widget, int region) + + Handles the event that the specified \a region in the given top + level \a widget is activated by a double click (the region + parameter is described using the DecorationRegion enum). + + This function is called whenever a region in a top level widget is + double clicked; the default implementation responds to a double + click on the widget's title, toggling its size between the maximum + and its normal size. + + \sa regionClicked(), region() +*/ +void QDecoration::regionDoubleClicked(QWidget *widget, int reg) +{ + switch(reg) + { + case Title: { + if (widget->windowState() & Qt::WindowMaximized) + widget->showNormal(); + else + widget->showMaximized(); + break; + } + } +} + +/*! + Starts to move the given top level \a widget by making its \l + Title region active and grabbing the mouse input. + + \sa startResize() +*/ +void QDecoration::startMove(QWidget *widget) +{ +#ifdef QT_NO_QWS_MANAGER + Q_UNUSED(widget); +#else + QWSManager *manager = widget->d_func()->topData()->qwsManager; + if (manager) + manager->startMove(); +#endif +} + +/*! + Starts to resize the given top level \a widget by making its \l + BottomRight region active and grabbing the mouse input. + + \sa startMove() +*/ +void QDecoration::startResize(QWidget *widget) +{ +#ifdef QT_NO_QWS_MANAGER + Q_UNUSED(widget); +#else + QWSManager *manager = widget->d_func()->topData()->qwsManager; + if (manager) + manager->startResize(); +#endif +} + + +QT_END_NAMESPACE diff --git a/src/gui/embedded/qdecoration_qws.h b/src/gui/embedded/qdecoration_qws.h new file mode 100644 index 0000000..ff4ebac --- /dev/null +++ b/src/gui/embedded/qdecoration_qws.h @@ -0,0 +1,124 @@ +/**************************************************************************** +** +** 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$ +** +****************************************************************************/ + +#ifndef QDECORATION_QWS_H +#define QDECORATION_QWS_H + +#include <QtGui/qregion.h> +#include <QtGui/qwidget.h> +#include <QtGui/qaction.h> + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +QT_MODULE(Gui) + +class QPopupMenu; +class QMenu; + +#ifndef QT_NO_ACTION +class QDecorationAction : public QAction +{ +public: + QDecorationAction(const QString &text, QObject* parent, int region) + : QAction(text, parent), reg(region) {} + int reg; +}; +#endif // QT_NO_ACTION + +/* + Implements decoration styles +*/ +class Q_GUI_EXPORT QDecoration +{ +public: + QDecoration() {} + virtual ~QDecoration() {} + + /* AABBBBBBBBBBCC Items in DecorationRegion: + AijjjjjjjklmnC + A C A = TopLeft B = Top C = TopRight + D E D = Left E = Right + D E F = BottomLeft H = Bottom G = BottomRight + F G i = Menu j = Title k = Help + FFHHHHHHHHHHGG l = Minimize m = Maximize n = Close + + */ + + enum DecorationRegion { + None = 0x0000000000, All = 0x7fffffff, + TopLeft = 0x0000000001, Top = 0x0000000002, TopRight = 0x0000000004, + Left = 0x0000000008, Right = 0x0000000010, + BottomLeft = 0x0000000020, Bottom = 0x0000000040, BottomRight = 0x0000000080, + Borders = 0x00000000ff, + Menu = 0x0000000100, Title = 0x0000000200, Help = 0x0000000400, + Minimize = 0x0000000800, Maximize = 0x0000001000, Normalize = 0x0000002000, + Close = 0x0000004000, Move = 0x0000008000, Resize = 0x0000010000 + }; + + enum DecorationState { Normal = 0x04, Disabled = 0x08, Hover = 0x01, Pressed = 0x02 }; + + virtual QRegion region(const QWidget *w, const QRect &rect, int decorationRegion = All ) = 0; + QRegion region(const QWidget *w, int decorationRegion = All ) + { return region(w, w->rect(), decorationRegion); } + virtual int regionAt(const QWidget *w, const QPoint &point); + + virtual void regionClicked(QWidget *widget, int region); + virtual void regionDoubleClicked(QWidget *widget, int region); +#ifndef QT_NO_MENU + virtual void buildSysMenu(QWidget *widget, QMenu *menu); + void menuTriggered(QWidget *widget, QAction *action); +#endif + + static void startMove(QWidget *widget); + static void startResize(QWidget *widget); + + virtual bool paint(QPainter *p, const QWidget *w, int decorationRegion = All, + DecorationState state = Normal) = 0; + +}; + +QT_END_NAMESPACE + +QT_END_HEADER + +#endif // QDECORATION_QWS_H diff --git a/src/gui/embedded/qdecorationdefault_qws.cpp b/src/gui/embedded/qdecorationdefault_qws.cpp new file mode 100644 index 0000000..6e5314d --- /dev/null +++ b/src/gui/embedded/qdecorationdefault_qws.cpp @@ -0,0 +1,803 @@ +/**************************************************************************** +** +** 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 <qapplication.h> +#include <qwidget.h> +#include <qpainter.h> +#include <qpaintengine.h> +#include <qdrawutil.h> +#include "qdecorationdefault_qws.h" + +QT_BEGIN_NAMESPACE + +#if !defined(QT_NO_QWS_DECORATION_DEFAULT) || defined(QT_PLUGIN) + +QPixmap *QDecorationDefault::staticHelpPixmap = 0; +QPixmap *QDecorationDefault::staticMenuPixmap = 0; +QPixmap *QDecorationDefault::staticClosePixmap = 0; +QPixmap *QDecorationDefault::staticMinimizePixmap = 0; +QPixmap *QDecorationDefault::staticMaximizePixmap = 0; +QPixmap *QDecorationDefault::staticNormalizePixmap = 0; + +#ifndef QT_NO_IMAGEFORMAT_XPM + +/* XPM */ +static const char * const default_menu_xpm[] = { +/* width height ncolors chars_per_pixel */ +"16 16 11 1", +/* colors */ +" c #000000", +". c #336600", +"X c #666600", +"o c #99CC00", +"O c #999933", +"+ c #333300", +"@ c #669900", +"# c #999900", +"$ c #336633", +"% c #666633", +"& c #99CC33", +/* pixels */ +"oooooooooooooooo", +"oooooooooooooooo", +"ooooo#.++X#ooooo", +"ooooX Xoooo", +"oooX XO#% X&oo", +"oo# Ooo&@O Ooo", +"oo. Xoo#+ @X Xoo", +"oo+ OoO+ +O# +oo", +"oo+ #O+ +## +oo", +"oo. %@ ++ +. Xoo", +"oo# O@OO+ #oo", +"oooX X##$ Ooo", +"ooooX Xoo", +"oooo&OX++X#OXooo", +"oooooooooooooooo", +"oooooooooooooooo" +}; + +static const char * const default_help_xpm[] = { +"16 16 3 1", +" s None c None", +". c #ffffff", +"X c #707070", +" ", +" ", +" ...... ", +" ..XXXXXX ", +" .XX .XX ", +" .XX .XX ", +" ..XX ", +" ..XX ", +" ..XX ", +" .XX ", +" .XX ", +" .. ", +" .XX ", +" .XX ", +" ", +" "}; + +static const char * const default_close_xpm[] = { +"16 16 3 1", +" s None c None", +". c #ffffff", +"X c #707070", +" ", +" ", +" .X .X ", +" .XX .XX ", +" .XX .XX ", +" .XX .XX ", +" .XX.XX ", +" .XXX ", +" .XXX ", +" .XX.XX ", +" .XX .XX ", +" .XX .XX ", +" .XX .XX ", +" .X .X ", +" ", +" "}; + +static const char * const default_maximize_xpm[] = { +"16 16 3 1", +" s None c None", +". c #ffffff", +"X c #707070", +" ", +" ", +" ........... ", +" .XXXXXXXXXX ", +" .X .X ", +" .X .X ", +" .X .X ", +" .X .X ", +" .X .X ", +" .X .X ", +" .X .X ", +" .X........X ", +" .XXXXXXXXXX ", +" ", +" ", +" "}; + +static const char * const default_minimize_xpm[] = { +"16 16 3 1", +" s None c None", +". c #ffffff", +"X c #707070", +" ", +" ", +" ", +" ", +" ", +" ", +" ... ", +" . X ", +" .XX ", +" ", +" ", +" ", +" ", +" ", +" ", +" "}; + +static const char * const default_normalize_xpm[] = { +"16 16 3 1", +" s None c None", +". c #ffffff", +"X c #707070", +" ", +" ", +" ........ ", +" .XXXXXXXX ", +" .X .X ", +" .X .X ", +" ....X... .X ", +" .XXXXXXXX .X ", +" .X .XXXX ", +" .X .X ", +" .X .X ", +" .X......X ", +" .XXXXXXXX ", +" ", +" ", +" "}; + +#endif // QT_NO_IMAGEFORMAT_XPM + +/*! + \class QDecorationDefault + \since 4.4 + \ingroup qws + \brief The QDecorationDefault class is a base class providing default window decorations. + + See the documentation for class QDecoration for a detailed + description. This subclass of QDecoration provides standard + icons for the decoration regions. + + Note that this class is non-portable and only available in + \l{Qt for Embedded Linux}. + */ + +/*! + Default constructor. + */ +QDecorationDefault::QDecorationDefault() + : QDecoration() +{ + menu_width = 20; + help_width = 20; + close_width = 20; + minimize_width = 20; + maximize_width = 20; + normalize_width = 20; +} + +/*! + The constructor deletes the static pixmaps. + */ +QDecorationDefault::~QDecorationDefault() +{ + delete staticMenuPixmap; + delete staticClosePixmap; + delete staticMinimizePixmap; + delete staticMaximizePixmap; + delete staticNormalizePixmap; + + // This makes it safe to delete and then create a QDecorationDefault + staticMenuPixmap = 0; + staticClosePixmap = 0; + staticMinimizePixmap = 0; + staticMaximizePixmap = 0; + staticNormalizePixmap = 0; +} + +/*! + \fn const char **QDecorationDefault::xpmForRegion(int region) + + Returns a pointer to the X pixmap for the icon specified by + \a region. An X pixmap is an ASCII-text-based image. The value + of \a region must be one of a subset of the values of enum + DecorationRegion. The supported values are \e Help, \e Menu, + \e Close, \e Minimize, \e Maximize, and \e Normalize. Other + values of \a region cause zero to be returned. + + \sa QDecoration::DecorationRegion + */ +const char **QDecorationDefault::xpmForRegion(int reg) +{ +#ifdef QT_NO_IMAGEFORMAT_XPM + Q_UNUSED(reg); +#else + switch(reg) + { + case Help: + return (const char **)default_help_xpm; + case Menu: + return (const char **)default_menu_xpm; + case Close: + return (const char **)default_close_xpm; + case Minimize: + return (const char **)default_minimize_xpm; + case Maximize: + return (const char **)default_maximize_xpm; + case Normalize: + return (const char **)default_normalize_xpm; + } +#endif + return 0; +} + +/*! + \fn QPixmap QDecorationDefault::pixmapFor(const QWidget *widget, + int decorationRegion, int &xoff, int &yoff) + + Returns a pointer to the QPixmap for the widget specified by \a widget and + \a decorationRegion. The returned QPixmap is constructed from the default + X pixmap obtained from xpmForRegion(). + + \a xoff and \a yoff specify the offset for the pixmap. + + The value of \a decorationRegion must be one of a subset of the values + of enum DecorationRegion. The supported values are \e Help, + \e Menu, \e Close, \e Minimize, \e Maximize, and \e Normalize. + Other values of \a decorationRegion return 0. + + \sa QDecoration::DecorationRegion +*/ +QPixmap QDecorationDefault::pixmapFor(const QWidget *widget, + int decorationRegion, + int &xoff, + int &/*yoff*/) +{ +#ifdef QT_NO_IMAGEFORMAT_XPM + Q_UNUSED(widget); + Q_UNUSED(decorationRegion); + Q_UNUSED(xoff); + return QPixmap(); +#else + static const char **staticHelpPixmapXPM = 0; + static const char **staticMenuPixmapXPM = 0; + static const char **staticClosePixmapXPM = 0; + static const char **staticMinimizePixmapXPM = 0; + static const char **staticMaximizePixmapXPM = 0; + static const char **staticNormalizePixmapXPM = 0; + const char **xpm; + + // Why don't we just use/extend the enum type... + + if (staticHelpPixmapXPM != (xpm = xpmForRegion(Help)) || !staticHelpPixmap) { + staticHelpPixmapXPM = xpm; + staticHelpPixmap = new QPixmap(xpm); + } + if (staticMenuPixmapXPM != (xpm = xpmForRegion(Menu)) || !staticMenuPixmap) { + staticMenuPixmapXPM = xpm; + staticMenuPixmap = new QPixmap(xpm); + } + if (staticClosePixmapXPM != (xpm = xpmForRegion(Close)) || !staticClosePixmap) { + staticClosePixmapXPM = xpm; + staticClosePixmap = new QPixmap(xpm); + } + if (staticMinimizePixmapXPM != (xpm = xpmForRegion(Minimize)) || !staticMinimizePixmap) { + staticMinimizePixmapXPM = xpm; + staticMinimizePixmap = new QPixmap(xpm); + } + if (staticMaximizePixmapXPM != (xpm = xpmForRegion(Maximize)) || !staticMaximizePixmap) { + staticMaximizePixmapXPM = xpm; + staticMaximizePixmap = new QPixmap(xpm); + } + if (staticNormalizePixmapXPM != (xpm = xpmForRegion(Normalize)) || !staticNormalizePixmap) { + staticNormalizePixmapXPM = xpm; + staticNormalizePixmap = new QPixmap(xpm); + } + + const QPixmap *pm = 0; + + switch (decorationRegion) { + case Help: + pm = staticHelpPixmap; + break; + case Menu: + if (!widget->windowIcon().isNull()) + return widget->windowIcon().pixmap(16,16); //##### QIcon::pixmap() needs a size !!!!!!" + if (!pm) { + xoff = 1; + pm = staticMenuPixmap; + } + break; + case Close: + pm = staticClosePixmap; + break; + case Maximize: + pm = staticMaximizePixmap; + break; + case Normalize: + pm = staticNormalizePixmap; + break; + case Minimize: + pm = staticMinimizePixmap; + break; + default: + break; + } + return *pm; +#endif +} + +/*! + \fn int QDecorationDefault::titleBarHeight(const QWidget *widget) + + Returns the title bar height in pixels for the given \a widget. It is the + greater of 20, or the sum of the application font's line spacing value + plus a border width fudge factor. +*/ +int QDecorationDefault::titleBarHeight(const QWidget *) +{ + return qMax(20, QApplication::fontMetrics().lineSpacing() + BORDER_WIDTH); +} + +/*! + Returns the region specified by \a decorationRegion for the + top-level \a widget. \a rect specifies the rectangle the decoration + wraps. The value of \a decorationRegion is a combination of the + bitmask values of enum DecorationRegion. + */ +QRegion QDecorationDefault::region(const QWidget *widget, + const QRect &rect, + int decorationRegion) +{ + Qt::WindowFlags flags = widget->windowFlags(); + bool hasBorder = !widget->isMaximized(); + bool hasTitle = flags & Qt::WindowTitleHint; + bool hasSysMenu = flags & Qt::WindowSystemMenuHint; + bool hasContextHelp = flags & Qt::WindowContextHelpButtonHint; + bool hasMinimize = flags & Qt::WindowMinimizeButtonHint; + bool hasMaximize = flags & Qt::WindowMaximizeButtonHint; + int state = widget->windowState(); + bool isMinimized = state & Qt::WindowMinimized; + bool isMaximized = state & Qt::WindowMaximized; + + int titleHeight = hasTitle ? titleBarHeight(widget) : 0; + int bw = hasBorder ? BORDER_WIDTH : 0; + int bbw = hasBorder ? BOTTOM_BORDER_WIDTH : 0; + + QRegion region; + switch (decorationRegion) { + case All: { + QRect r(rect.left() - bw, + rect.top() - titleHeight - bw, + rect.width() + 2 * bw, + rect.height() + titleHeight + bw + bbw); + region = r; + region -= rect; + } + break; + + case Title: { + QRect r(rect.left() + + (hasSysMenu ? menu_width : 0), + rect.top() - titleHeight, + rect.width() + - (hasSysMenu ? menu_width : 0) + - close_width + - (hasMaximize ? maximize_width : 0) + - (hasMinimize ? minimize_width : 0) + - (hasContextHelp ? help_width : 0), + + titleHeight); + if (r.width() > 0) + region = r; + } + break; + + case Top: { + QRect r(rect.left() + CORNER_GRAB, + rect.top() - titleHeight - bw, + rect.width() - 2 * CORNER_GRAB, + bw); + region = r; + } + break; + + case Left: { + QRect r(rect.left() - bw, + rect.top() - titleHeight + CORNER_GRAB, + bw, + rect.height() + titleHeight - 2 * CORNER_GRAB); + region = r; + } + break; + + case Right: { + QRect r(rect.right() + 1, + rect.top() - titleHeight + CORNER_GRAB, + bw, + rect.height() + titleHeight - 2 * CORNER_GRAB); + region = r; + } + break; + + case Bottom: { + QRect r(rect.left() + CORNER_GRAB, + rect.bottom() + 1, + rect.width() - 2 * CORNER_GRAB, + bw); + region = r; + } + break; + + case TopLeft: { + QRect r1(rect.left() - bw, + rect.top() - bw - titleHeight, + CORNER_GRAB + bw, + bw); + + QRect r2(rect.left() - bw, + rect.top() - bw - titleHeight, + bw, + CORNER_GRAB + bw); + + region = QRegion(r1) + r2; + } + break; + + case TopRight: { + QRect r1(rect.right() - CORNER_GRAB, + rect.top() - bw - titleHeight, + CORNER_GRAB + bw, + bw); + + QRect r2(rect.right() + 1, + rect.top() - bw - titleHeight, + bw, + CORNER_GRAB + bw); + + region = QRegion(r1) + r2; + } + break; + + case BottomLeft: { + QRect r1(rect.left() - bw, + rect.bottom() + 1, + CORNER_GRAB + bw, + bw); + + QRect r2(rect.left() - bw, + rect.bottom() - CORNER_GRAB, + bw, + CORNER_GRAB + bw); + region = QRegion(r1) + r2; + } + break; + + case BottomRight: { + QRect r1(rect.right() - CORNER_GRAB, + rect.bottom() + 1, + CORNER_GRAB + bw, + bw); + + QRect r2(rect.right() + 1, + rect.bottom() - CORNER_GRAB, + bw, + CORNER_GRAB + bw); + region = QRegion(r1) + r2; + } + break; + + case Menu: { + if (hasSysMenu) { + region = QRect(rect.left(), rect.top() - titleHeight, + menu_width, titleHeight); + } + } + break; + + case Help: { + if (hasContextHelp) { + QRect r(rect.right() + - close_width + - (hasMaximize ? maximize_width : 0) + - (hasMinimize ? minimize_width : 0) + - help_width + 1, rect.top() - titleHeight, + help_width, titleHeight); + if (r.left() > rect.left() + titleHeight) + region = r; + } + } + break; + + + case Minimize: { + if (hasMinimize && !isMinimized) { + QRect r(rect.right() - close_width + - (hasMaximize ? maximize_width : 0) + - minimize_width + 1, rect.top() - titleHeight, + minimize_width, titleHeight); + if (r.left() > rect.left() + titleHeight) + region = r; + } + } + break; + + case Maximize: { + if (hasMaximize && !isMaximized) { + QRect r(rect.right() - close_width - maximize_width + 1, + rect.top() - titleHeight, maximize_width, titleHeight); + if (r.left() > rect.left() + titleHeight) + region = r; + } + } + break; + + case Normalize: { + if (hasMinimize && isMinimized) { + QRect r(rect.right() - close_width + - (hasMaximize ? maximize_width : 0) + - minimize_width + 1, rect.top() - titleHeight, + minimize_width, titleHeight); + if (r.left() > rect.left() + titleHeight) + region = r; + } else if (hasMaximize && isMaximized) { + QRect r(rect.right() - close_width - maximize_width + 1, + rect.top() - titleHeight, maximize_width, titleHeight); + if (r.left() > rect.left() + titleHeight) + region = r; + } + } + break; + + case Close: { + QRect r(rect.right() - close_width + 1, rect.top() - titleHeight, + close_width, titleHeight); + if (r.left() > rect.left() + titleHeight) + region = r; + } + break; + + default: { + int i = 1; + while (i) { + if (i & decorationRegion) + region += this->region(widget, rect, i); + i <<= 1; + } + } + break; + } + + return region; +} + +/*! + Paints the border and title decoration for the top-level \a widget + using the \a painter provided and the decoration \a state. The value + of \a decorationRegion is a combination of the bitmask values of + enum DecorationRegion. + + Note that Qt for Embedded Linux expects this function to return true if any of + the widget's decorations are repainted; otherwise it returns false. + */ +bool QDecorationDefault::paint(QPainter *painter, + const QWidget *widget, + int decorationRegion, + DecorationState state) +{ + if (decorationRegion == None) + return false; + + const QRect titleRect = QDecoration::region(widget, Title).boundingRect(); + const QPalette pal = QApplication::palette(); + int titleHeight = titleRect.height(); + int titleWidth = titleRect.width(); + QRegion oldClipRegion = painter->clipRegion(); + + + Qt::WindowFlags flags = widget->windowFlags(); + bool hasBorder = !widget->isMaximized(); + bool hasTitle = flags & Qt::WindowTitleHint; + bool hasSysMenu = flags & Qt::WindowSystemMenuHint; + bool hasContextHelp = flags & Qt::WindowContextHelpButtonHint; + bool hasMinimize = flags & Qt::WindowMinimizeButtonHint; + bool hasMaximize = flags & Qt::WindowMaximizeButtonHint; + + bool paintAll = (decorationRegion == int(All)); + bool handled = false; + + bool porterDuff = painter->paintEngine()->hasFeature(QPaintEngine::PorterDuff); + + if ((paintAll || decorationRegion & Borders) && state == Normal && hasBorder) { + if (hasTitle) { // reduce flicker + QRect rect(widget->rect()); + QRect r(rect.left(), rect.top() - titleHeight, + rect.width(), titleHeight); + painter->setClipRegion(oldClipRegion - r); + } + QRect br = QDecoration::region(widget).boundingRect(); + if (porterDuff) + painter->setCompositionMode(QPainter::CompositionMode_Source); + qDrawWinPanel(painter, br.x(), br.y(), br.width(), + br.height(), pal, false, + &pal.brush(QPalette::Window)); + if (porterDuff) + painter->setCompositionMode(QPainter::CompositionMode_SourceOver); + handled |= true; + } + + if ((paintAll || decorationRegion & Title && titleWidth > 0) && state == Normal && hasTitle) { + painter->setClipRegion(oldClipRegion); + QBrush titleBrush; + QPen titlePen; + + if (widget == qApp->activeWindow()) { + titleBrush = pal.brush(QPalette::Highlight); + titlePen = pal.color(QPalette::HighlightedText); + } else { + titleBrush = pal.brush(QPalette::Window); + titlePen = pal.color(QPalette::Text); + } + + if (porterDuff) + painter->setCompositionMode(QPainter::CompositionMode_Source); + qDrawShadePanel(painter, + titleRect.x(), titleRect.y(), titleRect.width(), titleRect.height(), + pal, true, 1, &titleBrush); + if (porterDuff) + painter->setCompositionMode(QPainter::CompositionMode_SourceOver); + + painter->setPen(titlePen); + painter->drawText(titleRect.x() + 4, titleRect.y(), + titleRect.width() - 8, titleRect.height(), + Qt::AlignVCenter, windowTitleFor(widget)); + handled |= true; + } + + if (state != Hover) { + painter->setClipRegion(oldClipRegion); + if ((paintAll || decorationRegion & Menu) && hasSysMenu) { + paintButton(painter, widget, Menu, state, pal); + handled |= true; + } + + if ((paintAll || decorationRegion & Help) && hasContextHelp) { + paintButton(painter, widget, Help, state, pal); + handled |= true; + } + + if ((paintAll || decorationRegion & Minimize) && hasMinimize) { + paintButton(painter, widget, Minimize, state, pal); + handled |= true; + } + + if ((paintAll || decorationRegion & Maximize) && hasMaximize) { + paintButton(painter, widget, + ((widget->windowState() & Qt::WindowMaximized)? Normalize : Maximize), + state, pal); + handled |= true; + } + + if (paintAll || decorationRegion & Close) { + paintButton(painter, widget, Close, state, pal); + handled |= true; + } + } + return handled; +} + +/*! + \fn void QDecorationDefault::paintButton(QPainter *painter, const + QWidget *widget, int buttonRegion, DecorationState state, + const QPalette &palette) + + Paints a region of the top-level \a widget. The region is + painted in the specified decoration \a state using the + \a painter and \a palette provided. The region to be painted is specified + by \a buttonRegion, which is a combination of the bitmask values of + DecorationRegion. If the value of \a buttonRegion is one of \e Help, + \e Menu, \e Close, \e Minimize, \e Maximize, and \e Normalize, the + button pixmap for that region is painted. + + \sa pixmapFor() + */ +void QDecorationDefault::paintButton(QPainter *painter, + const QWidget *widget, + int buttonRegion, + DecorationState state, + const QPalette &pal) +{ + int xoff = 2; + int yoff = 2; + + const QPixmap pm = pixmapFor(widget, buttonRegion, xoff, yoff); + QRect brect(QDecoration::region(widget, buttonRegion).boundingRect()); + bool porterDuff = painter->paintEngine()->hasFeature(QPaintEngine::PorterDuff); + + if (state & QDecoration::Pressed) { + if (porterDuff) + painter->setCompositionMode(QPainter::CompositionMode_Source); + qDrawWinPanel(painter, brect, pal, true, &pal.brush(QPalette::Window)); + if (porterDuff) + painter->setCompositionMode(QPainter::CompositionMode_SourceOver); + ++xoff; + ++yoff; + } else { + painter->fillRect(brect, pal.brush(QPalette::Window)); + } + + if (!pm.isNull()) + painter->drawPixmap(brect.x() + xoff, brect.y() + yoff, pm); +} + +extern QString qt_setWindowTitle_helperHelper(const QString&, const QWidget*); + +/*! + \internal + */ +QString QDecorationDefault::windowTitleFor(const QWidget *widget) const +{ + return qt_setWindowTitle_helperHelper(widget->windowTitle(), widget); +} + +#endif // QT_NO_QWS_DECORATION_DEFAULT + +QT_END_NAMESPACE diff --git a/src/gui/embedded/qdecorationdefault_qws.h b/src/gui/embedded/qdecorationdefault_qws.h new file mode 100644 index 0000000..4cc3703 --- /dev/null +++ b/src/gui/embedded/qdecorationdefault_qws.h @@ -0,0 +1,101 @@ +/**************************************************************************** +** +** 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$ +** +****************************************************************************/ + +#ifndef QDECORATIONDEFAULT_QWS_H +#define QDECORATIONDEFAULT_QWS_H + +#include <QtGui/qdecoration_qws.h> + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +QT_MODULE(Gui) + +#if !defined(QT_NO_QWS_DECORATION_DEFAULT) || defined(QT_PLUGIN) + +#define CORNER_GRAB 16 +#define BORDER_WIDTH 4 +#define BOTTOM_BORDER_WIDTH BORDER_WIDTH + +class Q_GUI_EXPORT QDecorationDefault : public QDecoration +{ +public: + QDecorationDefault(); + virtual ~QDecorationDefault(); + + virtual QRegion region(const QWidget *widget, const QRect &rect, int decorationRegion = All); + virtual bool paint(QPainter *painter, const QWidget *widget, int decorationRegion = All, + DecorationState state = Normal); + +protected: + virtual int titleBarHeight(const QWidget *widget); + + virtual void paintButton(QPainter *painter, const QWidget *widget, int buttonRegion, + DecorationState state, const QPalette &pal); + virtual QPixmap pixmapFor(const QWidget *widget, int decorationRegion, int &xoff, int &yoff); + virtual const char **xpmForRegion(int region); + + QString windowTitleFor(const QWidget *widget) const; + + int menu_width; + int help_width; + int close_width; + int minimize_width; + int maximize_width; + int normalize_width; + +private: + static QPixmap *staticHelpPixmap; + static QPixmap *staticMenuPixmap; + static QPixmap *staticClosePixmap; + static QPixmap *staticMinimizePixmap; + static QPixmap *staticMaximizePixmap; + static QPixmap *staticNormalizePixmap; + +}; + + +QT_END_NAMESPACE +#endif // QT_NO_QWS_DECORATION_DEFAULT +QT_END_HEADER + +#endif // QDECORATIONDEFAULT_QWS_H diff --git a/src/gui/embedded/qdecorationfactory_qws.cpp b/src/gui/embedded/qdecorationfactory_qws.cpp new file mode 100644 index 0000000..999e346 --- /dev/null +++ b/src/gui/embedded/qdecorationfactory_qws.cpp @@ -0,0 +1,156 @@ +/**************************************************************************** +** +** 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 "qdecorationfactory_qws.h" +#include "qdecorationplugin_qws.h" +#include "private/qfactoryloader_p.h" +#include "qmutex.h" + +#include "qapplication.h" +#include "qdecorationdefault_qws.h" +#include "qdecorationwindows_qws.h" +#include "qdecorationstyled_qws.h" + +QT_BEGIN_NAMESPACE + +#ifndef QT_NO_LIBRARY +Q_GLOBAL_STATIC_WITH_ARGS(QFactoryLoader, loader, + (QDecorationFactoryInterface_iid, + QLatin1String("/decorations"), Qt::CaseInsensitive)) +#endif + + + +/*! + \class QDecorationFactory + \ingroup qws + \ingroup appearance + + \brief The QDecorationFactory class creates window decorations in + Qt for Embedded Linux. + + Note that this class is only available in \l{Qt for Embedded Linux}. + + QDecorationFactory is used to detect and instantiate the available + decorations, allowing \l{Qt for Embedded Linux} to load the preferred + decoration into the application at runtime. The create() function + returns a QDecoration object representing the decoration + identified by a given key. The valid keys (i.e. the supported + decorations) can be retrieved using the keys() function. + + \l{Qt for Embedded Linux} provides three built-in decorations: \c Default, + \c Styled and \c Windows. In addition, custom decorations can be + added using Qt's \l {How to Create Qt Plugins}{plugin mechanism}, + i.e. by subclassing the QDecoration class and creating a mouse + driver plugin (QDecorationPlugin). + + \sa QDecoration, QDecorationPlugin +*/ + +/*! + Creates the decoration specified by the given \a key. Note that + the keys are case-insensitive. + + \sa keys() +*/ + +QDecoration *QDecorationFactory::create(const QString& key) +{ + QDecoration *ret = 0; + QString decoration = key.toLower(); +#ifndef QT_NO_QWS_DECORATION_DEFAULT + if (decoration == QLatin1String("default")) + ret = new QDecorationDefault; + else +#endif +#ifndef QT_NO_QWS_DECORATION_WINDOWS + if (decoration == QLatin1String("windows")) + ret = new QDecorationWindows; + else +#endif +#ifndef QT_NO_QWS_DECORATION_STYLED + if (decoration == QLatin1String("styled")) + ret = new QDecorationStyled; + else +#endif + { } // Keep these here - they make the #ifdefery above work +#ifndef QT_NO_LIBRARY + if (!ret) { + if (QDecorationFactoryInterface *factory = qobject_cast<QDecorationFactoryInterface*>(loader()->instance(decoration))) { + ret = factory->create(decoration); + } + } +#endif + return ret; +} + +/*! + Returns the list of valid keys, i.e., the available decorations. + + \sa create() +*/ +QStringList QDecorationFactory::keys() +{ + QStringList list; +#ifndef QT_NO_QWS_DECORATION_STYLED + list << QLatin1String("Styled"); +#endif +#ifndef QT_NO_QWS_DECORATION_DEFAULT + list << QLatin1String("Default"); +#endif +#ifndef QT_NO_QWS_DECORATION_WINDOWS + list << QLatin1String("Windows"); +#endif + +#if !defined(Q_OS_WIN32) || defined(QT_MAKEDLL) +#ifndef QT_NO_LIBRARY + QStringList plugins = loader()->keys(); + for (int i = 0; i < plugins.size(); ++i) { + if (!list.contains(plugins.at(i))) + list += plugins.at(i); + } +#endif //QT_NO_LIBRARY +#endif //QT_MAKEDLL + + return list; +} + +QT_END_NAMESPACE diff --git a/src/gui/embedded/qdecorationfactory_qws.h b/src/gui/embedded/qdecorationfactory_qws.h new file mode 100644 index 0000000..7397c78 --- /dev/null +++ b/src/gui/embedded/qdecorationfactory_qws.h @@ -0,0 +1,66 @@ +/**************************************************************************** +** +** 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$ +** +****************************************************************************/ + +#ifndef QDECORATIONFACTORY_QWS_H +#define QDECORATIONFACTORY_QWS_H + +#include <QtCore/qstringlist.h> + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +QT_MODULE(Gui) + +class QDecoration; + +class Q_GUI_EXPORT QDecorationFactory +{ +public: + static QStringList keys(); + static QDecoration *create(const QString&); +}; + +QT_END_NAMESPACE + +QT_END_HEADER + +#endif // QDECORATIONFACTORY_QWS_H diff --git a/src/gui/embedded/qdecorationplugin_qws.cpp b/src/gui/embedded/qdecorationplugin_qws.cpp new file mode 100644 index 0000000..eb37e21 --- /dev/null +++ b/src/gui/embedded/qdecorationplugin_qws.cpp @@ -0,0 +1,116 @@ +/**************************************************************************** +** +** 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 "qdecorationplugin_qws.h" +#include "qdecoration_qws.h" + +QT_BEGIN_NAMESPACE + +/*! + \class QDecorationPlugin + \ingroup qws + \ingroup plugins + + \brief The QDecorationPlugin class is an abstract base class for + window decoration plugins in Qt for Embedded Linux. + + Note that this class is only available in \l{Qt for Embedded Linux}. + + \l{Qt for Embedded Linux} provides three ready-made decoration styles: \c + Default, \c Styled and \c Windows. Custom decorations can be + implemented by subclassing the QDecoration class and creating a + decoration plugin. + + A decoration plugin can be created by subclassing + QDecorationPlugin and implementing the pure virtual keys() and + create() functions. By exporting the derived class using the + Q_EXPORT_PLUGIN2() macro, the default implementation of the + QDecorationFactory class will automatically detect the plugin and + load the driver into the application at run-time. See \l{How to + Create Qt Plugins} for details. + + To actually apply a decoration, use the + QApplication::qwsSetDecoration() function. + + \sa QDecoration, QDecorationFactory +*/ + +/*! + \fn QStringList QDecorationPlugin::keys() const + + Returns the list of valid keys, i.e., the decorations supported by + this plugin. + + \sa create() +*/ + +/*! + \fn QDecoration *QDecorationPlugin::create(const QString &key) + + Creates a decoration matching the given \a key. Note that keys are + case-insensitive. + + \sa keys() +*/ + +/*! + Constructs a decoration plugin with the given \a parent. + + Note that this constructor is invoked automatically by the + Q_EXPORT_PLUGIN2() macro, so there is no need for calling it + explicitly. +*/ +QDecorationPlugin::QDecorationPlugin(QObject *parent) + : QObject(parent) +{ +} + +/*! + Destroys the decoration plugin. + + Note that Qt destroys a plugin automatically when it is no longer + used, so there is no need for calling the destructor explicitly. +*/ +QDecorationPlugin::~QDecorationPlugin() +{ +} + +QT_END_NAMESPACE diff --git a/src/gui/embedded/qdecorationplugin_qws.h b/src/gui/embedded/qdecorationplugin_qws.h new file mode 100644 index 0000000..c58aaab --- /dev/null +++ b/src/gui/embedded/qdecorationplugin_qws.h @@ -0,0 +1,80 @@ +/**************************************************************************** +** +** 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$ +** +****************************************************************************/ + +#ifndef QDECORATIONPLUGIN_QWS_H +#define QDECORATIONPLUGIN_QWS_H + +#include <QtCore/qplugin.h> +#include <QtCore/qfactoryinterface.h> + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +QT_MODULE(Gui) + +class QDecoration; + +struct Q_GUI_EXPORT QDecorationFactoryInterface : public QFactoryInterface +{ + virtual QDecoration *create(const QString &key) = 0; +}; + +#define QDecorationFactoryInterface_iid "com.trolltech.Qt.QDecorationFactoryInterface" +Q_DECLARE_INTERFACE(QDecorationFactoryInterface, QDecorationFactoryInterface_iid) + +class Q_GUI_EXPORT QDecorationPlugin : public QObject, public QDecorationFactoryInterface +{ + Q_OBJECT + Q_INTERFACES(QDecorationFactoryInterface:QFactoryInterface) + public: + explicit QDecorationPlugin(QObject *parent = 0); + ~QDecorationPlugin(); + + virtual QStringList keys() const = 0; + virtual QDecoration *create(const QString &key) = 0; +}; + +QT_END_NAMESPACE + +QT_END_HEADER + +#endif // QDECORATIONPLUGIN_QWS_H diff --git a/src/gui/embedded/qdecorationstyled_qws.cpp b/src/gui/embedded/qdecorationstyled_qws.cpp new file mode 100644 index 0000000..847881b --- /dev/null +++ b/src/gui/embedded/qdecorationstyled_qws.cpp @@ -0,0 +1,313 @@ +/**************************************************************************** +** +** 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 <qapplication.h> +#include <qwidget.h> +#include <qpainter.h> +#include <qdrawutil.h> +#include "qdecorationstyled_qws.h" +#include "qstyle.h" +#include "qstyleoption.h" +#include "qpaintengine.h" + +QT_BEGIN_NAMESPACE + +#if !defined(QT_NO_QWS_DECORATION_STYLED) || defined(QT_PLUGIN) + +QDecorationStyled::QDecorationStyled() + : QDecorationDefault() +{ +} + +QDecorationStyled::~QDecorationStyled() +{ +} + +int QDecorationStyled::titleBarHeight(const QWidget *widget) +{ + QStyleOptionTitleBar opt; + opt.subControls = QStyle::SC_TitleBarLabel + | QStyle::SC_TitleBarSysMenu + | QStyle::SC_TitleBarNormalButton + | QStyle::SC_TitleBarContextHelpButton + | QStyle::SC_TitleBarMinButton + | QStyle::SC_TitleBarMaxButton + | QStyle::SC_TitleBarCloseButton; + opt.titleBarFlags = widget->windowFlags(); + opt.direction = QApplication::layoutDirection(); + opt.text = windowTitleFor(widget); + opt.icon = widget->windowIcon(); + opt.rect = widget->rect(); + + QStyle *style = QApplication::style(); + if (!style) + return 18; + + return style->pixelMetric(QStyle::PM_TitleBarHeight, &opt, 0); +} + +bool QDecorationStyled::paint(QPainter *painter, const QWidget *widget, int decorationRegion, + DecorationState state) +{ + if (decorationRegion == None) + return false; + + bool isActive = (widget == qApp->activeWindow()); + QPalette pal = qApp->palette(); + //ideally, the difference between Active and Inactive should be enough, so we shouldn't need to test this + if (!isActive) { + //pal.setCurrentColorGroup(QPalette::Disabled); //Can't do this either, because of palette limitations + //copied from Q3TitleBar: + pal.setColor(QPalette::Inactive, QPalette::Highlight, + pal.color(QPalette::Inactive, QPalette::Dark)); + pal.setColor(QPalette::Inactive, QPalette::Base, + pal.color(QPalette::Inactive, QPalette::Dark)); + pal.setColor(QPalette::Inactive, QPalette::HighlightedText, + pal.color(QPalette::Inactive, QPalette::Window)); + } + + Qt::WindowFlags flags = widget->windowFlags(); + bool hasBorder = !widget->isMaximized(); + bool hasTitle = flags & Qt::WindowTitleHint; + bool hasSysMenu = flags & Qt::WindowSystemMenuHint; + bool hasContextHelp = flags & Qt::WindowContextHelpButtonHint; + bool hasMinimize = flags & Qt::WindowMinimizeButtonHint; + bool hasMaximize = flags & Qt::WindowMaximizeButtonHint; + + bool paintAll = (DecorationRegion(decorationRegion) == All); + bool handled = false; + + QStyle *style = QApplication::style(); + + // In the case of a borderless title bar, the title bar must be expanded one + // borderWidth to the left, right and up. + bool noTitleBorder = style->styleHint(QStyle::SH_TitleBar_NoBorder, 0, widget); + int borderWidth = style->pixelMetric(QStyle::PM_MDIFrameWidth, 0, 0); + int titleHeight = titleBarHeight(widget) + (noTitleBorder ? borderWidth : 0); + int titleExtra = noTitleBorder ? borderWidth : 0; + + if ((paintAll || decorationRegion & Borders) && state == Normal && hasBorder) { + QRegion newClip = painter->clipRegion(); + if (hasTitle) { // reduce flicker + QRect rect(widget->rect()); + QRect r(rect.left() - titleExtra, rect.top() - titleHeight, + rect.width() + 2 * titleExtra, titleHeight); + newClip -= r; + } + if (!newClip.isEmpty()) { + QRect br = QDecoration::region(widget).boundingRect(); + painter->save(); + painter->setClipRegion(newClip); + + QStyleOptionFrame opt; + opt.palette = pal; + opt.rect = br; + opt.lineWidth = borderWidth; + + if (isActive) + opt.state |= QStyle::State_Active; + bool porterDuff = painter->paintEngine()->hasFeature(QPaintEngine::PorterDuff); + if (porterDuff) + painter->setCompositionMode(QPainter::CompositionMode_Source); + painter->fillRect(br, pal.window()); + if (porterDuff) + painter->setCompositionMode(QPainter::CompositionMode_SourceOver); + style->drawPrimitive(QStyle::PE_FrameWindow, &opt, painter, 0); + painter->restore(); + + decorationRegion &= (~Borders); + handled |= true; + } + } + + if (hasTitle) { + painter->save(); + + QStyleOptionTitleBar opt; + opt.subControls = (decorationRegion & Title + ? QStyle::SC_TitleBarLabel : QStyle::SubControl(0)) + | (decorationRegion & Menu + ? QStyle::SC_TitleBarSysMenu : QStyle::SubControl(0)) + | (decorationRegion & Help + ? QStyle::SC_TitleBarContextHelpButton : QStyle::SubControl(0)) + | (decorationRegion & Minimize + ? QStyle::SC_TitleBarMinButton : QStyle::SubControl(0)) + | (decorationRegion & Maximize + ? QStyle::SC_TitleBarMaxButton : QStyle::SubControl(0)) + | (decorationRegion & (Minimize | Maximize) + ? QStyle::SC_TitleBarNormalButton : QStyle::SubControl(0)) + | (decorationRegion & Close + ? QStyle::SC_TitleBarCloseButton : QStyle::SubControl(0)); + opt.titleBarFlags = widget->windowFlags(); + opt.titleBarState = widget->windowState(); + if (isActive) + opt.titleBarState |= QStyle::State_Active; + opt.text = windowTitleFor(widget); + opt.icon = widget->windowIcon(); + opt.palette = pal; + opt.rect = QRect(widget->rect().x() - titleExtra, -titleHeight, + widget->rect().width() + 2 * titleExtra, titleHeight); + + if (paintAll) { + painter->setClipRegion(opt.rect); + } else { + const QRect widgetRect = widget->rect(); + QRegion newClip = opt.rect; + if (!(decorationRegion & Menu) && hasSysMenu) + newClip -= region(widget, widgetRect, Menu); + if (!(decorationRegion & Title) && hasTitle) + newClip -= region(widget, widgetRect, Title); + if (!(decorationRegion & Help) && hasContextHelp) + newClip -= region(widget, widgetRect, Help); + if (!(decorationRegion & Minimize) && hasMinimize) + newClip -= region(widget, widgetRect, Minimize); + if (!(decorationRegion & Maximize) && hasMaximize) + newClip -= region(widget, widgetRect, Maximize); + if (!(decorationRegion & (Minimize | Maximize)) && (hasMaximize | hasMinimize)) + newClip -= region(widget, widgetRect, Normal); + if (!(decorationRegion & Close)) + newClip -= region(widget, widgetRect, Close); + painter->setClipRegion(newClip); + } + + if (state == Pressed) + opt.activeSubControls = opt.subControls; + + style->drawComplexControl(QStyle::CC_TitleBar, &opt, painter, 0); + painter->restore(); + + decorationRegion &= ~(Title | Menu | Help | Normalize | Minimize | Maximize | Close); + handled |= true; + } + + return handled; +} + +QRegion QDecorationStyled::region(const QWidget *widget, const QRect &rect, int decorationRegion) +{ + QStyle *style = QApplication::style(); + + // In the case of a borderless title bar, the title bar must be expanded one + // borderWidth to the left, right and up. + bool noTitleBorder = style->styleHint(QStyle::SH_TitleBar_NoBorder, 0, widget); + int borderWidth = style->pixelMetric(QStyle::PM_MDIFrameWidth, 0, 0); + int titleHeight = titleBarHeight(widget) + (noTitleBorder ? borderWidth : 0); + int titleExtra = noTitleBorder ? borderWidth : 0; + + QRect inside = QRect(rect.x() - titleExtra, rect.top() - titleHeight, + rect.width() + 2 * titleExtra, titleHeight); + + Qt::WindowFlags flags = widget->windowFlags(); + bool hasSysMenu = flags & Qt::WindowSystemMenuHint; + bool hasContextHelp = flags & Qt::WindowContextHelpButtonHint; + bool hasMinimize = flags & Qt::WindowMinimizeButtonHint; + bool hasMaximize = flags & Qt::WindowMaximizeButtonHint; + + QStyleOptionTitleBar opt; + opt.subControls = QStyle::SC_TitleBarLabel + | QStyle::SC_TitleBarSysMenu + | QStyle::SC_TitleBarNormalButton + | QStyle::SC_TitleBarMinButton + | QStyle::SC_TitleBarMaxButton + | QStyle::SC_TitleBarCloseButton; + opt.titleBarFlags = widget->windowFlags(); + opt.direction = QApplication::layoutDirection(); + opt.text = windowTitleFor(widget); + opt.icon = widget->windowIcon(); + opt.rect = inside; + + QRegion region; + switch (decorationRegion) { + case Title: + region = style->subControlRect(QStyle::CC_TitleBar, &opt, + QStyle::SC_TitleBarLabel, 0); + break; + case Menu: + if (hasSysMenu) + region = style->subControlRect(QStyle::CC_TitleBar, &opt, + QStyle::SC_TitleBarSysMenu, 0); + break; + case Help: + if (hasContextHelp) + region = style->subControlRect(QStyle::CC_TitleBar, &opt, + QStyle::SC_TitleBarContextHelpButton, + 0); + break; + case Normalize: + if (hasMaximize | hasMinimize) + region = style->subControlRect(QStyle::CC_TitleBar, &opt, + QStyle::SC_TitleBarNormalButton, + 0); + break; + case Minimize: + if (hasMinimize) + region = style->subControlRect(QStyle::CC_TitleBar, &opt, + QStyle::SC_TitleBarMinButton, + 0); + break; + case Maximize: + if (hasMaximize) + region = style->subControlRect(QStyle::CC_TitleBar, &opt, + QStyle::SC_TitleBarMaxButton, + 0); + break; + case Close: + region = style->subControlRect(QStyle::CC_TitleBar, &opt, + QStyle::SC_TitleBarCloseButton, 0); + break; + + default: + region = QDecorationDefault::region(widget, rect, decorationRegion); + } + + opt.rect = QRect(rect.x() - titleExtra, rect.top() - titleHeight, + rect.width() + 2 * titleExtra, + rect.height() + titleHeight + titleExtra); + + QStyleHintReturnMask mask; + style->styleHint(QStyle::SH_WindowFrame_Mask, &opt, 0, &mask); + + return (mask.region.isEmpty() ? region : (region & mask.region)); +} + +#endif // QT_NO_QWS_DECORATION_STYLED + +QT_END_NAMESPACE diff --git a/src/gui/embedded/qdecorationstyled_qws.h b/src/gui/embedded/qdecorationstyled_qws.h new file mode 100644 index 0000000..583c3f2 --- /dev/null +++ b/src/gui/embedded/qdecorationstyled_qws.h @@ -0,0 +1,73 @@ +/**************************************************************************** +** +** 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$ +** +****************************************************************************/ + +#ifndef QDECORATIONSTYLED_QWS_H +#define QDECORATIONSTYLED_QWS_H + +#include <QtGui/qdecorationdefault_qws.h> + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +QT_MODULE(Gui) + +#if !defined(QT_NO_QWS_DECORATION_STYLED) || defined(QT_PLUGIN) + +class Q_GUI_EXPORT QDecorationStyled : public QDecorationDefault +{ +public: + QDecorationStyled(); + virtual ~QDecorationStyled(); + + QRegion region(const QWidget *widget, const QRect &rect, int decorationRegion = All); + bool paint(QPainter *painter, const QWidget *widget, int decorationRegion = All, + DecorationState state = Normal); + int titleBarHeight(const QWidget *widget); +}; + +#endif // QT_NO_QWS_DECORATION_STYLED + +QT_END_NAMESPACE + +QT_END_HEADER + +#endif // QDECORATIONSTYLED_QWS_H diff --git a/src/gui/embedded/qdecorationwindows_qws.cpp b/src/gui/embedded/qdecorationwindows_qws.cpp new file mode 100644 index 0000000..374f45e --- /dev/null +++ b/src/gui/embedded/qdecorationwindows_qws.cpp @@ -0,0 +1,407 @@ +/**************************************************************************** +** +** 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 <qapplication.h> +#include <qwidget.h> +#include <qpainter.h> +#include <qdrawutil.h> +#include "qdecorationwindows_qws.h" + +QT_BEGIN_NAMESPACE + +#if !defined(QT_NO_QWS_DECORATION_WINDOWS) || defined(QT_PLUGIN) + +#ifndef QT_NO_IMAGEFORMAT_XPM + +/* XPM */ +static const char * const win_close_xpm[] = { +"16 16 4 1", +" s None c None", +". c #000000", +"X c #FFFFFF", +"Y c #707070", +" ", +" ", +" ", +" Y. .Y ", +" .. .. ", +" .. .. ", +" .YY. ", +" Y..Y ", +" .YY. ", +" .. .. ", +" .. .. ", +" Y. .Y ", +" ", +" ", +" ", +" "}; + +static const char * const win_help_xpm[] = { +"16 16 3 1", +" s None c None", +". c #ffffff", +"X c #000000", +" ", +" ", +" ", +" XXXXXX ", +" XX XX ", +" XX XX ", +" XX ", +" XX ", +" XX ", +" XX ", +" ", +" XX ", +" XX ", +" ", +" ", +" "}; + +static const char * const win_maximize_xpm[] = { +"16 16 4 1", +" s None c None", +". c #000000", +"X c #FFFFFF", +"Y c #707070", +" ", +" ", +" ", +" .......... ", +" .......... ", +" . . ", +" . . ", +" . . ", +" . . ", +" . . ", +" . . ", +" .......... ", +" ", +" ", +" ", +" "}; + +static const char * const win_minimize_xpm[] = { +"16 16 4 1", +" s None c None", +". c #000000", +"X c #FFFFFF", +"Y c #707070", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ........ ", +" ........ ", +" ", +" ", +" ", +" "}; + +static const char * const win_normalize_xpm[] = { +"16 16 4 1", +" s None c None", +". c #000000", +"X c #FFFFFF", +"Y c #707070", +" ", +" ", +" ......... ", +" ......... ", +" . . ", +" . . ", +" ......... . ", +" ......... . ", +" . . . ", +" . .... ", +" . . ", +" . . ", +" ......... ", +" ", +" ", +" "}; + +#endif // QT_NO_IMAGEFORMAT_XPM + + +QDecorationWindows::QDecorationWindows() + : QDecorationDefault() +{ + menu_width = 16; + help_width = 18; + minimize_width = 18; + maximize_width = 18; + close_width = 18; +} + +QDecorationWindows::~QDecorationWindows() +{ +} + +const char **QDecorationWindows::xpmForRegion(int reg) +{ +#ifdef QT_NO_IMAGEFORMAT_XPM + Q_UNUSED(reg); +#else + switch(reg) + { + case Close: + return (const char **)win_close_xpm; + case Help: + return (const char **)win_help_xpm; + case Minimize: + return (const char **)win_minimize_xpm; + case Maximize: + return (const char **)win_maximize_xpm; + case Normalize: + return (const char **)win_normalize_xpm; + default: + return QDecorationDefault::xpmForRegion(reg); + } +#endif + return 0; +} + +QRegion QDecorationWindows::region(const QWidget *widget, const QRect &rect, int type) +{ + Qt::WindowFlags flags = widget->windowFlags(); + bool hasTitle = flags & Qt::WindowTitleHint; + bool hasSysMenu = flags & Qt::WindowSystemMenuHint; + bool hasContextHelp = flags & Qt::WindowContextHelpButtonHint; + bool hasMinimize = flags & Qt::WindowMinimizeButtonHint; + bool hasMaximize = flags & Qt::WindowMaximizeButtonHint; + const QFontMetrics fontMetrics = QApplication::fontMetrics(); + int titleHeight = hasTitle ? qMax(20, fontMetrics.lineSpacing()) : 0; + int state = widget->windowState(); + bool isMinimized = state & Qt::WindowMinimized; + bool isMaximized = state & Qt::WindowMaximized; + + QRegion region; + switch (type) { + case Menu: { + if (hasSysMenu) { + region = QRect(rect.left() + 2, rect.top() - titleHeight, + menu_width, titleHeight); + } + } + break; + + case Title: { + QRect r(rect.left() + + (hasSysMenu ? menu_width + 4: 0), + rect.top() - titleHeight, + rect.width() + - (hasSysMenu ? menu_width : 0) + - close_width + - (hasMaximize ? maximize_width : 0) + - (hasMinimize ? minimize_width : 0) + - (hasContextHelp ? help_width : 0) + - 3, + titleHeight); + if (r.width() > 0) + region = r; + } + break; + case Help: { + if (hasContextHelp) { + QRect r(rect.right() + - close_width + - (hasMaximize ? maximize_width : 0) + - (hasMinimize ? minimize_width : 0) + - help_width - 3, rect.top() - titleHeight, + help_width, titleHeight); + if (r.left() > rect.left() + titleHeight) + region = r; + } + } + break; + + case Minimize: { + if (hasMinimize && !isMinimized) { + QRect r(rect.right() - close_width + - (hasMaximize ? maximize_width : 0) + - minimize_width - 3, rect.top() - titleHeight, + minimize_width, titleHeight); + if (r.left() > rect.left() + titleHeight) + region = r; + } + } + break; + + case Maximize: { + if (hasMaximize && !isMaximized) { + QRect r(rect.right() - close_width - maximize_width - 3, + rect.top() - titleHeight, maximize_width, titleHeight); + if (r.left() > rect.left() + titleHeight) + region = r; + } + } + break; + + case Normalize: { + if (hasMinimize && isMinimized) { + QRect r(rect.right() - close_width + - (hasMaximize ? maximize_width : 0) + - minimize_width - 3, rect.top() - titleHeight, + minimize_width, titleHeight); + if (r.left() > rect.left() + titleHeight) + region = r; + } else if (hasMaximize && isMaximized) { + QRect r(rect.right() - close_width - maximize_width - 3, + rect.top() - titleHeight, maximize_width, titleHeight); + if (r.left() > rect.left() + titleHeight) + region = r; + } + } + break; + + case Close: { + QRect r(rect.right() - close_width - 1, rect.top() - titleHeight, + close_width, titleHeight); + if (r.left() > rect.left() + titleHeight) + region = r; + } + break; + + default: + region = QDecorationDefault::region(widget, rect, type); + break; + } + + return region; +} + +bool QDecorationWindows::paint(QPainter *painter, const QWidget *widget, int decorationRegion, + DecorationState state) +{ + if (decorationRegion == None) + return false; + + const QRect titleRect = QDecoration::region(widget, Title).boundingRect(); + const QPalette pal = QApplication::palette(); + QRegion oldClipRegion = painter->clipRegion(); + + bool paintAll = (decorationRegion == int(All)); + if ((paintAll || decorationRegion & Title && titleRect.width() > 0) && state == Normal + && (widget->windowFlags() & Qt::WindowTitleHint) ) { + painter->setClipRegion(oldClipRegion); + QColor fromBrush, toBrush; + QPen titlePen; + + if (widget == qApp->activeWindow() || qApp->activeWindow() == qApp->activePopupWidget()) { + fromBrush = pal.color(QPalette::Highlight); + titlePen = pal.color(QPalette::HighlightedText); + } else { + fromBrush = pal.color(QPalette::Window); + titlePen = pal.color(QPalette::Text); + } + toBrush = fromBrush.lighter(300); + + painter->setPen(Qt::NoPen); + QPoint p1(titleRect.x(), titleRect.y() + titleRect.height()/2); + QPoint p2(titleRect.right(), titleRect.y() + titleRect.height()/2); + QLinearGradient lg(p1, p2); + lg.setColorAt(0, fromBrush); + lg.setColorAt(1, toBrush); + painter->fillRect(titleRect, lg); + + painter->setPen(titlePen); + painter->drawText(titleRect, Qt::AlignVCenter, windowTitleFor(widget)); + decorationRegion ^= Title; + } + + return QDecorationDefault::paint(painter, widget, decorationRegion, state); +} + +void QDecorationWindows::paintButton(QPainter *painter, const QWidget *widget, int buttonRegion, + DecorationState state, const QPalette &pal) +{ + QBrush fromBrush, toBrush; + QPen titlePen; + + if (widget == qApp->activeWindow() || qApp->activeWindow() == qApp->activePopupWidget()) { + fromBrush = pal.brush(QPalette::Highlight); + titlePen = pal.color(QPalette::HighlightedText); + } else { + fromBrush = pal.brush(QPalette::Window); + titlePen = pal.color(QPalette::Text); + } + toBrush = fromBrush.color().lighter(300); + + QRect brect(QDecoration::region(widget, buttonRegion).boundingRect()); + if (buttonRegion != Close && buttonRegion != Menu) + painter->fillRect(brect, toBrush); + else + painter->fillRect(brect.x() - 2, brect.y(), brect.width() + 4, brect.height(), + buttonRegion == Menu ? fromBrush : toBrush); + + int xoff = 1; + int yoff = 2; + const QPixmap pm = pixmapFor(widget, buttonRegion, xoff, yoff); + if (buttonRegion != Menu) { + if (state & Normal) { + qDrawWinPanel(painter, brect.x(), brect.y() + 2, brect.width(), + brect.height() - 4, pal, false, &pal.brush(QPalette::Window)); + } else if (state & Pressed) { + qDrawWinPanel(painter, brect.x(), brect.y() + 2, brect.width(), + brect.height() - 4, pal, true, &pal.brush(QPalette::Window)); + ++xoff; + ++yoff; + } + } else { + xoff = 0; + yoff = 2; + } + + if (!pm.isNull()) + painter->drawPixmap(brect.x() + xoff, brect.y() + yoff, pm); +} + +#endif // QT_NO_QWS_DECORATION_WINDOWS || QT_PLUGIN + +QT_END_NAMESPACE diff --git a/src/gui/embedded/qdecorationwindows_qws.h b/src/gui/embedded/qdecorationwindows_qws.h new file mode 100644 index 0000000..c7eb80e --- /dev/null +++ b/src/gui/embedded/qdecorationwindows_qws.h @@ -0,0 +1,77 @@ +/**************************************************************************** +** +** 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$ +** +****************************************************************************/ + +#ifndef QDECORATIONWINDOWS_QWS_H +#define QDECORATIONWINDOWS_QWS_H + +#include <QtGui/qdecorationdefault_qws.h> + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +QT_MODULE(Gui) + +#if !defined(QT_NO_QWS_DECORATION_WINDOWS) || defined(QT_PLUGIN) + +class Q_GUI_EXPORT QDecorationWindows : public QDecorationDefault +{ +public: + QDecorationWindows(); + virtual ~QDecorationWindows(); + + QRegion region(const QWidget *widget, const QRect &rect, int decorationRegion = All); + bool paint(QPainter *painter, const QWidget *widget, int decorationRegion = All, + DecorationState state = Normal); + +protected: + void paintButton(QPainter *painter, const QWidget *widget, int buttonRegion, + DecorationState state, const QPalette &pal); + const char **xpmForRegion(int reg); +}; + +#endif // QT_NO_QWS_DECORATION_WINDOWS + +QT_END_NAMESPACE + +QT_END_HEADER + +#endif // QDECORATIONWINDOWS_QWS_H diff --git a/src/gui/embedded/qdirectpainter_qws.cpp b/src/gui/embedded/qdirectpainter_qws.cpp new file mode 100644 index 0000000..113e607 --- /dev/null +++ b/src/gui/embedded/qdirectpainter_qws.cpp @@ -0,0 +1,682 @@ +/**************************************************************************** +** +** 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 "qdirectpainter_qws.h" + +#include "qscreen_qws.h" +#include "private/qobject_p.h" +#include "private/qapplication_p.h" +#include "qwsdisplay_qws.h" +#include "qwidget.h" +#include "qimage.h" +#include <qwsevent_qws.h> +#include <private/qwindowsurface_qws_p.h> +#include <private/qwsdisplay_qws_p.h> + +QT_BEGIN_NAMESPACE + +#ifdef Q_WS_QWS +#ifndef QT_NO_DIRECTPAINTER + +/*! + \class QDirectPainter + \ingroup multimedia + \ingroup qws + + \brief The QDirectPainter class provides direct access to the + underlying hardware in Qt for Embedded Linux. + + Note that this class is only available in \l{Qt for Embedded Linux}. + + QDirectPainter allows a client application to reserve a region of + the framebuffer and render directly onto the screen. There are two + ways of using the QDirectPainter class: You can either reserve a + region using the provided static functions, or you can instantiate + an object and make use of its more dynamic API. + + \tableofcontents + + \section1 Dynamic Allocation + + By instantiating a QDirectPainter object using the default + QDirectPainter::NonReserved surface flag, the client application + only gets some control over the reserved region, i.e., it can + still render directly onto the screen but the allocated region may + change (for example, if a window with a higher focus requests + parts of the same region). The currently allocated region can be + retrieved using the allocatedRegion() function, while the + requestedRegion() function returns the originally reserved + region. + + + \section1 Static Allocation + + + Using the static approach, the client application gets complete + control over the reserved region, i.e., the affected region will + never be modified by the screen driver. + + To create a static region, pass the QDirectPainter::Reserved + surface flag to the constructor. After the reserved region is + reported through regionChanged(), the allocated region will not + change, unless setRegion() is called. + + If QDirectPainter::ReservedSynchronous is passed to the + constructor, calls to setRegion() will block until the region is + reserved, meaning that allocatedRegion() will be available immediately. + Note that in the current version setRegion() will cause the application + event loop to be entered, potentially causing reentrancy issues. + + \section1 Rendering + + To draw on a given region, the application must first get hold of + a pointer to the framebuffer. In most cases, this pointer can be + retrieved using the QDirectPainter::frameBuffer() function. But + note that if the current screen has subscreens, you must query the + screen driver instead to identify the correct subscreen. A pointer + to the current screen driver can always be retrieved using the + static QScreen::instance() function. Then use QScreen's \l + {QScreen::}{subScreenIndexAt()} and \l {QScreen::}{subScreens()} + functions to access the correct subscreen, and the subscreen's \l + {QScreen::}{base()} function to retrieve a pointer to the + framebuffer. + + Depending on the hardware, it might be necessary to lock the + framebuffer for exclusive use while writing to it. This is + possible using the lock() and unlock() functions. Note that + calling lock() will prevent all other applications from working + until unlock() is called. + + In addition, QDirectPainter provides several functions returning + information about the framebuffer: the linestep() function returns + the length (in bytes) of each scanline of the framebuffer while + the screenDepth(), screenWidth() and screenHeight() function + return the screen metrics. + + \sa QScreen, QWSEmbedWidget, {Qt for Embedded Linux Architecture} +*/ + +/*! + \enum QDirectPainter::SurfaceFlag + + This enum describes the behavior of the region reserved by this + QDirectPainter object. + + \value NonReserved The allocated region may change, e.g., if a + window with a higher focus requests parts of the same region. See + also \l {Dynamic Allocation}. + + \value Reserved The allocated region will never change. See also + \l {Static Allocation}. + + \value ReservedSynchronous The allocated region will never change and + each function that changes the allocated region will be blocking. + + \sa reservedRegion(), allocatedRegion() +*/ + +/*! + \fn QRegion QDirectPainter::region() + \obsolete + + Use QDirectPainter::reservedRegion() instead. +*/ + +static inline QScreen *getPrimaryScreen() +{ + QScreen *screen = QScreen::instance(); + if (!screen->base()) { + QList<QScreen*> subScreens = screen->subScreens(); + if (subScreens.size() < 1) + return 0; + screen = subScreens.at(0); + } + return screen; +} + +static inline QSize screenS() +{ + QScreen *screen = getPrimaryScreen(); + if (!screen) + return QSize(); + return QSize(screen->width(), screen->height()); +} + +static inline QSize devS() +{ + QScreen *screen = getPrimaryScreen(); + if (!screen) + return QSize(); + return QSize(screen->deviceWidth(), screen->deviceHeight()); +} + + +class QDirectPainterPrivate : public QObjectPrivate +{ + Q_DECLARE_PUBLIC(QDirectPainter); +public: + + QDirectPainterPrivate() : surface(0), seenRegion(false) {} + + ~QDirectPainterPrivate() { + if (QPaintDevice::qwsDisplay()) { // make sure not in QApplication destructor + qApp->d_func()->directPainters->remove(surface->windowId()); + surface->setGeometry(QRect()); + } + delete surface; + } + + QWSDirectPainterSurface *surface; + QRegion requested_region; + + static QDirectPainter *staticPainter; + bool seenRegion; +}; + +QDirectPainter *QDirectPainterPrivate::staticPainter = 0; + +void qt_directpainter_region(QDirectPainter *dp, const QRegion &alloc, int type) +{ + QDirectPainterPrivate *d = dp->d_func(); + + QRegion r = alloc; + QScreen *screen = d->surface->screen(); + if (screen->isTransformed()) { + const QSize screenSize(screen->width(), screen->height()); + r = screen->mapToDevice(r, screenSize); + } + if (type == QWSRegionEvent::Allocation) { + d->surface->setClipRegion(alloc); + d->seenRegion = true; + if (dp != QDirectPainterPrivate::staticPainter) { + if (!d->surface->flushingRegionEvents) // recursion guard + dp->regionChanged(r); + } + } +} + +#ifndef QT_NO_QWSEMBEDWIDGET +void qt_directpainter_embedevent(QDirectPainter *dp, const QWSEmbedEvent *event) +{ + if (event->type | QWSEmbedEvent::Region) { + QScreen *screen = dp->d_func()->surface->screen(); + QRegion r = event->region; + if (screen->isTransformed()) { + const QSize screenSize(screen->width(), screen->height()); + r = screen->mapToDevice(r, screenSize); + } + dp->setRegion(r); + } +} +#endif + +/*! + Constructs a QDirectPainter object with the given \a parent and + surface \a flag. +*/ +QDirectPainter::QDirectPainter(QObject *parent, SurfaceFlag flag) + :QObject(*new QDirectPainterPrivate, parent) +{ + Q_D(QDirectPainter); + d->surface = new QWSDirectPainterSurface(true, flag); + + if (flag != NonReserved) + d->surface->setReserved(); + + QApplicationPrivate *ad = qApp->d_func(); + if (!ad->directPainters) + ad->directPainters = new QMap<WId, QDirectPainter*>; + ad->directPainters->insert(d->surface->windowId(), this); +} + +/*! + Destroys this QDirectPainter object, releasing the reserved region. + + \sa allocatedRegion() +*/ +QDirectPainter::~QDirectPainter() +{ + /* should not be necessary + if (this == QDirectPainterPrivate::staticPainter) + QDirectPainterPrivate::staticPainter = 0; + */ +} + +/*! + \fn void QDirectPainter::setGeometry(const QRect &rectangle) + \since 4.2 + + Request to reserve the given \a rectangle of the framebuffer. + + Note that the actually allocated region might differ from the + requested one, e.g., if the given region overlaps with the + region of another QDirectPainter object. + + \sa geometry(), allocatedRegion(), setRegion() +*/ +void QDirectPainter::setGeometry(const QRect &rect) +{ + setRegion(rect); +} + +/*! + \since 4.2 + + Returns the bounding rectangle of the requested region. + + \sa setGeometry(), requestedRegion() +*/ +QRect QDirectPainter::geometry() const +{ + Q_D(const QDirectPainter); + return d->requested_region.boundingRect(); +} + +/*! + \since 4.2 + + Requests to reserve the given \a region of the framebuffer. + + Note that the actually allocated region might differ from the + requested one, e.g., if the given region overlaps with the region + of another QDirectPainter object. + + \sa requestedRegion(), allocatedRegion(), {Dynamic Allocation} +*/ +void QDirectPainter::setRegion(const QRegion ®ion) +{ + Q_D(QDirectPainter); + d->requested_region = region; + + const QScreen *screen = d->surface->screen(); + if (screen->isTransformed()) { + const QSize devSize(screen->deviceWidth(), screen->deviceHeight()); + const QRegion r = screen->mapFromDevice(region, devSize); + d->surface->setRegion(r); + } else { + d->surface->setRegion(region); + } +} + +/*! + \since 4.2 + + Returns the region requested by this QDirectPainter. + + Note that if the QDirectPainter::Reserved flag is set, the region + returned by this function will always be equivalent to the region + returned by the allocatedRegion() function. Otherwise they might + differ (see \l {Dynamic Allocation} for details). + + \sa geometry(), setRegion() +*/ +QRegion QDirectPainter::requestedRegion() const +{ + Q_D(const QDirectPainter); + return d->requested_region; +} + +/*! + \since 4.2 + + Returns the currently reserved region. + + Note that if the QDirectPainter::Reserved flag is set, the region + returned by this function will always be equivalent to the region + returned by the requestedRegion() function. Otherwise they might + differ (see \l {Dynamic Allocation} for details). + + \sa requestedRegion(), geometry() +*/ +QRegion QDirectPainter::allocatedRegion() const +{ + Q_D(const QDirectPainter); + const QScreen *screen = d->surface->screen(); + if (screen->isTransformed()) { + const QSize screenSize(screen->width(), screen->height()); + return screen->mapToDevice(d->surface->region(), screenSize); + } else { + return d->surface->region(); + } +} + +/*! + \since 4.2 + + Returns the window system identifier of the widget. +*/ +WId QDirectPainter::winId() const +{ + Q_D(const QDirectPainter); + return d->surface->windowId(); +} + +/*! + \fn void QDirectPainter::regionChanged(const QRegion &newRegion) + \since 4.2 + + This function is called when the allocated region changes. + + This function is not called for region changes that happen while the + startPainting() function is executing. + + Note that the given region, \a newRegion, is not guaranteed to be correct at the + time you access the display. To prevent reentrancy problems you should + always call startPainting() before updating the display and then use + allocatedRegion() to retrieve the correct region. + + \sa allocatedRegion(), startPainting(), {Dynamic Allocation} +*/ +void QDirectPainter::regionChanged(const QRegion ®ion) +{ + Q_UNUSED(region); +} + +/*! + \since 4.2 + + Call this function before you start updating the pixels in the + allocated region. The hardware will be notified, if necessary, + that you are about to start painting operations. + + Set \a lockDisplay if you want startPainting() and endPainting() + to lock() and unlock() the display automatically. + + Note that for a NonReserved direct painter, you must call + allocatedRegion() after calling this function, since the allocated + region is only guaranteed to be correct after this function has + returned. + + The regionChanged() function will not be called between startPainting() + and endPainting(). + + \sa endPainting(), flush() +*/ +void QDirectPainter::startPainting(bool lockDisplay) +{ + Q_D(QDirectPainter); + d->surface->setLocking(lockDisplay); + + const QScreen *screen = d->surface->screen(); + if (screen->isTransformed()) { + const QSize devSize(screen->deviceWidth(), screen->deviceHeight()); + const QRegion r = screen->mapFromDevice(d->surface->region(), devSize); + d->surface->beginPaint(r); + } else { + d->surface->beginPaint(d->surface->region()); + } +} + +/*! + \since 4.2 + + Call this function when you are done updating the screen. It will + notify the hardware, if necessary, that your painting operations + have ended. +*/ +void QDirectPainter::endPainting() +{ + Q_D(QDirectPainter); + + const QScreen *screen = d->surface->screen(); + if (screen->isTransformed()) { + const QSize devSize(screen->deviceWidth(), screen->deviceHeight()); + const QRegion r = screen->mapFromDevice(d->surface->region(), devSize); + d->surface->endPaint(r); + } else { + d->surface->endPaint(d->surface->region()); + } +} + +/*! + \since 4.3 + \overload + + This function will automatically call flush() to flush the + \a region to the display before notifying the hardware, if + necessary, that painting operations have ended. +*/ +void QDirectPainter::endPainting(const QRegion ®ion) +{ + endPainting(); + flush(region); +} + +/*! + \since 4.3 + + Flushes the \a region onto the screen. +*/ +void QDirectPainter::flush(const QRegion ®ion) +{ + Q_D(QDirectPainter); + + const QScreen *screen = d->surface->screen(); + if (screen->isTransformed()) { + const QSize devSize(screen->deviceWidth(), screen->deviceHeight()); + const QRegion r = screen->mapFromDevice(region, devSize); + d->surface->flush(0, r, QPoint()); + } else { + d->surface->flush(0, region, QPoint()); + } +} + +/*! + \since 4.2 + + Raises the reserved region to the top of the widget stack. + + After this call the reserved region will be visually in front of + any overlapping widgets. + + \sa lower(), requestedRegion() +*/ +void QDirectPainter::raise() +{ + QWidget::qwsDisplay()->setAltitude(winId(),QWSChangeAltitudeCommand::Raise); +} + +/*! + \since 4.2 + + Lowers the reserved region to the bottom of the widget stack. + + After this call the reserved region will be visually behind (and + therefore obscured by) any overlapping widgets. + + \sa raise(), requestedRegion() +*/ +void QDirectPainter::lower() +{ + QWidget::qwsDisplay()->setAltitude(winId(),QWSChangeAltitudeCommand::Lower); +} + + +/*! + \fn QRegion QDirectPainter::reserveRegion(const QRegion ®ion) + + Attempts to reserve the \a region and returns the region that is + actually reserved. + + This function also releases the previously reserved region if + any. If not released explicitly, the region will be released on + application exit. + + \sa reservedRegion(), {Static Allocation} + + \obsolete + + Construct a QDirectPainter using QDirectPainter::ReservedSynchronous instead. +*/ +QRegion QDirectPainter::reserveRegion(const QRegion ®) +{ + if (!QDirectPainterPrivate::staticPainter) + QDirectPainterPrivate::staticPainter = new QDirectPainter(qApp, ReservedSynchronous); + + QDirectPainter *dp = QDirectPainterPrivate::staticPainter; + dp->setRegion(reg); + + return dp->allocatedRegion(); +} + +/*! + Returns a pointer to the beginning of the display memory. + + Note that it is the application's responsibility to limit itself + to modifying only the reserved region. + + Do not use this pointer if the current screen has subscreens, + query the screen driver instead: A pointer to the current screen + driver can always be retrieved using the static + QScreen::instance() function. Then use QScreen's \l + {QScreen::}{subScreenIndexAt()} and \l {QScreen::}{subScreens()} + functions to access the correct subscreen, and the subscreen's \l + {QScreen::}{base()} function to retrieve a pointer to the + framebuffer. + + \sa requestedRegion(), allocatedRegion(), linestep() +*/ +uchar* QDirectPainter::frameBuffer() +{ + QScreen *screen = getPrimaryScreen(); + if (!screen) + return 0; + return screen->base(); +} + +/*! + \since 4.2 + + Returns the reserved region. + + \sa reserveRegion(), frameBuffer() + + \obsolete + + Use allocatedRegion() instead. +*/ +QRegion QDirectPainter::reservedRegion() +{ + return QDirectPainterPrivate::staticPainter + ? QDirectPainterPrivate::staticPainter->allocatedRegion() : QRegion(); +} + +/*! + Returns the bit depth of the display. + + \sa screenHeight(), screenWidth() +*/ +int QDirectPainter::screenDepth() +{ + QScreen *screen = getPrimaryScreen(); + if (!screen) + return 0; + return screen->depth(); +} + +/*! + Returns the width of the display in pixels. + + \sa screenHeight(), screenDepth() +*/ +int QDirectPainter::screenWidth() +{ + QScreen *screen = getPrimaryScreen(); + if (!screen) + return 0; + return screen->deviceWidth(); +} + +/*! + Returns the height of the display in pixels. + + \sa screenWidth(), screenDepth() +*/ +int QDirectPainter::screenHeight() +{ + QScreen *screen = getPrimaryScreen(); + if (!screen) + return 0; + return screen->deviceHeight(); +} + +/*! + Returns the length (in bytes) of each scanline of the framebuffer. + + \sa frameBuffer() +*/ +int QDirectPainter::linestep() +{ + QScreen *screen = getPrimaryScreen(); + if (!screen) + return 0; + return screen->linestep(); +} + + +/*! + Locks access to the framebuffer. + + Note that calling this function will prevent all other + applications from updating the display until unlock() is called. + + \sa unlock() +*/ +void QDirectPainter::lock() +{ + QWSDisplay::grab(true); +} +/*! + Unlocks the lock on the framebuffer (set using the lock() + function), allowing other applications to access the screen. + + \sa lock() + */ +void QDirectPainter::unlock() +{ + QWSDisplay::ungrab(); +} + +#endif //QT_NO_DIRECTPAINTER + +#endif + +QT_END_NAMESPACE diff --git a/src/gui/embedded/qdirectpainter_qws.h b/src/gui/embedded/qdirectpainter_qws.h new file mode 100644 index 0000000..8c5c30f --- /dev/null +++ b/src/gui/embedded/qdirectpainter_qws.h @@ -0,0 +1,112 @@ +/**************************************************************************** +** +** 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$ +** +****************************************************************************/ + +#ifndef QDIRECTPAINTER_QWS_H +#define QDIRECTPAINTER_QWS_H + +#include <QtCore/qobject.h> +#include <QtGui/qregion.h> + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +QT_MODULE(Gui) + +#ifndef QT_NO_DIRECTPAINTER +class QDirectPainterPrivate; +class QWSEmbedEvent; + +class Q_GUI_EXPORT QDirectPainter : public QObject { + Q_OBJECT + Q_DECLARE_PRIVATE(QDirectPainter) +public: + + enum SurfaceFlag { NonReserved = 0, + Reserved = 1, + ReservedSynchronous = 3 }; + + explicit QDirectPainter(QObject *parentObject = 0, SurfaceFlag flag = NonReserved); + ~QDirectPainter(); + + void setRegion(const QRegion&); + QRegion requestedRegion() const; + QRegion allocatedRegion() const; + + void setGeometry(const QRect&); + QRect geometry() const; + + WId winId() const; + virtual void regionChanged(const QRegion &exposedRegion); + + void startPainting(bool lockDisplay = true); + void endPainting(); + void endPainting(const QRegion ®ion); + void flush(const QRegion ®ion); + + void raise(); + void lower(); + + + static QRegion reserveRegion(const QRegion&); + static QRegion reservedRegion(); + static QRegion region() { return reservedRegion(); } + + static uchar* frameBuffer(); + static int screenDepth(); + static int screenWidth(); + static int screenHeight(); + static int linestep(); + + static void lock(); + static void unlock(); +private: + friend void qt_directpainter_region(QDirectPainter *dp, const QRegion &alloc, int type); + friend void qt_directpainter_embedevent(QDirectPainter*, const QWSEmbedEvent*); +}; + +#endif + +QT_END_NAMESPACE + +QT_END_HEADER + +#endif // QDIRECTPAINTER_QWS_H diff --git a/src/gui/embedded/qkbd_qws.cpp b/src/gui/embedded/qkbd_qws.cpp new file mode 100644 index 0000000..8cf87db --- /dev/null +++ b/src/gui/embedded/qkbd_qws.cpp @@ -0,0 +1,248 @@ +/**************************************************************************** +** +** 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 "qkbd_qws.h" + +#ifndef QT_NO_QWS_KEYBOARD + +#include "qwindowsystem_qws.h" +#include "qscreen_qws.h" +#include "qtimer.h" +#include <stdlib.h> + +QT_BEGIN_NAMESPACE + +class QWSKbPrivate : public QObject +{ + Q_OBJECT +public: + QWSKbPrivate(QWSKeyboardHandler *h) { + handler = h; + arTimer = new QTimer(this); + arTimer->setSingleShot(true); + connect(arTimer, SIGNAL(timeout()), SLOT(autoRepeat())); + repeatdelay = 400; + repeatperiod = 80; + } + + void beginAutoRepeat(int uni, int code, Qt::KeyboardModifiers mod) { + unicode = uni; + keycode = code; + modifier = mod; + arTimer->start(repeatdelay); + } + void endAutoRepeat() { + arTimer->stop(); + } + +private slots: + void autoRepeat() { + handler->processKeyEvent(unicode, keycode, modifier, false, true); + handler->processKeyEvent(unicode, keycode, modifier, true, true); + arTimer->start(repeatperiod); + } + +private: + QWSKeyboardHandler *handler; + int unicode; + int keycode; + Qt::KeyboardModifiers modifier; + int repeatdelay; + int repeatperiod; + QTimer *arTimer; +}; + +/*! + \class QWSKeyboardHandler + \ingroup qws + + \brief The QWSKeyboardHandler class is a base class for keyboard + drivers in Qt for Embedded Linux. + + Note that this class is only available in \l{Qt for Embedded Linux}. + + \l{Qt for Embedded Linux} provides ready-made drivers for several keyboard + protocols, see the \l{Qt for Embedded Linux Character Input}{character + input} documentation for details. Custom keyboard drivers can be + implemented by subclassing the QWSKeyboardHandler class and + creating a keyboard driver plugin (derived from + QKbdDriverPlugin). The default implementation of the + QKbdDriverFactory class will automatically detect the plugin, and + load the driver into the server application at run-time using Qt's + \l{How to Create Qt Plugins}{plugin system}. + + The keyboard driver receives keyboard events from the system + device and encapsulates each event with an instance of the + QWSEvent class which it then passes to the server application (the + server is responsible for propagating the event to the appropriate + client). To receive keyboard events, a QWSKeyboardHandler object + will usually create a QSocketNotifier object for the given + device. The QSocketNotifier class provides support for monitoring + activity on a file descriptor. When the socket notifier receives + data, it will call the keyboard driver's processKeyEvent() + function to send the event to the \l{Qt for Embedded Linux} server + application for relaying to clients. + + + QWSKeyboardHandler also provides functions to control + auto-repetion of key sequences, beginAutoRepeat() and + endAutoRepeat(), and the transformDirKey() function enabling + transformation of arrow keys according to the display orientation. + + \sa QKbdDriverPlugin, QKbdDriverFactory, {Qt for Embedded Linux Character Input} +*/ + + +/*! + Constructs a keyboard driver. + + Call the QWSServer::setKeyboardHandler() function to make the + newly created keyboard driver, the primary driver. Note that the + primary driver is controlled by the system, i.e., the system will + delete it upon exit. +*/ +QWSKeyboardHandler::QWSKeyboardHandler() +{ + d = new QWSKbPrivate(this); +} + +/*! + Destroys this keyboard driver. + + Do not call this function if this driver is the primary keyboard + handler, i.e., if QWSServer::setKeyboardHandler() function has + been called passing this driver as argument. The primary keyboard + driver is deleted by the system. +*/ +QWSKeyboardHandler::~QWSKeyboardHandler() +{ + delete d; +} + + +/*! + Sends a key event to the \l{Qt for Embedded Linux} server application. + + The key event is identified by its \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. + + \sa beginAutoRepeat(), endAutoRepeat(), transformDirKey() +*/ +void QWSKeyboardHandler::processKeyEvent(int unicode, int keycode, Qt::KeyboardModifiers modifiers, + bool isPress, bool autoRepeat) +{ + qwsServer->processKeyEvent(unicode, keycode, modifiers, isPress, autoRepeat); +} + +/*! + \fn int QWSKeyboardHandler::transformDirKey(int keycode) + + Transforms the arrow key specified by the given \a keycode, to the + orientation of the display and returns the transformed keycode. + + The \a keycode is a Qt::Key value. The values identifying arrow + keys are: + + \list + \o Qt::Key_Left + \o Qt::Key_Up + \o Qt::Key_Right + \o Qt::Key_Down + \endlist + + \sa processKeyEvent() + */ +int QWSKeyboardHandler::transformDirKey(int key) +{ + static int dir_keyrot = -1; + if (dir_keyrot < 0) { + // get the rotation + switch (qgetenv("QWS_CURSOR_ROTATION").toInt()) { + case 90: dir_keyrot = 1; break; + case 180: dir_keyrot = 2; break; + case 270: dir_keyrot = 3; break; + default: dir_keyrot = 0; break; + } + } + int xf = qt_screen->transformOrientation() + dir_keyrot; + return (key-Qt::Key_Left+xf)%4+Qt::Key_Left; +} + +/*! + \fn void QWSKeyboardHandler::beginAutoRepeat(int unicode, int keycode, Qt::KeyboardModifiers modifier) + + Begins auto-repeating the specified key press; after a short delay + the key press is sent periodically until the endAutoRepeat() + function is called. + + The key press is specified by its \a unicode, \a keycode and \a + modifier state. + + \sa endAutoRepeat(), processKeyEvent() +*/ +void QWSKeyboardHandler::beginAutoRepeat(int uni, int code, Qt::KeyboardModifiers mod) +{ + d->beginAutoRepeat(uni, code, mod); +} + +/*! + Stops auto-repeating a key press. + + \sa beginAutoRepeat(), processKeyEvent() +*/ +void QWSKeyboardHandler::endAutoRepeat() +{ + d->endAutoRepeat(); +} + +QT_END_NAMESPACE + +#include "qkbd_qws.moc" + +#endif // QT_NO_QWS_KEYBOARD diff --git a/src/gui/embedded/qkbd_qws.h b/src/gui/embedded/qkbd_qws.h new file mode 100644 index 0000000..8809f0a --- /dev/null +++ b/src/gui/embedded/qkbd_qws.h @@ -0,0 +1,81 @@ +/**************************************************************************** +** +** 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$ +** +****************************************************************************/ + +#ifndef QKBD_QWS_H +#define QKBD_QWS_H + +#include <QtGui/qapplication.h> + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +QT_MODULE(Gui) + +#ifndef QT_NO_QWS_KEYBOARD + +class QWSKbPrivate; + +class Q_GUI_EXPORT QWSKeyboardHandler +{ +public: + QWSKeyboardHandler(); + virtual ~QWSKeyboardHandler(); + + virtual void processKeyEvent(int unicode, int keycode, Qt::KeyboardModifiers modifiers, + bool isPress, bool autoRepeat); + +protected: + int transformDirKey(int key); + void beginAutoRepeat(int uni, int code, Qt::KeyboardModifiers mod); + void endAutoRepeat(); + +private: + QWSKbPrivate *d; +}; + +#endif // QT_NO_QWS_KEYBOARD + +QT_END_NAMESPACE + +QT_END_HEADER + +#endif // QKBD_QWS_H diff --git a/src/gui/embedded/qkbddriverfactory_qws.cpp b/src/gui/embedded/qkbddriverfactory_qws.cpp new file mode 100644 index 0000000..1ade652 --- /dev/null +++ b/src/gui/embedded/qkbddriverfactory_qws.cpp @@ -0,0 +1,193 @@ +/**************************************************************************** +** +** 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 "qkbddriverfactory_qws.h" + +#ifndef QT_NO_QWS_KEYBOARD + +#include "qapplication.h" +#include "qkbdtty_qws.h" +#include "qkbdusb_qws.h" +#include "qkbdum_qws.h" +#include "qkbdsl5000_qws.h" +#include "qkbdvfb_qws.h" +#include "qkbdyopy_qws.h" +#include "qkbdvr41xx_qws.h" +#include <stdlib.h> +#include "private/qfactoryloader_p.h" +#include "qkbddriverplugin_qws.h" + +QT_BEGIN_NAMESPACE + +#if !defined(Q_OS_WIN32) || defined(QT_MAKEDLL) +#ifndef QT_NO_LIBRARY +Q_GLOBAL_STATIC_WITH_ARGS(QFactoryLoader, loader, + (QWSKeyboardHandlerFactoryInterface_iid, + QLatin1String("/kbddrivers"), Qt::CaseInsensitive)) + +#endif //QT_NO_LIBRARY +#endif //QT_MAKEDLL + +/*! + \class QKbdDriverFactory + \ingroup qws + + \brief The QKbdDriverFactory class creates keyboard drivers in + Qt for Embedded Linux. + + Note that this class is only available in \l{Qt for Embedded Linux}. + + QKbdDriverFactory is used to detect and instantiate the available + keyboard drivers, allowing \l{Qt for Embedded Linux} to load the preferred + driver into the server application at runtime. The create() + function returns a QWSKeyboardHandler object representing the + keyboard driver identified by a given key. The valid keys + (i.e. the supported drivers) can be retrieved using the keys() + function. + + \l{Qt for Embedded Linux} provides several built-in keyboard drivers. In + addition, custom keyboard drivers can be added using Qt's plugin + mechanism, i.e. by subclassing the QWSKeyboardHandler class and + creating a keyboard driver plugin (QKbdDriverPlugin). See the + \l{Qt for Embedded Linux Character Input}{character input} documentation + for details. + + \sa QWSKeyboardHandler, QKbdDriverPlugin +*/ + +/*! + Creates the keyboard driver specified by the given \a key, using + the display specified by the given \a device. + + Note that the keys are case-insensitive. + + \sa keys() +*/ +QWSKeyboardHandler *QKbdDriverFactory::create(const QString& key, const QString& device) +{ + QString driver = key.toLower(); +#ifndef QT_NO_QWS_KBD_SL5000 + if (driver == QLatin1String("sl5000") || driver.isEmpty()) + return new QWSSL5000KeyboardHandler(device); +#endif +#ifndef QT_NO_QWS_KBD_YOPY + if (driver == QLatin1String("yopy") || driver.isEmpty()) + return new QWSYopyKeyboardHandler(device); +#endif +#ifndef QT_NO_QWS_KBD_VR41XX + if (driver == QLatin1String("vr41xx") || driver.isEmpty()) + return new QWSVr41xxKeyboardHandler(device); +#endif +#ifndef QT_NO_QWS_KEYBOARD +# ifndef QT_NO_QWS_KBD_TTY + if (driver == QLatin1String("tty") || driver.isEmpty()) + return new QWSTtyKeyboardHandler(device); +# endif +# ifndef QT_NO_QWS_KBD_USB + if (driver == QLatin1String("usb")) + return new QWSUsbKeyboardHandler(device); +# endif +# ifndef QT_NO_QWS_KBD_UM + if (driver == QLatin1String("um") || driver == QLatin1String("qvfbkeyboard")) + return new QWSUmKeyboardHandler(device); +# endif +# ifndef QT_NO_QWS_KBD_QVFB + if (driver == QLatin1String("qvfbkbd") + || driver == QLatin1String("qvfbkeyboard") + || driver == QLatin1String("qvfb")) + return new QVFbKeyboardHandler(device); +# endif +#endif + +#if !defined(Q_OS_WIN32) || defined(QT_MAKEDLL) +#ifndef QT_NO_LIBRARY + if (QWSKeyboardHandlerFactoryInterface *factory = qobject_cast<QWSKeyboardHandlerFactoryInterface*>(loader()->instance(driver))) + return factory->create(driver, device); +#endif +#endif + return 0; +} + +/*! + Returns the list of valid keys, i.e. the available keyboard + drivers. + + \sa create() +*/ +QStringList QKbdDriverFactory::keys() +{ + QStringList list; + +#ifndef QT_NO_QWS_KBD_SL5000 + list << QLatin1String("SL5000"); +#endif +#ifndef QT_NO_QWS_KBD_YOPY + list << QLatin1String("YOPY"); +#endif +#ifndef QT_NO_QWS_KBD_VR41XX + list << QLatin1String("VR41xx"); +#endif +#ifndef QT_NO_QWS_KBD_TTY + list << QLatin1String("TTY"); +#endif +#ifndef QT_NO_QWS_KBD_USB + list << QLatin1String("USB"); +#endif +#ifndef QT_NO_QWS_KBD_UM + list << QLatin1String("UM"); +#endif + +#if !defined(Q_OS_WIN32) || defined(QT_MAKEDLL) +#ifndef QT_NO_LIBRARY + QStringList plugins = loader()->keys(); + for (int i = 0; i < plugins.size(); ++i) { + if (!list.contains(plugins.at(i))) + list += plugins.at(i); + } +#endif //QT_NO_LIBRARY +#endif //QT_MAKEDLL + + return list; +} + +QT_END_NAMESPACE + +#endif // QT_NO_QWS_KEYBOARD diff --git a/src/gui/embedded/qkbddriverfactory_qws.h b/src/gui/embedded/qkbddriverfactory_qws.h new file mode 100644 index 0000000..45e5664 --- /dev/null +++ b/src/gui/embedded/qkbddriverfactory_qws.h @@ -0,0 +1,70 @@ +/**************************************************************************** +** +** 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$ +** +****************************************************************************/ + +#ifndef QKBDDRIVERFACTORY_QWS_H +#define QKBDDRIVERFACTORY_QWS_H + +#include <QtCore/qstringlist.h> + +#ifndef QT_NO_QWS_KEYBOARD + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +QT_MODULE(Gui) + +class QString; +class QWSKeyboardHandler; + +class Q_GUI_EXPORT QKbdDriverFactory +{ +public: + static QStringList keys(); + static QWSKeyboardHandler *create(const QString&, const QString&); +}; + +QT_END_NAMESPACE + +QT_END_HEADER + +#endif // QT_NO_QWS_KEYBOARD +#endif // QKBDDRIVERFACTORY_QWS_H diff --git a/src/gui/embedded/qkbddriverplugin_qws.cpp b/src/gui/embedded/qkbddriverplugin_qws.cpp new file mode 100644 index 0000000..93dd9df --- /dev/null +++ b/src/gui/embedded/qkbddriverplugin_qws.cpp @@ -0,0 +1,124 @@ +/**************************************************************************** +** +** 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 "qkbddriverplugin_qws.h" + +#ifndef QT_NO_LIBRARY + +#include "qkbd_qws.h" + +QT_BEGIN_NAMESPACE + +/*! + \class QKbdDriverPlugin + \ingroup plugins + \ingroup qws + + \brief The QKbdDriverPlugin class is an abstract base class for + keyboard driver plugins in Qt for Embedded Linux. + + Note that this class is only available in \l{Qt for Embedded Linux}. + + \l{Qt for Embedded Linux} provides ready-made drivers for several keyboard + protocols, see the \l{Qt for Embedded Linux Character Input}{character + input} documentation for details. Custom keyboard drivers can be + implemented by subclassing the QWSKeyboardHandler class and + creating a keyboard driver plugin. + + A keyboard driver plugin can be created by subclassing + QKbdDriverPlugin and reimplementing the pure virtual keys() and + create() functions. By exporting the derived class using the + Q_EXPORT_PLUGIN2() macro, the default implementation of the + QKbdDriverFactory class will automatically detect the plugin and + load the driver into the server application at run-time. See + \l{How to Create Qt Plugins} for details. + + \sa QKbdDriverFactory, QWSKeyboardHandler +*/ + +/*! + \fn QStringList QKbdDriverPlugin::keys() const + + Implement this function to return the list of valid keys, i.e. the + keyboard drivers supported by this plugin. + + \l{Qt for Embedded Linux} provides ready-made drivers for several keyboard + protocols, see the \l{Qt for Embedded Linux Character Input}{character + input} documentation for details. + + \sa create() +*/ + +/*! + Constructs a keyboard driver plugin with the given \a parent. + + Note that this constructor is invoked automatically by the + Q_EXPORT_PLUGIN2() macro, so there is no need for calling it + explicitly. +*/ +QKbdDriverPlugin::QKbdDriverPlugin(QObject *parent) + : QObject(parent) +{ +} + +/*! + Destroys the keyboard driver plugin. + + Note that Qt destroys a plugin automatically when it is no longer + used, so there is no need for calling the destructor explicitly. +*/ +QKbdDriverPlugin::~QKbdDriverPlugin() +{ +} + +/*! + \fn QScreen *QKbdDriverPlugin::create(const QString &key, const QString &device) + + Implement this function to create a driver matching the type + specified by the given \a key and \a device parameters. Note that + keys are case-insensitive. + + \sa keys() +*/ + +QT_END_NAMESPACE + +#endif // QT_NO_LIBRARY diff --git a/src/gui/embedded/qkbddriverplugin_qws.h b/src/gui/embedded/qkbddriverplugin_qws.h new file mode 100644 index 0000000..e320b17 --- /dev/null +++ b/src/gui/embedded/qkbddriverplugin_qws.h @@ -0,0 +1,84 @@ +/**************************************************************************** +** +** 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$ +** +****************************************************************************/ + +#ifndef QKBDDRIVERPLUGIN_QWS_H +#define QKBDDRIVERPLUGIN_QWS_H + +#include <QtCore/qplugin.h> +#include <QtCore/qfactoryinterface.h> + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +QT_MODULE(Gui) + +#ifndef QT_NO_LIBRARY + +class QWSKeyboardHandler; + +struct Q_GUI_EXPORT QWSKeyboardHandlerFactoryInterface : public QFactoryInterface +{ + virtual QWSKeyboardHandler* create(const QString &name, const QString &device) = 0; +}; + +#define QWSKeyboardHandlerFactoryInterface_iid "com.trolltech.Qt.QWSKeyboardHandlerFactoryInterface" +Q_DECLARE_INTERFACE(QWSKeyboardHandlerFactoryInterface, QWSKeyboardHandlerFactoryInterface_iid) + +class Q_GUI_EXPORT QKbdDriverPlugin : public QObject, public QWSKeyboardHandlerFactoryInterface +{ + Q_OBJECT + Q_INTERFACES(QWSKeyboardHandlerFactoryInterface:QFactoryInterface) +public: + explicit QKbdDriverPlugin(QObject *parent = 0); + ~QKbdDriverPlugin(); + + virtual QStringList keys() const = 0; + virtual QWSKeyboardHandler* create(const QString& driver, const QString &device) = 0; +}; + +#endif // QT_NO_LIBRARY + +QT_END_NAMESPACE + +QT_END_HEADER + +#endif // QKBDDRIVERPLUGIN_QWS_H diff --git a/src/gui/embedded/qkbdpc101_qws.cpp b/src/gui/embedded/qkbdpc101_qws.cpp new file mode 100644 index 0000000..3173645 --- /dev/null +++ b/src/gui/embedded/qkbdpc101_qws.cpp @@ -0,0 +1,485 @@ +/**************************************************************************** +** +** 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 "qkbdpc101_qws.h" + +#ifndef QT_NO_QWS_KEYBOARD + +#include "qscreen_qws.h" +#include "qwindowsystem_qws.h" +#include "qnamespace.h" +#include "qapplication.h" + +#include <unistd.h> +#include <sys/ioctl.h> +#include <sys/types.h> +#include <sys/stat.h> +#include <fcntl.h> +#include <errno.h> + +#ifdef Q_OS_LINUX +#include <sys/kd.h> +#include <sys/vt.h> +#endif + +QT_BEGIN_NAMESPACE + +static const QWSKeyMap pc101KeyM[] = { + { Qt::Key_unknown, 0xffff , 0xffff , 0xffff }, + { Qt::Key_Escape, 27 , 27 , 0xffff }, + { Qt::Key_1, '1' , '!' , 0xffff }, + { Qt::Key_2, '2' , '@' , 0xffff }, + { Qt::Key_3, '3' , '#' , 0xffff }, + { Qt::Key_4, '4' , '$' , 0xffff }, + { Qt::Key_5, '5' , '%' , 0xffff }, + { Qt::Key_6, '6' , '^' , 0xffff }, + { Qt::Key_7, '7' , '&' , 0xffff }, + { Qt::Key_8, '8' , '*' , 0xffff }, + { Qt::Key_9, '9' , '(' , 0xffff }, // 10 + { Qt::Key_0, '0' , ')' , 0xffff }, + { Qt::Key_Minus, '-' , '_' , 0xffff }, + { Qt::Key_Equal, '=' , '+' , 0xffff }, + { Qt::Key_Backspace, 8 , 8 , 0xffff }, + { Qt::Key_Tab, 9 , 9 , 0xffff }, + { Qt::Key_Q, 'q' , 'Q' , 'Q'-64 }, + { Qt::Key_W, 'w' , 'W' , 'W'-64 }, + { Qt::Key_E, 'e' , 'E' , 'E'-64 }, + { Qt::Key_R, 'r' , 'R' , 'R'-64 }, + { Qt::Key_T, 't' , 'T' , 'T'-64 }, // 20 + { Qt::Key_Y, 'y' , 'Y' , 'Y'-64 }, + { Qt::Key_U, 'u' , 'U' , 'U'-64 }, + { Qt::Key_I, 'i' , 'I' , 'I'-64 }, + { Qt::Key_O, 'o' , 'O' , 'O'-64 }, + { Qt::Key_P, 'p' , 'P' , 'P'-64 }, + { Qt::Key_BraceLeft, '[' , '{' , 0xffff }, + { Qt::Key_BraceRight, ']' , '}' , 0xffff }, + { Qt::Key_Return, 13 , 13 , 0xffff }, + { Qt::Key_Control, 0xffff , 0xffff , 0xffff }, + { Qt::Key_A, 'a' , 'A' , 'A'-64 }, // 30 + { Qt::Key_S, 's' , 'S' , 'S'-64 }, + { Qt::Key_D, 'd' , 'D' , 'D'-64 }, + { Qt::Key_F, 'f' , 'F' , 'F'-64 }, + { Qt::Key_G, 'g' , 'G' , 'G'-64 }, + { Qt::Key_H, 'h' , 'H' , 'H'-64 }, + { Qt::Key_J, 'j' , 'J' , 'J'-64 }, + { Qt::Key_K, 'k' , 'K' , 'K'-64 }, + { Qt::Key_L, 'l' , 'L' , 'L'-64 }, + { Qt::Key_Semicolon, ';' , ':' , 0xffff }, + { Qt::Key_Apostrophe, '\'' , '"' , 0xffff }, // 40 + { Qt::Key_QuoteLeft, '`' , '~' , 0xffff }, + { Qt::Key_Shift, 0xffff , 0xffff , 0xffff }, + { Qt::Key_Backslash, '\\' , '|' , 0xffff }, + { Qt::Key_Z, 'z' , 'Z' , 'Z'-64 }, + { Qt::Key_X, 'x' , 'X' , 'X'-64 }, + { Qt::Key_C, 'c' , 'C' , 'C'-64 }, + { Qt::Key_V, 'v' , 'V' , 'V'-64 }, + { Qt::Key_B, 'b' , 'B' , 'B'-64 }, + { Qt::Key_N, 'n' , 'N' , 'N'-64 }, + { Qt::Key_M, 'm' , 'M' , 'M'-64 }, // 50 + { Qt::Key_Comma, ',' , '<' , 0xffff }, + { Qt::Key_Period, '.' , '>' , 0xffff }, + { Qt::Key_Slash, '/' , '?' , 0xffff }, + { Qt::Key_Shift, 0xffff , 0xffff , 0xffff }, + { Qt::Key_Asterisk, '*' , '*' , 0xffff }, + { Qt::Key_Alt, 0xffff , 0xffff , 0xffff }, + { Qt::Key_Space, ' ' , ' ' , 0xffff }, + { Qt::Key_CapsLock, 0xffff , 0xffff , 0xffff }, + { Qt::Key_F1, 0xffff , 0xffff , 0xffff }, + { Qt::Key_F2, 0xffff , 0xffff , 0xffff }, // 60 + { Qt::Key_F3, 0xffff , 0xffff , 0xffff }, + { Qt::Key_F4, 0xffff , 0xffff , 0xffff }, + { Qt::Key_F5, 0xffff , 0xffff , 0xffff }, + { Qt::Key_F6, 0xffff , 0xffff , 0xffff }, + { Qt::Key_F7, 0xffff , 0xffff , 0xffff }, + { Qt::Key_F8, 0xffff , 0xffff , 0xffff }, + { Qt::Key_F9, 0xffff , 0xffff , 0xffff }, + { Qt::Key_F10, 0xffff , 0xffff , 0xffff }, + { Qt::Key_NumLock, 0xffff , 0xffff , 0xffff }, + { Qt::Key_ScrollLock, 0xffff , 0xffff , 0xffff }, // 70 + { Qt::Key_7, '7' , '7' , 0xffff }, + { Qt::Key_8, '8' , '8' , 0xffff }, + { Qt::Key_9, '9' , '9' , 0xffff }, + { Qt::Key_Minus, '-' , '-' , 0xffff }, + { Qt::Key_4, '4' , '4' , 0xffff }, + { Qt::Key_5, '5' , '5' , 0xffff }, + { Qt::Key_6, '6' , '6' , 0xffff }, + { Qt::Key_Plus, '+' , '+' , 0xffff }, + { Qt::Key_1, '1' , '1' , 0xffff }, + { Qt::Key_2, '2' , '2' , 0xffff }, // 80 + { Qt::Key_3, '3' , '3' , 0xffff }, + { Qt::Key_0, '0' , '0' , 0xffff }, + { Qt::Key_Period, '.' , '.' , 0xffff }, + { Qt::Key_SysReq, 0xffff , 0xffff , 0xffff }, + { Qt::Key_unknown, 0xffff , 0xffff , 0xffff }, + { Qt::Key_Less, '<' , '>' , 0xffff }, + { Qt::Key_F11, 0xffff , 0xffff , 0xffff }, + { Qt::Key_F12, 0xffff , 0xffff , 0xffff }, + { Qt::Key_unknown, 0xffff , 0xffff , 0xffff }, + { Qt::Key_unknown, 0xffff , 0xffff , 0xffff }, // 90 + { Qt::Key_unknown, 0xffff , 0xffff , 0xffff }, + { Qt::Key_unknown, 0xffff , 0xffff , 0xffff }, + { Qt::Key_unknown, 0xffff , 0xffff , 0xffff }, + { Qt::Key_unknown, 0xffff , 0xffff , 0xffff }, + { Qt::Key_unknown, 0xffff , 0xffff , 0xffff }, + { Qt::Key_Enter, 13 , 13 , 0xffff }, + { Qt::Key_Control, 0xffff , 0xffff , 0xffff }, + { Qt::Key_Slash, '/' , '/' , 0xffff }, + { Qt::Key_SysReq, 0xffff , 0xffff , 0xffff }, + { Qt::Key_Meta, 0xffff , 0xffff , 0xffff }, // 100 + { Qt::Key_unknown, 0xffff , 0xffff , 0xffff }, // break + { Qt::Key_Home, 0xffff , 0xffff , 0xffff }, + { Qt::Key_Up, 0xffff , 0xffff , 0xffff }, + { Qt::Key_PageUp, 0xffff , 0xffff , 0xffff }, + { Qt::Key_Left, 0xffff , 0xffff , 0xffff }, + { Qt::Key_Right, 0xffff , 0xffff , 0xffff }, + { Qt::Key_End, 0xffff , 0xffff , 0xffff }, + { Qt::Key_Down, 0xffff , 0xffff , 0xffff }, + { Qt::Key_PageDown, 0xffff , 0xffff , 0xffff }, + { Qt::Key_Insert, 0xffff , 0xffff , 0xffff }, // 110 + { Qt::Key_Delete, 0xffff , 0xffff , 0xffff }, + { Qt::Key_unknown, 0xffff , 0xffff , 0xffff }, // macro + { Qt::Key_F13, 0xffff , 0xffff , 0xffff }, + { Qt::Key_F14, 0xffff , 0xffff , 0xffff }, + { Qt::Key_Help, 0xffff , 0xffff , 0xffff }, + { Qt::Key_unknown, 0xffff , 0xffff , 0xffff }, // do + { Qt::Key_F17, 0xffff , 0xffff , 0xffff }, + { Qt::Key_Plus, '+' , '-' , 0xffff }, + { Qt::Key_Pause, 0xffff , 0xffff , 0xffff }, + { Qt::Key_unknown, 0xffff , 0xffff , 0xffff }, + { Qt::Key_unknown, 0xffff , 0xffff , 0xffff }, + { Qt::Key_unknown, 0xffff , 0xffff , 0xffff }, + { Qt::Key_unknown, 0xffff , 0xffff , 0xffff }, + { Qt::Key_unknown, 0xffff , 0xffff , 0xffff }, + { Qt::Key_unknown, 0xffff , 0xffff , 0xffff }, + { Qt::Key_unknown, 0xffff , 0xffff , 0xffff }, + { Qt::Key_unknown, 0xffff , 0xffff , 0xffff }, + { 0, 0xffff , 0xffff , 0xffff } +}; + +static const int keyMSize = sizeof(pc101KeyM)/sizeof(QWSKeyMap)-1; + +//=========================================================================== + +// +// PC-101 type keyboards +// + +/*! + \class QWSPC101KeyboardHandler + \ingroup qws + + \internal +*/ + +QWSPC101KeyboardHandler::QWSPC101KeyboardHandler(const QString&) +{ + shift = false; + alt = false; + ctrl = false; + extended = 0; + prevuni = 0; + prevkey = 0; + caps = false; +#if defined(QT_QWS_IPAQ) + // iPAQ Action Key has ScanCode 0x60: 0x60|0x80 = 0xe0 == extended mode 1 ! + ipaq_return_pressed = false; +#endif +} + +QWSPC101KeyboardHandler::~QWSPC101KeyboardHandler() +{ +} + +const QWSKeyMap *QWSPC101KeyboardHandler::keyMap() const +{ + return pc101KeyM; +} + +void QWSPC101KeyboardHandler::doKey(uchar code) +{ + + int keyCode = Qt::Key_unknown; + bool release = false; + int keypad = 0; + bool softwareRepeat = false; + +#ifndef QT_QWS_USE_KEYCODES + // extended? + if (code == 224 +#if defined(QT_QWS_IPAQ) + && !ipaq_return_pressed +#endif + ) { + extended = 1; + return; + } else if (code == 225) { + extended = 2; + return; + } +#endif + + if (code & 0x80) { + release = true; + code &= 0x7f; + } + +#ifndef QT_QWS_USE_KEYCODES + if (extended == 1) { + switch (code) { + case 72: + keyCode = Qt::Key_Up; + break; + case 75: + keyCode = Qt::Key_Left; + break; + case 77: + keyCode = Qt::Key_Right; + break; + case 80: + keyCode = Qt::Key_Down; + break; + case 82: + keyCode = Qt::Key_Insert; + break; + case 71: + keyCode = Qt::Key_Home; + break; + case 73: + keyCode = Qt::Key_PageUp; + break; + case 83: + keyCode = Qt::Key_Delete; + break; + case 79: + keyCode = Qt::Key_End; + break; + case 81: + keyCode = Qt::Key_PageDown; + break; + case 28: + keyCode = Qt::Key_Enter; + break; + case 53: + keyCode = Qt::Key_Slash; + break; + case 0x1d: + keyCode = Qt::Key_Control; + break; + case 0x2a: + keyCode = Qt::Key_Print; + break; + case 0x38: + keyCode = Qt::Key_Alt; + break; + case 0x5b: + keyCode = Qt::Key_Super_L; + break; + case 0x5c: + keyCode = Qt::Key_Super_R; + break; + case 0x5d: + keyCode = Qt::Key_Menu; + break; +#if 0 + default: + qDebug("extended1 code %x release %d", code, release); + break; +#endif + } + } else if (extended == 2) { + switch (code) { + case 0x1d: + return; + case 0x45: + keyCode = Qt::Key_Pause; + break; + } + } else +#endif + { + if (code < keyMSize) { + keyCode = pc101KeyM[code].key_code; + } + +#if defined(QT_QWS_IPAQ) || defined(QT_QWS_EBX) + softwareRepeat = true; + + switch (code) { + case 0x7a: case 0x7b: case 0x7c: case 0x7d: + keyCode = code - 0x7a + Qt::Key_F9; + softwareRepeat = false; + break; + case 0x79: + keyCode = Qt::Key_SysReq; + softwareRepeat = false; + break; + case 0x78: +# ifdef QT_QWS_IPAQ + keyCode = Qt::Key_F24; // record +# else + keyCode = Qt::Key_Escape; +# endif + softwareRepeat = false; + break; + case 0x60: + keyCode = Qt::Key_Return; +# ifdef QT_QWS_IPAQ + ipaq_return_pressed = !release; +# endif + break; + case 0x67: + keyCode = Qt::Key_Right; + break; + case 0x69: + keyCode = Qt::Key_Up; + break; + case 0x6a: + keyCode = Qt::Key_Down; + break; + case 0x6c: + keyCode = Qt::Key_Left; + break; + } + + if (qt_screen->isTransformed() + && keyCode >= Qt::Key_Left && keyCode <= Qt::Key_Down) + { + keyCode = transformDirKey(keyCode); + } +#endif + /* + Translate shift+Qt::Key_Tab to Qt::Key_Backtab + */ + if ((keyCode == Qt::Key_Tab) && shift) + keyCode = Qt::Key_Backtab; + } + +#ifndef QT_QWS_USE_KEYCODES + /* + Qt::Keypad consists of extended keys 53 and 28, + and non-extended keys 55 and 71 through 83. + */ + if ((extended == 1) ? (code == 53 || code == 28) : + (code == 55 || (code >= 71 && code <= 83))) +#else + if (code == 55 || code >= 71 && code <= 83 || code == 96 + || code == 98 || code == 118) +#endif + { + keypad = Qt::KeypadModifier; + } + + // Ctrl-Alt-Backspace exits qws + if (ctrl && alt && keyCode == Qt::Key_Backspace) { + qApp->quit(); + } + + if (keyCode == Qt::Key_Alt) { + alt = !release; + } else if (keyCode == Qt::Key_Control) { + ctrl = !release; + } else if (keyCode == Qt::Key_Shift) { + shift = !release; + } else if (keyCode == Qt::Key_CapsLock && release) { + caps = !caps; +#if defined(Q_OS_LINUX) + char leds; + ioctl(0, KDGETLED, &leds); + leds = leds & ~LED_CAP; + if (caps) leds |= LED_CAP; + ioctl(0, KDSETLED, leds); +#endif + } + if (keyCode != Qt::Key_unknown) { + bool bAlt = alt; + bool bCtrl = ctrl; + bool bShift = shift; + int unicode = 0; + if (code < keyMSize) { + if (!extended) { + bool bCaps = shift || + (caps ? QChar(keyMap()[code].unicode).isLetter() : false); + if (bCtrl) + unicode = keyMap()[code].ctrl_unicode ? keyMap()[code].ctrl_unicode : 0xffff; + else if (bCaps) + unicode = keyMap()[code].shift_unicode ? keyMap()[code].shift_unicode : 0xffff; + else + unicode = keyMap()[code].unicode ? keyMap()[code].unicode : 0xffff; +#ifndef QT_QWS_USE_KEYCODES + } else if (extended==1) { + if (code == 53) + unicode = '/'; +#endif + } + } + + modifiers = 0; + if (bAlt) modifiers |= Qt::AltModifier; + if (bCtrl) modifiers |= Qt::ControlModifier; + if (bShift) modifiers |= Qt::ShiftModifier; + if (keypad) modifiers |= Qt::KeypadModifier; + + // looks wrong -- WWA + bool repeat = false; + if (prevuni == unicode && prevkey == keyCode && !release) + repeat = true; + + processKeyEvent(unicode, keyCode, modifiers, !release, repeat); + + if (!release) { + prevuni = unicode; + prevkey = keyCode; + } else { + prevkey = prevuni = 0; + } + } + + if (softwareRepeat && !release) + beginAutoRepeat(prevuni, prevkey, modifiers); + else + endAutoRepeat(); + + extended = 0; +} + +QT_END_NAMESPACE + +#endif // QT_NO_QWS_KEYBOARD diff --git a/src/gui/embedded/qkbdpc101_qws.h b/src/gui/embedded/qkbdpc101_qws.h new file mode 100644 index 0000000..f9f0104 --- /dev/null +++ b/src/gui/embedded/qkbdpc101_qws.h @@ -0,0 +1,95 @@ +/**************************************************************************** +** +** 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$ +** +****************************************************************************/ + +#ifndef QKBDPC101_QWS_H +#define QKBDPC101_QWS_H + +#include <QtGui/qkbd_qws.h> + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +QT_MODULE(Gui) + +#ifndef QT_NO_QWS_KEYBOARD + +#ifndef QT_NO_QWS_KBD_PC101 + +struct QWSKeyMap { + uint key_code; + ushort unicode; + ushort shift_unicode; + ushort ctrl_unicode; +}; + +class QWSPC101KeyboardHandler : public QWSKeyboardHandler +{ +public: + explicit QWSPC101KeyboardHandler(const QString&); + virtual ~QWSPC101KeyboardHandler(); + + virtual void doKey(uchar scancode); + virtual const QWSKeyMap *keyMap() const; + +protected: + bool shift; + bool alt; + bool ctrl; + bool caps; +#if defined(QT_QWS_IPAQ) + uint ipaq_return_pressed:1; +#endif + uint extended:2; + Qt::KeyboardModifiers modifiers; + int prevuni; + int prevkey; +}; + +#endif // QT_NO_QWS_KBD_PC101 + +#endif // QT_NO_QWS_KEYBOARD + +QT_END_NAMESPACE + +QT_END_HEADER + +#endif // QKBDPC101_QWS_H diff --git a/src/gui/embedded/qkbdsl5000_qws.cpp b/src/gui/embedded/qkbdsl5000_qws.cpp new file mode 100644 index 0000000..bc412b6 --- /dev/null +++ b/src/gui/embedded/qkbdsl5000_qws.cpp @@ -0,0 +1,356 @@ +/**************************************************************************** +** +** 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 "qkbdsl5000_qws.h" + +#ifndef QT_NO_QWS_KBD_SL5000 + +#include "qwindowsystem_qws.h" +#include "qwsutils_qws.h" +#include "qscreen_qws.h" + +#include "qapplication.h" +#include "qnamespace.h" +#include "qtimer.h" + +#include <unistd.h> +#include <sys/ioctl.h> +#include <sys/types.h> +#include <sys/stat.h> +#include <fcntl.h> +#include <errno.h> +#include <signal.h> + +#include <asm/sharp_char.h> + +#include <string.h> + +QT_BEGIN_NAMESPACE + +static const QWSKeyMap sl5000KeyMap[] = { + { Qt::Key_unknown, 0xffff , 0xffff , 0xffff }, // 00 + { Qt::Key_A, 'a' , 'A' , 'A'-64 }, // 01 + { Qt::Key_B, 'b' , 'B' , 'B'-64 }, // 02 + { Qt::Key_C, 'c' , 'C' , 'C'-64 }, // 03 + { Qt::Key_D, 'd' , 'D' , 'D'-64 }, // 04 + { Qt::Key_E, 'e' , 'E' , 'E'-64 }, // 05 + { Qt::Key_F, 'f' , 'F' , 'F'-64 }, // 06 + { Qt::Key_G, 'g' , 'G' , 'G'-64 }, // 07 + { Qt::Key_H, 'h' , 'H' , 'H'-64 }, // 08 + { Qt::Key_I, 'i' , 'I' , 'I'-64 }, // 09 + { Qt::Key_J, 'j' , 'J' , 'J'-64 }, // 0a 10 + { Qt::Key_K, 'k' , 'K' , 'K'-64 }, // 0b + { Qt::Key_L, 'l' , 'L' , 'L'-64 }, // 0c + { Qt::Key_M, 'm' , 'M' , 'M'-64 }, // 0d + { Qt::Key_N, 'n' , 'N' , 'N'-64 }, // 0e + { Qt::Key_O, 'o' , 'O' , 'O'-64 }, // 0f + { Qt::Key_P, 'p' , 'P' , 'P'-64 }, // 10 + { Qt::Key_Q, 'q' , 'Q' , 'Q'-64 }, // 11 + { Qt::Key_R, 'r' , 'R' , 'R'-64 }, // 12 + { Qt::Key_S, 's' , 'S' , 'S'-64 }, // 13 + { Qt::Key_T, 't' , 'T' , 'T'-64 }, // 14 20 + { Qt::Key_U, 'u' , 'U' , 'U'-64 }, // 15 + { Qt::Key_V, 'v' , 'V' , 'V'-64 }, // 16 + { Qt::Key_W, 'w' , 'W' , 'W'-64 }, // 17 + { Qt::Key_X, 'x' , 'X' , 'X'-64 }, // 18 + { Qt::Key_Y, 'y' , 'Y' , 'Y'-64 }, // 19 + { Qt::Key_Z, 'z' , 'Z' , 'Z'-64 }, // 1a + { Qt::Key_Shift, 0xffff , 0xffff , 0xffff }, // 1b + { Qt::Key_Return, 13 , 13 , 0xffff }, // 1c + { Qt::Key_F11, 0xffff , 0xffff , 0xffff }, // 1d todo + { Qt::Key_F22, 0xffff , 0xffff , 0xffff }, // 1e 30 + { Qt::Key_Backspace, 8 , 8 , 0xffff }, // 1f + { Qt::Key_F31, 0xffff , 0xffff , 0xffff }, // 20 + { Qt::Key_F35, 0xffff , 0xffff , 0xffff }, // 21 light + { Qt::Key_Escape, 0xffff , 0xffff , 0xffff }, // 22 + + // Direction key code are for *UNROTATED* display. + { Qt::Key_Up, 0xffff , 0xffff , 0xffff }, // 23 + { Qt::Key_Right, 0xffff , 0xffff , 0xffff }, // 24 + { Qt::Key_Left, 0xffff , 0xffff , 0xffff }, // 25 + { Qt::Key_Down, 0xffff , 0xffff , 0xffff }, // 26 + + { Qt::Key_F33, 0xffff , 0xffff , 0xffff }, // 27 OK + { Qt::Key_F12, 0xffff , 0xffff , 0xffff }, // 28 40 home + { Qt::Key_1, '1' , 'q' , 'Q'-64 }, // 29 + { Qt::Key_2, '2' , 'w' , 'W'-64 }, // 2a + { Qt::Key_3, '3' , 'e' , 'E'-64 }, // 2b + { Qt::Key_4, '4' , 'r' , 'R'-64 }, // 2c + { Qt::Key_5, '5' , 't' , 'T'-64 }, // 2d + { Qt::Key_6, '6' , 'y' , 'Y'-64 }, // 2e + { Qt::Key_7, '7' , 'u' , 'U'-64 }, // 2f + { Qt::Key_8, '8' , 'i' , 'I'-64 }, // 30 + { Qt::Key_9, '9' , 'o' , 'O'-64 }, // 31 + { Qt::Key_0, '0' , 'p' , 'P'-64 }, // 32 50 + { Qt::Key_unknown, 0xffff , 0xffff , 0xffff }, // 33 + { Qt::Key_unknown, 0xffff , 0xffff , 0xffff }, // 34 + { Qt::Key_unknown, 0xffff , 0xffff , 0xffff }, // 35 + { Qt::Key_unknown, 0xffff , 0xffff , 0xffff }, // 36 + { Qt::Key_unknown, 0xffff , 0xffff , 0xffff }, // 37 + { Qt::Key_unknown, 0xffff , 0xffff , 0xffff }, // 38 + { Qt::Key_unknown, 0xffff , 0xffff , 0xffff }, // 39 + { Qt::Key_Minus, '-' , 'b' , 'B'-64 }, // 3a + { Qt::Key_Plus, '+' , 'n' , 'N'-64 }, // 3b + { Qt::Key_CapsLock, 0xffff , 0xffff , 0xffff }, // 3c 60 + { Qt::Key_At, '@' , 's' , 'S'-64 }, // 3d + { Qt::Key_Question, '?' , '?' , 0xffff }, // 3e + { Qt::Key_Comma, ',' , ',' , 0xffff }, // 3f + { Qt::Key_Period, '.' , '.' , 0xffff }, // 40 + { Qt::Key_Tab, 9 , '\\' , 0xffff }, // 41 + { Qt::Key_X, 0xffff , 'x' , 'X'-64 }, // 42 + { Qt::Key_C, 0xffff , 'c' , 'C'-64 }, // 43 + { Qt::Key_V, 0xffff , 'v' , 'V'-64 }, // 44 + { Qt::Key_Slash, '/' , '/' , 0xffff }, // 45 + { Qt::Key_Apostrophe, '\'' , '\'' , 0xffff }, // 46 70 + { Qt::Key_Semicolon, ';' , ';' , 0xffff }, // 47 + { Qt::Key_QuoteDbl, '\"' , '\"' , 0xffff }, // 48 + { Qt::Key_Colon, ':' , ':' , 0xffff }, // 49 + { Qt::Key_NumberSign, '#' , 'd' , 'D'-64 }, // 4a + { Qt::Key_Dollar, '$' , 'f' , 'F'-64 }, // 4b + { Qt::Key_Percent, '%' , 'g' , 'G'-64 }, // 4c + { Qt::Key_Underscore, '_' , 'h' , 'H'-64 }, // 4d + { Qt::Key_Ampersand, '&' , 'j' , 'J'-64 }, // 4e + { Qt::Key_Asterisk, '*' , 'k' , 'K'-64 }, // 4f + { Qt::Key_ParenLeft, '(' , 'l' , 'L'-64 }, // 50 80 + { Qt::Key_Delete, '[' , '[' , '[' }, // 51 + { Qt::Key_Z, 0xffff , 'z' , 'Z'-64 }, // 52 + { Qt::Key_Equal, '=' , 'm' , 'M'-64 }, // 53 + { Qt::Key_ParenRight, ')' , ']' , ']' }, // 54 + { Qt::Key_AsciiTilde, '~' , '^' , '^' }, // 55 + { Qt::Key_Less, '<' , '{' , '{' }, // 56 + { Qt::Key_Greater, '>' , '}' , '}' }, // 57 + { Qt::Key_F9, 0xffff , 0xffff , 0xffff }, // 58 datebook + { Qt::Key_F10, 0xffff , 0xffff , 0xffff }, // 59 address + { Qt::Key_F13, 0xffff , 0xffff , 0xffff }, // 5a 90 email + { Qt::Key_F30, ' ' , ' ' , 0xffff }, // 5b select + { Qt::Key_Space, ' ' , '|' , '`' }, // 5c + { Qt::Key_unknown, 0xffff , 0xffff , 0xffff }, // 5d + { Qt::Key_Exclam, '!' , 'a' , 'A'-64 }, // 5e + { Qt::Key_unknown, 0xffff , 0xffff , 0xffff }, // 5f + { Qt::Key_unknown, 0xffff , 0xffff , 0xffff }, // 60 + { Qt::Key_unknown, 0xffff , 0xffff , 0xffff }, // 61 + { Qt::Key_unknown, 0xffff , 0xffff , 0xffff }, // 62 + { Qt::Key_unknown, 0xffff , 0xffff , 0xffff }, // 63 + { Qt::Key_unknown, 0xffff , 0xffff , 0xffff }, // 64 + { Qt::Key_unknown, 0xffff , 0xffff , 0xffff }, // 65 + { Qt::Key_unknown, 0xffff , 0xffff , 0xffff }, // 66 + { Qt::Key_Meta, 0xffff , 0xffff , 0xffff }, // 67 + { Qt::Key_unknown, 0xffff , 0xffff , 0xffff }, // 68 + { Qt::Key_unknown, 0xffff , 0xffff , 0xffff }, // 69 + { Qt::Key_unknown, 0xffff , 0xffff , 0xffff }, // 6a + { Qt::Key_unknown, 0xffff , 0xffff , 0xffff }, // 6b + { Qt::Key_unknown, 0xffff , 0xffff , 0xffff }, // 6c + { Qt::Key_F34, 0xffff , 0xffff , 0xffff }, // 6d power + { Qt::Key_F13, 0xffff , 0xffff , 0xffff }, // 6e mail long + { Qt::Key_unknown, 0xffff , 0xffff , 0xffff }, // 6f + { Qt::Key_NumLock, 0xffff , 0xffff , 0xffff }, // 70 + { Qt::Key_unknown, 0xffff , 0xffff , 0xffff }, // 71 + { Qt::Key_unknown, 0xffff , 0xffff , 0xffff }, // 72 + { 0x20ac, 0xffff , 0x20ac , 0x20ac }, // 73 Euro sign + { Qt::Key_unknown, 0xffff , 0xffff , 0xffff }, // 74 + { Qt::Key_F32, 0xffff , 0xffff , 0xffff }, // 75 Sync + { 0, 0xffff , 0xffff , 0xffff } +}; + +static const int keyMSize = sizeof(sl5000KeyMap)/sizeof(QWSKeyMap)-1; + +QWSSL5000KeyboardHandler::QWSSL5000KeyboardHandler(const QString &device) + : QWSTtyKeyboardHandler(device) +{ + meta = false; + fn = false; + numLock = false; + + sharp_kbdctl_modifstat st; + int dev = ::open(device.isEmpty()?"/dev/sharp_kbdctl":device.toLocal8Bit().constData(), O_RDWR); + if (dev >= 0) { + memset(&st, 0, sizeof(st)); + st.which = 3; + int ret = ioctl(dev, SHARP_KBDCTL_GETMODIFSTAT, (char*)&st); + if(!ret) + numLock = (bool)st.stat; + ::close(dev); + } +} + +QWSSL5000KeyboardHandler::~QWSSL5000KeyboardHandler() +{ +} + +const QWSKeyMap *QWSSL5000KeyboardHandler::keyMap() const +{ + return sl5000KeyMap; +} + +void QWSSL5000KeyboardHandler::doKey(uchar code) +{ + int keyCode = Qt::Key_unknown; + bool release = false; + + if (code & 0x80) { + release = true; + code &= 0x7f; + } + + if (fn && !meta && (code >= 0x42 && code <= 0x52)) { + ushort unicode=0; + int scan=0; + if (code == 0x42) { unicode='X'-'@'; scan=Qt::Key_X; } // Cut + else if (code == 0x43) { unicode='C'-'@'; scan=Qt::Key_C; } // Copy + else if (code == 0x44) { unicode='V'-'@'; scan=Qt::Key_V; } // Paste + else if (code == 0x52) { unicode='Z'-'@'; scan=Qt::Key_Z; } // Undo + if (scan) { + processKeyEvent(unicode, scan, Qt::ControlModifier, !release, false); + return; + } + } + + if (code < keyMSize) { + keyCode = keyMap()[code].key_code; + } + + bool repeatable = true; + + if (release && (keyCode == Qt::Key_F34 || keyCode == Qt::Key_F35)) + return; // no release for power and light keys + if (keyCode >= Qt::Key_F1 && keyCode <= Qt::Key_F35 + || keyCode == Qt::Key_Escape || keyCode == Qt::Key_Home + || keyCode == Qt::Key_Shift || keyCode == Qt::Key_Meta) + repeatable = false; + + if (qt_screen->isTransformed() + && keyCode >= Qt::Key_Left && keyCode <= Qt::Key_Down) + { + keyCode = transformDirKey(keyCode); + } + + // Ctrl-Alt-Delete exits qws + if (ctrl && alt && keyCode == Qt::Key_Delete) { + qApp->quit(); + } + + if (keyCode == Qt::Key_F22) { /* Fn key */ + fn = !release; + } else if (keyCode == Qt::Key_NumLock) { + if (release) + numLock = !numLock; + } else if (keyCode == Qt::AltModifier) { + alt = !release; + } else if (keyCode == Qt::ControlModifier) { + ctrl = !release; + } else if (keyCode == Qt::ShiftModifier) { + shift = !release; + } else if (keyCode == Qt::MetaModifier) { + meta = !release; + } else if (keyCode == Qt::Key_CapsLock && release) { + caps = !caps; + } + if (keyCode != Qt::Key_unknown) { + bool bAlt = alt; + bool bCtrl = ctrl; + bool bShift = shift; + int unicode = 0; + if (code < keyMSize) { + bool bCaps = caps ^ shift; + if (fn) { + if (shift) { + bCaps = bShift = false; + bCtrl = true; + } + if (meta) { + bCaps = bShift = true; + bAlt = true; + } + } else if (meta) { + bCaps = bShift = true; + } + if (code > 40 && caps) { + // fn-keys should only react to shift, not caps + bCaps = bShift = shift; + } + if (numLock) { + if (keyCode != Qt::Key_Space && keyCode != Qt::Key_Tab) + bCaps = bShift = false; + } + if (keyCode == Qt::Key_Delete && (bAlt || bCtrl)) { + keyCode = Qt::Key_BraceLeft; + unicode = '['; + bCaps = bShift = bAlt = bCtrl = false; + } else if (keyCode == Qt::Key_F31 && bCtrl) { + keyCode = Qt::Key_QuoteLeft; + unicode = '`'; + } else if (bCtrl) + unicode = keyMap()[code].ctrl_unicode ? keyMap()[code].ctrl_unicode : 0xffff; + else if (bCaps) + unicode = keyMap()[code].shift_unicode ? keyMap()[code].shift_unicode : 0xffff; + else + unicode = keyMap()[code].unicode ? keyMap()[code].unicode : 0xffff; + } + + modifiers = 0; + if (bAlt) modifiers |= Qt::AltModifier; + if (bCtrl) modifiers |= Qt::ControlModifier; + if (bShift) modifiers |= Qt::ShiftModifier; + + // looks wrong -- WWA + bool repeat = false; + if (prevuni == unicode && prevkey == keyCode && !release) + repeat = true; + + processKeyEvent(unicode, keyCode, modifiers, !release, repeat); + + if (!release) { + prevuni = unicode; + prevkey = keyCode; + } else { + prevkey = prevuni = 0; + } + } + + if (repeatable && !release) + beginAutoRepeat(prevuni, prevkey, modifiers); + else + endAutoRepeat(); +} + +QT_END_NAMESPACE + +#endif // QT_NO_QWS_KBD_SL5000 diff --git a/src/gui/embedded/qkbdsl5000_qws.h b/src/gui/embedded/qkbdsl5000_qws.h new file mode 100644 index 0000000..514d602 --- /dev/null +++ b/src/gui/embedded/qkbdsl5000_qws.h @@ -0,0 +1,79 @@ +/**************************************************************************** +** +** 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$ +** +****************************************************************************/ + +#ifndef QKBDSL5000_QWS_H +#define QKBDSL5000_QWS_H + +#include <QtGui/qkbdtty_qws.h> + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +QT_MODULE(Gui) + +#ifndef QT_NO_QWS_KBD_SL5000 + +class QWSSL5000KbPrivate; + +class QWSSL5000KeyboardHandler : public QWSTtyKeyboardHandler +{ +public: + explicit QWSSL5000KeyboardHandler(const QString&); + virtual ~QWSSL5000KeyboardHandler(); + + virtual void doKey(uchar scancode); + virtual const QWSKeyMap *keyMap() const; + +private: + bool meta; + bool fn; + bool numLock; + QWSSL5000KbPrivate *d; +}; + +#endif // QT_NO_QWS_KBD_SL5000 + +QT_END_NAMESPACE + +QT_END_HEADER + +#endif // QKBDSL5000_QWS_H diff --git a/src/gui/embedded/qkbdtty_qws.cpp b/src/gui/embedded/qkbdtty_qws.cpp new file mode 100644 index 0000000..b588e55 --- /dev/null +++ b/src/gui/embedded/qkbdtty_qws.cpp @@ -0,0 +1,263 @@ +/**************************************************************************** +** +** 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 "qkbdtty_qws.h" + +#if !defined(QT_NO_QWS_KEYBOARD) && !defined(QT_NO_QWS_KBD_TTY) + +#include "qscreen_qws.h" + +#include "qwindowsystem_qws.h" +#include "qapplication.h" +#include "qsocketnotifier.h" +#include "qnamespace.h" +#include "qtimer.h" +#include <private/qwssignalhandler_p.h> +#include <private/qwindowsurface_qws_p.h> + +#include <unistd.h> +#include <sys/ioctl.h> +#include <sys/types.h> +#include <sys/stat.h> +#include <fcntl.h> +#include <errno.h> +#include <signal.h> +#include <termios.h> + +#include <qeventloop.h> + +#ifdef Q_OS_LINUX +#include <sys/kd.h> +#include <sys/vt.h> +#endif + +QT_BEGIN_NAMESPACE + +#define VTACQSIG SIGUSR1 +#define VTRELSIG SIGUSR2 + +static int vtQws = 0; +static int kbdFD = -1; + +//=========================================================================== + +// +// Tty keyboard +// + +class QWSTtyKbPrivate : public QObject +{ + Q_OBJECT +public: + QWSTtyKbPrivate(QWSPC101KeyboardHandler *, const QString &device); + ~QWSTtyKbPrivate(); + +private slots: + void readKeyboardData(); + void handleTtySwitch(int); + +private: + QWSPC101KeyboardHandler *handler; + struct termios origTermData; +}; + +QWSTtyKeyboardHandler::QWSTtyKeyboardHandler(const QString &device) + : QWSPC101KeyboardHandler(device) +{ + d = new QWSTtyKbPrivate(this, device); +} + +QWSTtyKeyboardHandler::~QWSTtyKeyboardHandler() +{ + delete d; +} + +void QWSTtyKeyboardHandler::processKeyEvent(int unicode, int keycode, + Qt::KeyboardModifiers modifiers, bool isPress, + bool autoRepeat) +{ +#if defined(Q_OS_LINUX) + // Virtual console switching + int term = 0; + bool ctrl = modifiers & Qt::ControlModifier; + bool alt = modifiers & Qt::AltModifier; + if (ctrl && alt && keycode >= Qt::Key_F1 && keycode <= Qt::Key_F10) + term = keycode - Qt::Key_F1 + 1; + else if (ctrl && alt && keycode == Qt::Key_Left) + term = qMax(vtQws - 1, 1); + else if (ctrl && alt && keycode == Qt::Key_Right) + term = qMin(vtQws + 1, 10); + if (term && isPress) { + ioctl(kbdFD, VT_ACTIVATE, term); + return; + } +#endif + + QWSPC101KeyboardHandler::processKeyEvent(unicode, keycode, modifiers, + isPress, autoRepeat); +} + + +QWSTtyKbPrivate::QWSTtyKbPrivate(QWSPC101KeyboardHandler *h, const QString &device) : handler(h) +{ + kbdFD = ::open(device.isEmpty()?"/dev/tty0":device.toLatin1().constData(), O_RDWR|O_NDELAY, 0); +#ifndef QT_NO_QWS_SIGNALHANDLER + QWSSignalHandler::instance()->addObject(this); +#endif + + if (kbdFD >= 0) { + QSocketNotifier *notifier; + notifier = new QSocketNotifier(kbdFD, QSocketNotifier::Read, this); + connect(notifier, SIGNAL(activated(int)),this, + SLOT(readKeyboardData())); + + // save for restore. + tcgetattr(kbdFD, &origTermData); + + struct termios termdata; + tcgetattr(kbdFD, &termdata); + +#if defined(Q_OS_LINUX) +# ifdef QT_QWS_USE_KEYCODES + ioctl(kbdFD, KDSKBMODE, K_MEDIUMRAW); +# else + ioctl(kbdFD, KDSKBMODE, K_RAW); +# endif +#endif + + termdata.c_iflag = (IGNPAR | IGNBRK) & (~PARMRK) & (~ISTRIP); + termdata.c_oflag = 0; + termdata.c_cflag = CREAD | CS8; + termdata.c_lflag = 0; + termdata.c_cc[VTIME]=0; + termdata.c_cc[VMIN]=1; + cfsetispeed(&termdata, 9600); + cfsetospeed(&termdata, 9600); + tcsetattr(kbdFD, TCSANOW, &termdata); + +#if defined(Q_OS_LINUX) + + connect(QApplication::instance(), SIGNAL(unixSignal(int)), this, SLOT(handleTtySwitch(int))); + QApplication::instance()->watchUnixSignal(VTACQSIG, true); + QApplication::instance()->watchUnixSignal(VTRELSIG, true); + + struct vt_mode vtMode; + ioctl(kbdFD, VT_GETMODE, &vtMode); + + // let us control VT switching + vtMode.mode = VT_PROCESS; + vtMode.relsig = VTRELSIG; + vtMode.acqsig = VTACQSIG; + ioctl(kbdFD, VT_SETMODE, &vtMode); + + struct vt_stat vtStat; + ioctl(kbdFD, VT_GETSTATE, &vtStat); + vtQws = vtStat.v_active; +#endif + } else { + qCritical("Cannot open keyboard: %s", strerror(errno)); + } + +} + +QWSTtyKbPrivate::~QWSTtyKbPrivate() +{ + if (kbdFD >= 0) { +#if defined(Q_OS_LINUX) + ioctl(kbdFD, KDSKBMODE, K_XLATE); +#endif + tcsetattr(kbdFD, TCSANOW, &origTermData); + ::close(kbdFD); + kbdFD = -1; + } +} + +void QWSTtyKbPrivate::handleTtySwitch(int sig) +{ +#if defined(Q_OS_LINUX) + if (sig == VTACQSIG) { + if (ioctl(kbdFD, VT_RELDISP, VT_ACKACQ) == 0) { + qwsServer->enablePainting(true); + qt_screen->restore(); + qwsServer->resumeMouse(); + qwsServer->refresh(); + } + } else if (sig == VTRELSIG) { + qwsServer->enablePainting(false); + + // Check for reserved surfaces which might still do painting + bool allWindowsHidden = true; + const QList<QWSWindow*> windows = QWSServer::instance()->clientWindows(); + for (int i = 0; i < windows.size(); ++i) { + const QWSWindow *w = windows.at(i); + QWSWindowSurface *s = w->windowSurface(); + if (s && s->isRegionReserved() && !w->allocatedRegion().isEmpty()) { + allWindowsHidden = false; + break; + } + } + + if (!allWindowsHidden) { + ioctl(kbdFD, VT_RELDISP, 0); // abort console switch + qwsServer->enablePainting(true); + } else if (ioctl(kbdFD, VT_RELDISP, 1) == 0) { + qt_screen->save(); + qwsServer->suspendMouse(); + } else { + qwsServer->enablePainting(true); + } + } +#endif +} + +void QWSTtyKbPrivate::readKeyboardData() +{ + unsigned char buf[81]; + int n = read(kbdFD, buf, 80); + for (int loop = 0; loop < n; loop++) + handler->doKey(buf[loop]); +} + +QT_END_NAMESPACE + +#include "qkbdtty_qws.moc" + +#endif // QT_NO_QWS_KEYBOARD || QT_NO_QWS_KBD_TTY diff --git a/src/gui/embedded/qkbdtty_qws.h b/src/gui/embedded/qkbdtty_qws.h new file mode 100644 index 0000000..4f93d6c --- /dev/null +++ b/src/gui/embedded/qkbdtty_qws.h @@ -0,0 +1,81 @@ +/**************************************************************************** +** +** 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$ +** +****************************************************************************/ + +#ifndef QKBDTTY_QWS_H +#define QKBDTTY_QWS_H + +#include <QtGui/qkbdpc101_qws.h> + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +QT_MODULE(Gui) + +#ifndef QT_NO_QWS_KEYBOARD + +#ifndef QT_NO_QWS_KBD_TTY + +class QWSTtyKbPrivate; + +class QWSTtyKeyboardHandler : public QWSPC101KeyboardHandler +{ +public: + explicit QWSTtyKeyboardHandler(const QString&); + virtual ~QWSTtyKeyboardHandler(); + +protected: + virtual void processKeyEvent(int unicode, int keycode, Qt::KeyboardModifiers modifiers, + bool isPress, bool autoRepeat); + +private: + QWSTtyKbPrivate *d; +}; + +#endif // QT_NO_QWS_KBD_TTY + +#endif // QT_NO_QWS_KEYBOARD + +QT_END_NAMESPACE + +QT_END_HEADER + +#endif // QKBDTTY_QWS_H diff --git a/src/gui/embedded/qkbdum_qws.cpp b/src/gui/embedded/qkbdum_qws.cpp new file mode 100644 index 0000000..d525c66 --- /dev/null +++ b/src/gui/embedded/qkbdum_qws.cpp @@ -0,0 +1,143 @@ +/**************************************************************************** +** +** 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 "qkbdum_qws.h" +#include "qvfbhdr.h" + +#if !defined(QT_NO_QWS_KEYBOARD) && !defined(QT_NO_QWS_KBD_UM) + +#include <stdlib.h> +#include <sys/types.h> +#include <sys/stat.h> +#include <unistd.h> +#include <fcntl.h> +#include <errno.h> + +#include <qstring.h> +#include <qwindowsystem_qws.h> +#include <qsocketnotifier.h> + +QT_BEGIN_NAMESPACE + +class QWSUmKeyboardHandlerPrivate : public QObject +{ + Q_OBJECT + +public: + QWSUmKeyboardHandlerPrivate(const QString&); + ~QWSUmKeyboardHandlerPrivate(); + +private slots: + void readKeyboardData(); + +private: + int kbdFD; + int kbdIdx; + const int kbdBufferLen; + unsigned char *kbdBuffer; + QSocketNotifier *notifier; +}; + +QWSUmKeyboardHandlerPrivate::QWSUmKeyboardHandlerPrivate(const QString &device) + : kbdFD(-1), kbdIdx(0), kbdBufferLen(sizeof(QVFbKeyData)*5) +{ + kbdBuffer = new unsigned char [kbdBufferLen]; + + if ((kbdFD = open((const char *)device.toLocal8Bit(), O_RDONLY | O_NDELAY)) < 0) { + qDebug("Cannot open %s (%s)", (const char *)device.toLocal8Bit(), + strerror(errno)); + } else { + // Clear pending input + char buf[2]; + while (read(kbdFD, buf, 1) > 0) { } + + notifier = new QSocketNotifier(kbdFD, QSocketNotifier::Read, this); + connect(notifier, SIGNAL(activated(int)),this, SLOT(readKeyboardData())); + } +} + +QWSUmKeyboardHandlerPrivate::~QWSUmKeyboardHandlerPrivate() +{ + if (kbdFD >= 0) + close(kbdFD); + delete [] kbdBuffer; +} + + +void QWSUmKeyboardHandlerPrivate::readKeyboardData() +{ + int n; + do { + n = read(kbdFD, kbdBuffer+kbdIdx, kbdBufferLen - kbdIdx); + if (n > 0) + kbdIdx += n; + } while (n > 0); + + int idx = 0; + while (kbdIdx - idx >= (int)sizeof(QVFbKeyData)) { + QVFbKeyData *kd = (QVFbKeyData *)(kbdBuffer + idx); + // Qtopia Key filters must still work. + QWSServer::processKeyEvent(kd->unicode, kd->keycode, kd->modifiers, kd->press, kd->repeat); + idx += sizeof(QVFbKeyData); + } + + int surplus = kbdIdx - idx; + for (int i = 0; i < surplus; i++) + kbdBuffer[i] = kbdBuffer[idx+i]; + kbdIdx = surplus; +} + +QWSUmKeyboardHandler::QWSUmKeyboardHandler(const QString &device) + : QWSKeyboardHandler() +{ + d = new QWSUmKeyboardHandlerPrivate(device); +} + +QWSUmKeyboardHandler::~QWSUmKeyboardHandler() +{ + delete d; +} + +QT_END_NAMESPACE + +#include "qkbdum_qws.moc" + +#endif // QT_NO_QWS_KEYBOARD && QT_NO_QWS_KBD_UM diff --git a/src/gui/embedded/qkbdum_qws.h b/src/gui/embedded/qkbdum_qws.h new file mode 100644 index 0000000..96aaef6 --- /dev/null +++ b/src/gui/embedded/qkbdum_qws.h @@ -0,0 +1,77 @@ +/**************************************************************************** +** +** 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$ +** +****************************************************************************/ + +#ifndef QKBDUM_QWS_H +#define QKBDUM_QWS_H + +#include <QtGui/qkbd_qws.h> + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +QT_MODULE(Gui) + +#ifndef QT_NO_QWS_KEYBOARD + +#ifndef QT_NO_QWS_KBD_UM + +class QWSUmKeyboardHandlerPrivate; + +class QWSUmKeyboardHandler : public QWSKeyboardHandler +{ +public: + QWSUmKeyboardHandler(const QString &); + virtual ~QWSUmKeyboardHandler(); + +private: + + QWSUmKeyboardHandlerPrivate *d; +}; +#endif // QT_NO_QWS_KBD_UM + +#endif // QT_NO_QWS_KEYBOARD + +QT_END_NAMESPACE + +QT_END_HEADER + +#endif // QKBDUM_QWS_H diff --git a/src/gui/embedded/qkbdusb_qws.cpp b/src/gui/embedded/qkbdusb_qws.cpp new file mode 100644 index 0000000..e35ac55 --- /dev/null +++ b/src/gui/embedded/qkbdusb_qws.cpp @@ -0,0 +1,401 @@ +/**************************************************************************** +** +** 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 "qkbdusb_qws.h" + +#ifndef QT_NO_QWS_KEYBOARD + +#include "qscreen_qws.h" + +#include "qwindowsystem_qws.h" +#include "qapplication.h" +#include "qsocketnotifier.h" +#include "qnamespace.h" +#include "qtimer.h" + +#include <unistd.h> +#include <sys/ioctl.h> +#include <sys/types.h> +#include <sys/stat.h> +#include <fcntl.h> +#include <errno.h> +#include <signal.h> + +#include <linux/input.h> + +#ifdef Q_OS_LINUX +#include <sys/kd.h> +#include <sys/vt.h> +#endif + +QT_BEGIN_NAMESPACE + +/* USB driver */ + + +class QWSUsbKbPrivate : public QObject +{ + Q_OBJECT +public: + QWSUsbKbPrivate(QWSPC101KeyboardHandler *, const QString &); + ~QWSUsbKbPrivate(); + +private slots: + void readKeyboardData(); + +private: + QWSPC101KeyboardHandler *handler; + int fd; +#ifdef QT_QWS_ZYLONITE + bool shift; +#endif +}; + +QWSUsbKeyboardHandler::QWSUsbKeyboardHandler(const QString &device) + : QWSPC101KeyboardHandler(device) +{ + d = new QWSUsbKbPrivate(this, device); +} + +QWSUsbKeyboardHandler::~QWSUsbKeyboardHandler() +{ + delete d; +} + +QWSUsbKbPrivate::QWSUsbKbPrivate(QWSPC101KeyboardHandler *h, const QString &device) : handler(h) +{ +#ifdef QT_QWS_ZYLONITE + shift = FALSE; +#endif + fd = ::open(device.isEmpty()?"/dev/input/event1":device.toLocal8Bit(),O_RDONLY, 0); + if (fd >= 0) { + QSocketNotifier *notifier; + notifier = new QSocketNotifier(fd, QSocketNotifier::Read, this); + connect(notifier, SIGNAL(activated(int)),this, + SLOT(readKeyboardData())); + } +} + +QWSUsbKbPrivate::~QWSUsbKbPrivate() +{ + ::close(fd); +} + +void QWSUsbKbPrivate::readKeyboardData() +{ + input_event event; + if (read(fd, &event, sizeof(input_event)) != sizeof(input_event)) + return; + + if (event.type != EV_KEY) + return; + +#ifdef QT_QWS_ZYLONITE + qDebug("keypressed: code=%03d (%s)\n",event.code,((event.value)!=0) ? "Down":"Up"); + int modifiers=0; + int unicode=0xffff; + int key_code=0; + + switch(event.code) + { + case 0xA2: + key_code = ((!shift) ? Qt::Key_0 : Qt::Key_Plus ); + unicode = ((!shift) ? 0x30 : 0x2B ); + break; + case 0x70: + key_code = ((!shift) ? Qt::Key_1 : Qt::Key_At ); + unicode = ((!shift) ? 0x31 : 0x40 ); + break; + case 0x72: + key_code = ((!shift) ? Qt::Key_2 : Qt::Key_Ampersand ); + unicode = ((!shift) ? 0x32 : 0x26 ); + break; + case 0x74: + key_code = ((!shift) ? Qt::Key_3 : Qt::Key_At ); + unicode = ((!shift) ? 0x33 : 0x3F ); + break; + case 0x80: + key_code = ((!shift) ? Qt::Key_4 : Qt::Key_Minus ); + unicode = ((!shift) ? 0x34 : 0x2D ); + break; + case 0x82: + key_code = ((!shift) ? Qt::Key_5 : Qt::Key_Apostrophe); + unicode = ((!shift) ? 0x35 : 0x27 ); + break; + case 0x84: + key_code = ((!shift) ? Qt::Key_6 : Qt::Key_Slash ); + unicode = ((!shift) ? 0x36 : 0x5C ); + break; + case 0x90: + key_code = ((!shift) ? Qt::Key_7 : Qt::Key_Colon ); + unicode = ((!shift) ? 0x37 : 0x3A ); + break; + case 0x92: + key_code = ((!shift) ? Qt::Key_8 : Qt::Key_Semicolon ); + unicode = ((!shift) ? 0x38 : 0x3B ); + break; + case 0x94: + key_code = ((!shift) ? Qt::Key_9 : Qt::Key_QuoteDbl ); + unicode = ((!shift) ? 0x39 : 0x22 ); + break; + case 0x0: + key_code = Qt::Key_A; + unicode = ((!shift) ? 0x61 : 0x41 ); + break; + case 0x10: + key_code = Qt::Key_B; + unicode = ((!shift) ? 0x62 : 0x42 ); + break; + case 0x20: + key_code = Qt::Key_C; + unicode = ((!shift) ? 0x63 : 0x43 ); + break; + case 0x30: + key_code = Qt::Key_D; + unicode = ((!shift) ? 0x64 : 0x44 ); + break; + case 0x40: + key_code = Qt::Key_E; + unicode = ((!shift) ? 0x65 : 0x45 ); + break; + case 0x50: + key_code = Qt::Key_F; + unicode = ((!shift) ? 0x66 : 0x46 ); + break; + case 0x01: + key_code = Qt::Key_G; + unicode = ((!shift) ? 0x67 : 0x47 ); + break; + case 0x11: + key_code = Qt::Key_H; + unicode = ((!shift) ? 0x68 : 0x48 ); + break; + case 0x21: + key_code = Qt::Key_I; + unicode = ((!shift) ? 0x69 : 0x49 ); + break; + case 0x31: + key_code = Qt::Key_J; + unicode = ((!shift) ? 0x6A : 0x4A ); + break; + case 0x41: + key_code = Qt::Key_K; + unicode = ((!shift) ? 0x6B : 0x4B ); + break; + case 0x51: + key_code = Qt::Key_L; + unicode = ((!shift) ? 0x6C : 0x4C ); + break; + case 0x02: + key_code = Qt::Key_M; + unicode = ((!shift) ? 0x6D : 0x4D ); + break; + case 0x12: + key_code = Qt::Key_N; + unicode = ((!shift) ? 0x6E : 0x4E ); + break; + case 0x22: + key_code = Qt::Key_O; + unicode = ((!shift) ? 0x6F : 0x4F ); + break; + case 0x32: + key_code = Qt::Key_P; + unicode = ((!shift) ? 0x70 : 0x50 ); + break; + case 0x42: + key_code = Qt::Key_Q; + unicode = ((!shift) ? 0x71 : 0x51 ); + break; + case 0x52: + key_code = Qt::Key_R; + unicode = ((!shift) ? 0x72 : 0x52 ); + break; + case 0x03: + key_code = Qt::Key_S; + unicode = ((!shift) ? 0x73 : 0x53 ); + break; + case 0x13: + key_code = Qt::Key_T; + unicode = ((!shift) ? 0x74 : 0x54 ); + break; + case 0x23: + key_code = Qt::Key_U; + unicode = ((!shift) ? 0x75 : 0x55 ); + break; + case 0x33: + key_code = Qt::Key_V; + unicode = ((!shift) ? 0x76 : 0x56 ); + break; + case 0x43: + key_code = Qt::Key_W; + unicode = ((!shift) ? 0x77 : 0x57 ); + break; + case 0x53: + key_code = Qt::Key_X; + unicode = ((!shift) ? 0x78 : 0x58 ); + break; + case 0x24: + key_code = Qt::Key_Y; + unicode = ((!shift) ? 0x79 : 0x59 ); + break; + case 0x34: + key_code = Qt::Key_Z; + unicode = ((!shift) ? 0x7A : 0x5A ); + break; + case 0xA4: + key_code = ((!shift) ? Qt::Key_NumberSign : Qt::Key_Period); + unicode = ((!shift) ? 0x23 : 0x2E ); + break; + case 0xA0: + key_code = ((!shift) ? Qt::Key_Asterisk : Qt::Key_NumberSign ); + unicode = ((!shift) ? 0x2A : 0x2C ); + break; + case 0x25: + key_code = Qt::Key_Space; + unicode = 0x20; + break; + case 0x06: + key_code = Qt::Key_Up; + unicode = 0xffff; modifiers |= Qt::KeypadModifier; + break; + case 0x16: + key_code = Qt::Key_Down; + unicode = 0xffff; modifiers |= Qt::KeypadModifier; + break; + case 0x26: + key_code = Qt::Key_Left; + unicode = 0xffff; modifiers |= Qt::KeypadModifier; + break; + case 0x36: + key_code = Qt::Key_Right; + unicode = 0xffff; modifiers |= Qt::KeypadModifier; + break; + case 0x46: + key_code = Qt::Key_Select; + unicode = 0xffff; modifiers |= Qt::KeypadModifier; + break; + case 0x61: + key_code = Qt::Key_No; + unicode = 0xffff; modifiers |= Qt::KeypadModifier; + break; + case 0x60: + key_code = Qt::Key_Call; + unicode = 0xffff; modifiers |= Qt::KeypadModifier; + break; + case 0x55: + key_code = Qt::Key_Hangup; + unicode = 0xffff; modifiers |= Qt::KeypadModifier; + break; + case 0x62: + key_code = Qt::Key_Context1; + unicode = 0xffff; modifiers |= Qt::KeypadModifier; + break; + case 0x63: + key_code = Qt::Key_No; + unicode = 0xffff; modifiers |= Qt::KeypadModifier; + break; + case 0x05: + key_code = Qt::Key_Home; + unicode = 0xffff; modifiers |= Qt::KeypadModifier; + break; + case 0x15: + key_code = Qt::Key_Shift; + unicode = 0xffff; modifiers |= Qt::ShiftModifier; + if(event.value==0) break; + if(shift) { + shift = FALSE; + qWarning("Caps Off!"); + } else { + shift = TRUE; + qWarning("Caps On!"); + } + break; + case 0x1C: + key_code = ((!shift) ? Qt::Key_Back : Qt::Key_Enter ); + unicode = 0xffff; modifiers |= Qt::KeypadModifier; + break; + case 0x19: + key_code = Qt::Key_Context2; + unicode = 0xffff; modifiers |= Qt::KeypadModifier; + break; + case 0x1A: + key_code = Qt::Key_Context3; + unicode = 0xffff; modifiers |= Qt::KeypadModifier; + break; + case 0x1B: + key_code = Qt::Key_Context4; + unicode = 0xffff; modifiers |= Qt::KeypadModifier; + break; + } + if(shift) modifiers |= Qt::ShiftModifier; + handler->processKeyEvent(unicode, key_code, (Qt::KeyboardModifiers)modifiers, event.value!=0, false); +#else + + int key=event.code; +#ifndef QT_QWS_USE_KEYCODES + // Handle SOME keys, otherwise it's useless. + + if(key==103) { + handler->processKeyEvent(0, Qt::Key_Up, 0, event.value!=0, false); + } else if(key==106) { + handler->processKeyEvent(0, Qt::Key_Right, 0, event.value!=0, false ); + } else if(key==108) { + handler->processKeyEvent(0, Qt::Key_Down, 0, event.value!=0, false); + } else if(key==105) { + handler->processKeyEvent(0, Qt::Key_Left, 0, event.value!=0, false); + } else + +#endif + + { + if(event.value == 0) { + key=key | 0x80; + } + handler->doKey(key); + } +#endif +} + +QT_END_NAMESPACE + +#include "qkbdusb_qws.moc" + +#endif // QT_NO_QWS_KEYBOARD diff --git a/src/gui/embedded/qkbdusb_qws.h b/src/gui/embedded/qkbdusb_qws.h new file mode 100644 index 0000000..81d0103 --- /dev/null +++ b/src/gui/embedded/qkbdusb_qws.h @@ -0,0 +1,77 @@ +/**************************************************************************** +** +** 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$ +** +****************************************************************************/ + +#ifndef QKBDUSB_QWS_H +#define QKBDUSB_QWS_H + +#include <QtGui/qkbdpc101_qws.h> + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +QT_MODULE(Gui) + +#ifndef QT_NO_QWS_KEYBOARD + +#ifndef QT_NO_QWS_KBD_USB + +class QWSUsbKbPrivate; + +class QWSUsbKeyboardHandler : public QWSPC101KeyboardHandler +{ +public: + QWSUsbKeyboardHandler(const QString&); + virtual ~QWSUsbKeyboardHandler(); + +private: + QWSUsbKbPrivate *d; +}; + +#endif // QT_NO_QWS_KBD_USB + +#endif // QT_NO_QWS_KEYBOARD + +QT_END_NAMESPACE + +QT_END_HEADER + +#endif // QKBDUSB_QWS_H diff --git a/src/gui/embedded/qkbdvfb_qws.cpp b/src/gui/embedded/qkbdvfb_qws.cpp new file mode 100644 index 0000000..1d53ce9 --- /dev/null +++ b/src/gui/embedded/qkbdvfb_qws.cpp @@ -0,0 +1,123 @@ +/**************************************************************************** +** +** 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 <stdlib.h> +#include <sys/types.h> +#include <sys/stat.h> +#include <unistd.h> +#include <fcntl.h> +#include <errno.h> + +#include <qvfbhdr.h> +#include <qkbdvfb_qws.h> + +#ifndef QT_NO_QWS_KEYBOARD +#ifndef QT_NO_QWS_KBD_QVFB + +#include <qwindowsystem_qws.h> +#include <qsocketnotifier.h> +#include <qapplication.h> + +QT_BEGIN_NAMESPACE + +QVFbKeyboardHandler::QVFbKeyboardHandler(const QString &device) + : QObject() +{ + terminalName = device; + if (terminalName.isEmpty()) + terminalName = QLatin1String("/dev/vkdb"); + kbdFD = -1; + kbdIdx = 0; + kbdBufferLen = sizeof(QVFbKeyData) * 5; + kbdBuffer = new unsigned char [kbdBufferLen]; + + if ((kbdFD = open(terminalName.toLatin1().constData(), O_RDONLY | O_NDELAY)) < 0) { + qWarning("Cannot open %s (%s)", terminalName.toLatin1().constData(), + strerror(errno)); + } else { + // Clear pending input + char buf[2]; + while (read(kbdFD, buf, 1) > 0) { } + + notifier = new QSocketNotifier(kbdFD, QSocketNotifier::Read, this); + connect(notifier, SIGNAL(activated(int)),this, SLOT(readKeyboardData())); + } +} + +QVFbKeyboardHandler::~QVFbKeyboardHandler() +{ + if (kbdFD >= 0) + close(kbdFD); + delete [] kbdBuffer; +} + + +void QVFbKeyboardHandler::readKeyboardData() +{ + int n; + do { + n = read(kbdFD, kbdBuffer+kbdIdx, kbdBufferLen - kbdIdx); + if (n > 0) + kbdIdx += n; + } while (n > 0); + + int idx = 0; + while (kbdIdx - idx >= (int)sizeof(QVFbKeyData)) { + QVFbKeyData *kd = (QVFbKeyData *)(kbdBuffer + idx); + if (kd->unicode == 0 && kd->keycode == 0 && kd->modifiers == 0 && kd->press) { + // magic exit key + qWarning("Instructed to quit by Virtual Keyboard"); + qApp->quit(); + } + QWSServer::processKeyEvent(kd->unicode ? kd->unicode : 0xffff, kd->keycode, kd->modifiers, kd->press, kd->repeat); + idx += sizeof(QVFbKeyData); + } + + int surplus = kbdIdx - idx; + for (int i = 0; i < surplus; i++) + kbdBuffer[i] = kbdBuffer[idx+i]; + kbdIdx = surplus; +} + +QT_END_NAMESPACE + +#endif // QT_NO_QWS_KBD_QVFB +#endif // QT_NO_QWS_KEYBOARD diff --git a/src/gui/embedded/qkbdvfb_qws.h b/src/gui/embedded/qkbdvfb_qws.h new file mode 100644 index 0000000..47fa5d3 --- /dev/null +++ b/src/gui/embedded/qkbdvfb_qws.h @@ -0,0 +1,86 @@ +/**************************************************************************** +** +** 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$ +** +****************************************************************************/ + +#ifndef QKBDVFB_QWS_H +#define QKBDVFB_QWS_H + +#include <QtGui/qkbd_qws.h> + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +QT_MODULE(Gui) + +#ifndef QT_NO_QWS_KEYBOARD + +#ifndef QT_NO_QWS_KBD_QVFB + +class QSocketNotifier; + +class QVFbKeyboardHandler : public QObject, public QWSKeyboardHandler +{ + Q_OBJECT +public: + QVFbKeyboardHandler(const QString &device); + virtual ~QVFbKeyboardHandler(); + +private Q_SLOTS: + void readKeyboardData(); + +private: + QString terminalName; + int kbdFD; + int kbdIdx; + int kbdBufferLen; + unsigned char *kbdBuffer; + QSocketNotifier *notifier; +}; + +#endif // QT_NO_QWS_KBD_QVFB + +#endif // QT_NO_QWS_KEYBOARD + +QT_END_NAMESPACE + +QT_END_HEADER + +#endif // QKBDVFB_QWS_H diff --git a/src/gui/embedded/qkbdvr41xx_qws.cpp b/src/gui/embedded/qkbdvr41xx_qws.cpp new file mode 100644 index 0000000..4613891 --- /dev/null +++ b/src/gui/embedded/qkbdvr41xx_qws.cpp @@ -0,0 +1,185 @@ +/**************************************************************************** +** +** 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 "qkbdvr41xx_qws.h" + +#if !defined(QT_NO_QWS_KEYBOARD) && !defined(QT_NO_QWS_KBD_VR41XX) + +#include <sys/types.h> +#include <sys/stat.h> +#include <sys/ioctl.h> +#include <fcntl.h> +#include <termios.h> +#include <unistd.h> +#include <errno.h> + +#include <qsocketnotifier.h> + +QT_BEGIN_NAMESPACE + +class QWSVr41xxKbPrivate : public QObject +{ + Q_OBJECT +public: + QWSVr41xxKbPrivate(QWSVr41xxKeyboardHandler *h, const QString&); + virtual ~QWSVr41xxKbPrivate(); + + bool isOpen() { return buttonFD > 0; } + +private slots: + void readKeyboardData(); + +private: + QString terminalName; + int buttonFD; + int kbdIdx; + int kbdBufferLen; + unsigned char *kbdBuffer; + QSocketNotifier *notifier; + QWSVr41xxKeyboardHandler *handler; +}; + +QWSVr41xxKeyboardHandler::QWSVr41xxKeyboardHandler(const QString &device) +{ + d = new QWSVr41xxKbPrivate(this, device); +} + +QWSVr41xxKeyboardHandler::~QWSVr41xxKeyboardHandler() +{ + delete d; +} + +QWSVr41xxKbPrivate::QWSVr41xxKbPrivate(QWSVr41xxKeyboardHandler *h, const QString &device) : handler(h) +{ + terminalName = device; + if (terminalName.isEmpty()) + terminalName = QLatin1String("/dev/buttons"); + buttonFD = -1; + notifier = 0; + + buttonFD = open(terminalName.toLatin1().constData(), O_RDWR | O_NDELAY, 0);; + if (buttonFD < 0) { + qWarning("Cannot open %s\n", qPrintable(terminalName)); + return; + } + + if (buttonFD >= 0) { + notifier = new QSocketNotifier(buttonFD, QSocketNotifier::Read, this); + connect(notifier, SIGNAL(activated(int)),this, + SLOT(readKeyboardData())); + } + + kbdBufferLen = 80; + kbdBuffer = new unsigned char [kbdBufferLen]; + kbdIdx = 0; +} + +QWSVr41xxKbPrivate::~QWSVr41xxKbPrivate() +{ + if (buttonFD > 0) { + ::close(buttonFD); + buttonFD = -1; + } + delete notifier; + notifier = 0; + delete [] kbdBuffer; +} + +void QWSVr41xxKbPrivate::readKeyboardData() +{ + int n = 0; + do { + n = read(buttonFD, kbdBuffer+kbdIdx, kbdBufferLen - kbdIdx); + if (n > 0) + kbdIdx += n; + } while (n > 0); + + int idx = 0; + while (kbdIdx - idx >= 2) { + unsigned char *next = kbdBuffer + idx; + unsigned short *code = (unsigned short *)next; + int keycode = Qt::Key_unknown; + switch ((*code) & 0x0fff) { + case 0x7: + keycode = Qt::Key_Up; + break; + case 0x9: + keycode = Qt::Key_Right; + break; + case 0x8: + keycode = Qt::Key_Down; + break; + case 0xa: + keycode = Qt::Key_Left; + break; + case 0x3: + keycode = Qt::Key_Up; + break; + case 0x4: + keycode = Qt::Key_Down; + break; + case 0x1: + keycode = Qt::Key_Return; + break; + case 0x2: + keycode = Qt::Key_F4; + break; + default: + qDebug("Unrecognised key sequence %d", *code); + } + if ((*code) & 0x8000) + handler->processKeyEvent(0, keycode, 0, false, false); + else + handler->processKeyEvent(0, keycode, 0, true, false); + idx += 2; + } + + int surplus = kbdIdx - idx; + for (int i = 0; i < surplus; i++) + kbdBuffer[i] = kbdBuffer[idx+i]; + kbdIdx = surplus; +} + +QT_END_NAMESPACE + +#include "qkbdvr41xx_qws.moc" + +#endif // QT_NO_QWS_KBD_VR41XX diff --git a/src/gui/embedded/qkbdvr41xx_qws.h b/src/gui/embedded/qkbdvr41xx_qws.h new file mode 100644 index 0000000..51719cf --- /dev/null +++ b/src/gui/embedded/qkbdvr41xx_qws.h @@ -0,0 +1,73 @@ +/**************************************************************************** +** +** 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$ +** +****************************************************************************/ + +#ifndef QKBDVR41XX_QWS_H +#define QKBDVR41XX_QWS_H + +#include <QtGui/qkbd_qws.h> + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +QT_MODULE(Gui) + +#if !defined(QT_NO_QWS_KEYBOARD) && !defined(QT_NO_QWS_KBD_VR41XX) + +class QWSVr41xxKbPrivate; + +class QWSVr41xxKeyboardHandler : public QWSKeyboardHandler +{ +public: + explicit QWSVr41xxKeyboardHandler(const QString&); + virtual ~QWSVr41xxKeyboardHandler(); + +private: + QWSVr41xxKbPrivate *d; +}; + +#endif // QT_NO_QWS_KBD_VR41XX + +QT_END_NAMESPACE + +QT_END_HEADER + +#endif // QKBDVR41XX_QWS_H diff --git a/src/gui/embedded/qkbdyopy_qws.cpp b/src/gui/embedded/qkbdyopy_qws.cpp new file mode 100644 index 0000000..bfa8c64 --- /dev/null +++ b/src/gui/embedded/qkbdyopy_qws.cpp @@ -0,0 +1,209 @@ +/**************************************************************************** +** +** 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$ +** +****************************************************************************/ + +/* + * YOPY buttons driver + * Contributed by Ron Victorelli (victorrj at icubed.com) + */ + +#include "qkbdyopy_qws.h" + +#ifndef QT_NO_QWS_KBD_YOPY + +#include <sys/types.h> +#include <sys/stat.h> +#include <sys/ioctl.h> +#include <fcntl.h> +#include <termios.h> +#include <unistd.h> +#include <errno.h> + +#include <linux/kd.h> +#include <linux/fb.h> +#include <linux/yopy_button.h> + +extern "C" { + int getpgid(int); +} + +#include <qwidget.h> +#include <qsocketnotifier.h> + +QT_BEGIN_NAMESPACE + +class QWSYopyKbPrivate : public QObject +{ + Q_OBJECT +public: + QWSYopyKbPrivate(QWSYopyKeyboardHandler *h, const QString&); + virtual ~QWSYopyKbPrivate(); + + bool isOpen() { return buttonFD > 0; } + +private slots: + void readKeyboardData(); + +private: + QString terminalName; + int buttonFD; + struct termios newT, oldT; + QSocketNotifier *notifier; + QWSYopyKeyboardHandler *handler; +}; + +QWSYopyKeyboardHandler::QWSYopyKeyboardHandler(const QString &device) +{ + d = new QWSYopyKbPrivate(this, device); +} + +QWSYopyKeyboardHandler::~QWSYopyKeyboardHandler() +{ + delete d; +} + +QWSYopyKbPrivate::QWSYopyKbPrivate(QWSYopyKeyboardHandler *h, const QString &device) : handler(h) +{ + terminalName = device.isEmpty()?"/dev/tty1":device.toLatin1().constData(); + buttonFD = -1; + notifier = 0; + + buttonFD = ::open(terminalName.toLatin1().constData(), O_RDWR | O_NDELAY, 0); + if (buttonFD < 0) { + qWarning("Cannot open %s\n", qPrintable(terminalName)); + return; + } else { + + tcsetpgrp(buttonFD, getpgid(0)); + + /* put tty into "straight through" mode. + */ + if (tcgetattr(buttonFD, &oldT) < 0) { + qFatal("Linux-kbd: tcgetattr failed"); + } + + newT = oldT; + newT.c_lflag &= ~(ICANON | ECHO | ISIG); + newT.c_iflag &= ~(ISTRIP | IGNCR | ICRNL | INLCR | IXOFF | IXON); + newT.c_iflag |= IGNBRK; + newT.c_cc[VMIN] = 0; + newT.c_cc[VTIME] = 0; + + + if (tcsetattr(buttonFD, TCSANOW, &newT) < 0) { + qFatal("Linux-kbd: TCSANOW tcsetattr failed"); + } + + if (ioctl(buttonFD, KDSKBMODE, K_MEDIUMRAW) < 0) { + qFatal("Linux-kbd: KDSKBMODE tcsetattr failed"); + } + + notifier = new QSocketNotifier(buttonFD, QSocketNotifier::Read, this); + connect(notifier, SIGNAL(activated(int)),this, + SLOT(readKeyboardData())); + } +} + +QWSYopyKbPrivate::~QWSYopyKbPrivate() +{ + if (buttonFD > 0) { + ::close(buttonFD); + buttonFD = -1; + } +} + +void QWSYopyKbPrivate::readKeyboardData() +{ + uchar buf[1]; + char c='1'; + int fd; + + int n=read(buttonFD,buf,1); + if (n<0) { + qDebug("Keyboard read error %s",strerror(errno)); + } else { + uint code = buf[0]&YPBUTTON_CODE_MASK; + bool press = !(buf[0]&0x80); + // printf("Key=%d/%d/%d\n",buf[1],code,press); + int k=(-1); + switch(code) { + case 39: k=Qt::Key_Up; break; + case 44: k=Qt::Key_Down; break; + case 41: k=Qt::Key_Left; break; + case 42: k=Qt::Key_Right; break; + case 56: k=Qt::Key_F1; break; //windows + case 29: k=Qt::Key_F2; break; //cycle + case 24: k=Qt::Key_F3; break; //record + case 23: k=Qt::Key_F4; break; //mp3 + case 4: k=Qt::Key_F5; break; // PIMS + case 1: k=Qt::Key_Escape; break; // Escape + case 40: k=Qt::Key_Up; break; // prev + case 45: k=Qt::Key_Down; break; // next + case 35: if(!press) { + fd = open("/proc/sys/pm/sleep",O_RDWR,0); + if(fd >= 0) { + write(fd,&c,sizeof(c)); + close(fd); + // + // Updates all widgets. + // + QWidgetList list = QApplication::allWidgets(); + for (int i = 0; i < list.size(); ++i) { + QWidget *w = list.at(i); + w->update(); + } + } + } + break; + + default: k=(-1); break; + } + + if (k >= 0) { + handler->processKeyEvent(0, k, 0, press, false); + } + } +} + +QT_END_NAMESPACE + +#include "qkbdyopy_qws.moc" + +#endif // QT_NO_QWS_KBD_YOPY diff --git a/src/gui/embedded/qkbdyopy_qws.h b/src/gui/embedded/qkbdyopy_qws.h new file mode 100644 index 0000000..81c1c4d --- /dev/null +++ b/src/gui/embedded/qkbdyopy_qws.h @@ -0,0 +1,73 @@ +/**************************************************************************** +** +** 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$ +** +****************************************************************************/ + +#ifndef QKBDYOPY_QWS_H +#define QKBDYOPY_QWS_H + +#include <QtGui/qkbd_qws.h> + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +QT_MODULE(Gui) + +#ifndef QT_NO_QWS_KBD_YOPY + +class QWSYopyKbPrivate; + +class QWSYopyKeyboardHandler : public QWSKeyboardHandler +{ +public: + explicit QWSYopyKeyboardHandler(const QString&); + virtual ~QWSYopyKeyboardHandler(); + +private: + QWSYopyKbPrivate *d; +}; + +#endif // QT_NO_QWS_KBD_YOPY + +QT_END_NAMESPACE + +QT_END_HEADER + +#endif // QKBDYOPY_QWS_H diff --git a/src/gui/embedded/qlock.cpp b/src/gui/embedded/qlock.cpp new file mode 100644 index 0000000..874ca7d --- /dev/null +++ b/src/gui/embedded/qlock.cpp @@ -0,0 +1,318 @@ +/**************************************************************************** +** +** 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 "qlock_p.h" + +#ifndef QT_NO_QWS_MULTIPROCESS + +#include "qwssignalhandler_p.h" +#include <unistd.h> +#include <sys/types.h> +#if defined(Q_OS_DARWIN) +# define Q_NO_SEMAPHORE +# include <sys/stat.h> +# include <sys/file.h> +#else // Q_OS_DARWIN +# include <sys/sem.h> +# if (defined(__GNU_LIBRARY__) && !defined(_SEM_SEMUN_UNDEFINED) && !defined(QT_LINUXBASE)) \ + || defined(Q_OS_FREEBSD) || defined(Q_OS_OPENBSD) || defined(Q_OS_NETBSD) \ + || defined(Q_OS_BSDI) + /* union semun is defined by including <sys/sem.h> */ +# else +/* according to X/OPEN we have to define it ourselves */ +union semun { + int val; /* value for SETVAL */ + struct semid_ds *buf; /* buffer for IPC_STAT, IPC_SET */ + unsigned short *array; /* array for GETALL, SETALL */ +}; +# endif +#endif // Q_OS_DARWIN +#include <sys/ipc.h> +#include <string.h> +#include <errno.h> +#include <qdebug.h> +#include <signal.h> + +#endif // QT_NO_QWS_MULTIPROCESS + +#define MAX_LOCKS 200 // maximum simultaneous read locks + +QT_BEGIN_NAMESPACE + + +#ifndef QT_NO_QWS_MULTIPROCESS +class QLockData +{ +public: +#ifdef Q_NO_SEMAPHORE + QByteArray file; +#endif // Q_NO_SEMAPHORE + int id; + int count; + bool owned; +}; +#endif // QT_NO_QWS_MULTIPROCESS + +/*! + \class QLock + \brief The QLock class is a wrapper for a System V shared semaphore. + + \ingroup qws + \ingroup io + + \internal + + It is used by \l{Qt for Embedded Linux} for synchronizing access to the graphics + card and shared memory region between processes. +*/ + +/*! + \enum QLock::Type + + \value Read + \value Write +*/ + +/*! + \fn QLock::QLock(const QString &filename, char id, bool create) + + Creates a lock. \a filename is the file path of the Unix-domain + socket the \l{Qt for Embedded Linux} client is using. \a id is the name of the + particular lock to be created on that socket. If \a create is true + the lock is to be created (as the Qt for Embedded Linux server does); if \a + create is false the lock should exist already (as the Qt for Embedded Linux + client expects). +*/ + +QLock::QLock(const QString &filename, char id, bool create) +{ +#ifdef QT_NO_QWS_MULTIPROCESS + Q_UNUSED(filename); + Q_UNUSED(id); + Q_UNUSED(create); +#else + data = new QLockData; + data->count = 0; +#ifdef Q_NO_SEMAPHORE + data->file = QString(filename+id).toLocal8Bit().constData(); + for(int x = 0; x < 2; x++) { + data->id = open(data->file, O_RDWR | (x ? O_CREAT : 0), S_IRWXU); + if(data->id != -1 || !create) { + data->owned = x; + break; + } + } +#else + key_t semkey = ftok(filename.toLocal8Bit().constData(), id); + data->id = semget(semkey,0,0); + data->owned = create; + if (create) { + semun arg; arg.val = 0; + if (data->id != -1) + semctl(data->id,0,IPC_RMID,arg); + data->id = semget(semkey,1,IPC_CREAT|0600); + arg.val = MAX_LOCKS; + semctl(data->id,0,SETVAL,arg); + + QWSSignalHandler::instance()->addSemaphore(data->id); + } +#endif + if (data->id == -1) { + int eno = errno; + qWarning("Cannot %s semaphore %s '%c'", (create ? "create" : "get"), + qPrintable(filename), id); + qDebug() << "Error" << eno << strerror(eno); + } +#endif +} + +/*! + \fn QLock::~QLock() + + Destroys a lock +*/ + +QLock::~QLock() +{ +#ifndef QT_NO_QWS_MULTIPROCESS + if (locked()) + unlock(); +#ifdef Q_NO_SEMAPHORE + if(isValid()) { + close(data->id); + if(data->owned) + unlink(data->file); + } +#else + if(data->owned) + QWSSignalHandler::instance()->removeSemaphore(data->id); +#endif + delete data; +#endif +} + +/*! + \fn bool QLock::isValid() const + + Returns true if the lock constructor was successful; returns false if + the lock could not be created or was not available to connect to. +*/ + +bool QLock::isValid() const +{ +#ifndef QT_NO_QWS_MULTIPROCESS + return (data->id != -1); +#else + return true; +#endif +} + +/*! + Locks the semaphore with a lock of type \a t. Locks can either be + \c Read or \c Write. If a lock is \c Read, attempts by other + processes to obtain \c Read locks will succeed, and \c Write + attempts will block until the lock is unlocked. If locked as \c + Write, all attempts to lock by other processes will block until + the lock is unlocked. Locks are stacked: i.e. a given QLock can be + locked multiple times by the same process without blocking, and + will only be unlocked after a corresponding number of unlock() + calls. +*/ + +void QLock::lock(Type t) +{ +#ifdef QT_NO_QWS_MULTIPROCESS + Q_UNUSED(t); +#else + if (!data->count) { +#ifdef Q_NO_SEMAPHORE + int op = LOCK_SH; + if(t == Write) + op = LOCK_EX; + for(int rv=1; rv;) { + rv = flock(data->id, op); + if (rv == -1 && errno != EINTR) + qDebug("Semop lock failure %s",strerror(errno)); + } +#else + sembuf sops; + sops.sem_num = 0; + sops.sem_flg = SEM_UNDO; + + if (t == Write) { + sops.sem_op = -MAX_LOCKS; + type = Write; + } else { + sops.sem_op = -1; + type = Read; + } + + int rv; + do { + rv = semop(data->id,&sops,1); + if (rv == -1 && errno != EINTR) + qDebug("Semop lock failure %s",strerror(errno)); + } while (rv == -1 && errno == EINTR); +#endif + } + data->count++; +#endif +} + +/*! + \fn void QLock::unlock() + + Unlocks the semaphore. If other processes were blocking waiting to + lock() the semaphore, one of them will wake up and succeed in + lock()ing. +*/ + +void QLock::unlock() +{ +#ifndef QT_NO_QWS_MULTIPROCESS + if(data->count) { + data->count--; + if(!data->count) { +#ifdef Q_NO_SEMAPHORE + for(int rv=1; rv;) { + rv = flock(data->id, LOCK_UN); + if (rv == -1 && errno != EINTR) + qDebug("Semop lock failure %s",strerror(errno)); + } +#else + sembuf sops; + sops.sem_num = 0; + sops.sem_op = 1; + sops.sem_flg = SEM_UNDO; + if (type == Write) + sops.sem_op = MAX_LOCKS; + + int rv; + do { + rv = semop(data->id,&sops,1); + if (rv == -1 && errno != EINTR) + qDebug("Semop unlock failure %s",strerror(errno)); + } while (rv == -1 && errno == EINTR); +#endif + } + } else { + qDebug("Unlock without corresponding lock"); + } +#endif +} + +/*! + \fn bool QLock::locked() const + + Returns true if the lock is currently held by the current process; + otherwise returns false. +*/ + +bool QLock::locked() const +{ +#ifndef QT_NO_QWS_MULTIPROCESS + return (data->count > 0); +#else + return false; +#endif +} + +QT_END_NAMESPACE diff --git a/src/gui/embedded/qlock_p.h b/src/gui/embedded/qlock_p.h new file mode 100644 index 0000000..92e0704 --- /dev/null +++ b/src/gui/embedded/qlock_p.h @@ -0,0 +1,100 @@ +/**************************************************************************** +** +** 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$ +** +****************************************************************************/ + +#ifndef QLOCK_P_H +#define QLOCK_P_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. This header file may +// change from version to version without notice, or even be +// removed. +// +// We mean it. +// + +#include "QtCore/qstring.h" + +QT_BEGIN_NAMESPACE + +class QLockData; + +class Q_GUI_EXPORT QLock +{ +public: + QLock(const QString &filename, char id, bool create = false); + ~QLock(); + + enum Type { Read, Write }; + + bool isValid() const; + void lock(Type type); + void unlock(); + bool locked() const; + +private: + Type type; + QLockData *data; +}; + + +// Nice class for ensuring the lock is released. +// Just create one on the stack and the lock is automatically released +// when QLockHandle is destructed. +class Q_GUI_EXPORT QLockHandle +{ +public: + QLockHandle(QLock *l, QLock::Type type) : qlock(l) { qlock->lock(type); } + ~QLockHandle() { if (locked()) qlock->unlock(); } + + void lock(QLock::Type type) { qlock->lock(type); } + void unlock() { qlock->unlock(); } + bool locked() const { return qlock->locked(); } + +private: + QLock *qlock; +}; + +QT_END_NAMESPACE + +#endif // QLOCK_P_H diff --git a/src/gui/embedded/qmouse_qws.cpp b/src/gui/embedded/qmouse_qws.cpp new file mode 100644 index 0000000..044a574 --- /dev/null +++ b/src/gui/embedded/qmouse_qws.cpp @@ -0,0 +1,653 @@ +/**************************************************************************** +** +** 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 "qmouse_qws.h" +#include "qwindowsystem_qws.h" +#include "qscreen_qws.h" +#include "qapplication.h" +#include "qtextstream.h" +#include "qfile.h" +#include "qdebug.h" +#include "qscreen_qws.h" + +QT_BEGIN_NAMESPACE + +/*! + \class QWSPointerCalibrationData + \ingroup qws + + \brief The QWSPointerCalibrationData class is a container for + mouse calibration data in Qt for Embedded Linux. + + Note that this class is only available in \l{Qt for Embedded Linux}. + + QWSPointerCalibrationData stores device and screen coordinates in + the devPoints and screenPoints variables, respectively. + + A calibration program should create a QWSPointerCalibrationData + object, fill the devPoints and screenPoints variables with its + device and screen coordinates, and pass the object to the mouse + driver using the QWSMouseHandler::calibrate() function. + + \sa QWSCalibratedMouseHandler, {Mouse Calibration Example} +*/ + +/*! + \variable QWSPointerCalibrationData::devPoints + \brief the raw device coordinates for each value of the Location enum. +*/ + +/*! + \variable QWSPointerCalibrationData::screenPoints + \brief the logical screen coordinates for each value of the Location enum. +*/ + +/*! + \enum QWSPointerCalibrationData::Location + + This enum describes the various logical positions that can be + specified by the devPoints and screenPoints variables. + + \value TopLeft Index of the top left corner of the screen. + \value BottomLeft Index of the bottom left corner of the screen. + \value BottomRight Index of the bottom right corner of the screen. + \value TopRight Index of the top right corner of the screen. + \value Center Index of the center of the screen. + \value LastLocation Last index in the pointer arrays. +*/ + +class QWSMouseHandlerPrivate +{ +public: + QWSMouseHandlerPrivate() : screen(qt_screen) {} + + const QScreen *screen; +}; + +/*! + \class QWSMouseHandler + \ingroup qws + + \brief The QWSMouseHandler class is a base class for mouse drivers in + Qt for Embedded Linux. + + Note that this class is only available in \l{Qt for Embedded Linux}. + + \l{Qt for Embedded Linux} provides ready-made drivers for several mouse + protocols, see the \l{Qt for Embedded Linux Pointer Handling}{pointer + handling} documentation for details. Custom mouse drivers can be + implemented by subclassing the QWSMouseHandler class and creating + a mouse driver plugin (derived from QMouseDriverPlugin). + The default implementation of the QMouseDriverFactory class + will automatically detect the plugin, and load the driver into the + server application at run-time using Qt's \l {How to Create Qt + Plugins}{plugin system}. + + The mouse driver receives mouse events from the system device and + encapsulates each event with an instance of the QWSEvent class + which it then passes to the server application (the server is + responsible for propagating the event to the appropriate + client). To receive mouse events, a QWSMouseHandler object will + usually create a QSocketNotifier object for the given device. The + QSocketNotifier class provides support for monitoring activity on + a file descriptor. When the socket notifier receives data, it will + call the mouse driver's mouseChanged() function to send the event + to the \l{Qt for Embedded Linux} server application for relaying to + clients. + + If you are creating a driver for a device that needs calibration + or noise reduction, such as a touchscreen, use the + QWSCalibratedMouseHandler subclass instead to take advantage of + the calibrate() and clearCalibration() functions. The \l + {qws/mousecalibration}{Mouse Calibration} + demonstrates how to write a simple program using the mechanisms + provided by the QWSMouseHandler class to calibrate a mouse driver. + + Note that when deriving from the QWSMouseHandler class, the + resume() and suspend() functions must be reimplemented to control + the flow of mouse input, i.e., the default implementation does + nothing. Reimplementations of these functions typically call the + QSocketNotifier::setEnabled() function to enable or disable the + socket notifier, respectively. + + In addition, QWSMouseHandler provides the setScreen() function + that allows you to specify a screen for your mouse driver and the + limitToScreen() function that ensures that a given position is + within this screen's boundaries (changing the position if + necessary). Finally, QWSMouseHandler provides the pos() function + returning the current mouse position. + + \sa QMouseDriverPlugin, QMouseDriverFactory, {Qt for Embedded Linux Pointer + Handling} +*/ + + +/*! + \fn void QWSMouseHandler::suspend() + + Implement this function to suspend reading and handling of mouse + events, e.g., call the QSocketNotifier::setEnabled() function to + disable the socket notifier. + + \sa resume() +*/ + +/*! + \fn void QWSMouseHandler::resume() + + Implement this function to resume reading and handling mouse + events, e.g., call the QSocketNotifier::setEnabled() function to + enable the socket notifier. + + \sa suspend() +*/ + +/*! + \fn const QPoint &QWSMouseHandler::pos() const + + Returns the current mouse position. + + \sa mouseChanged(), limitToScreen() +*/ + +/*! + Constructs a mouse driver. The \a driver and \a device arguments + are passed by the QWS_MOUSE_PROTO environment variable. + + Call the QWSServer::setMouseHandler() function to make the newly + created mouse driver, the primary driver. Note that the primary + driver is controlled by the system, i.e., the system will delete + it upon exit. +*/ +QWSMouseHandler::QWSMouseHandler(const QString &, const QString &) + : mousePos(QWSServer::mousePosition), d_ptr(new QWSMouseHandlerPrivate) +{ +} + +/*! + Destroys this mouse driver. + + Do not call this function if this driver is the primary mouse + driver, i.e., if QWSServer::setMouseHandler() function has been + called passing this driver as argument. The primary mouse + driver is deleted by the system. +*/ +QWSMouseHandler::~QWSMouseHandler() +{ + delete d_ptr; +} + +/*! + Ensures that the given \a position is within the screen's + boundaries, changing the \a position if necessary. + + \sa pos(), setScreen() +*/ + +void QWSMouseHandler::limitToScreen(QPoint &position) +{ + position.setX(qMin(d_ptr->screen->deviceWidth() - 1, qMax(0, position.x()))); + position.setY(qMin(d_ptr->screen->deviceHeight() - 1, qMax(0, position.y()))); +} + +/*! + \since 4.2 + + Sets the screen for this mouse driver to be the given \a screen. + + \sa limitToScreen() +*/ +void QWSMouseHandler::setScreen(const QScreen *screen) +{ + d_ptr->screen = (screen ? screen : qt_screen); +} + +/*! + Notifies the system of a new mouse event. + + This function updates the current mouse position and sends the + event to the \l{Qt for Embedded Linux} server application for + delivery to the correct widget. Note that a custom mouse driver must call + this function whenever it wants to deliver a new mouse event. + + The given \a position is the global position of the mouse cursor. + The \a state parameter is a bitmask of the Qt::MouseButton enum's + values, indicating which mouse buttons are pressed. The \a wheel + parameter is the delta value of the mouse wheel as returned by + QWheelEvent::delta(). + + \sa pos() +*/ +void QWSMouseHandler::mouseChanged(const QPoint &position, int state, int wheel) +{ + mousePos = position + d_ptr->screen->offset(); + QWSServer::sendMouseEvent(mousePos, state, wheel); +} + +/*! + \fn QWSMouseHandler::clearCalibration() + + This virtual function allows subclasses of QWSMouseHandler to + clear the calibration information. Note that the default + implementation does nothing. + + \sa QWSCalibratedMouseHandler::clearCalibration(), calibrate() +*/ + +/*! + \fn QWSMouseHandler::calibrate(const QWSPointerCalibrationData *data) + + This virtual function allows subclasses of QWSMouseHandler to set + the calibration information passed in the given \a data. Note that + the default implementation does nothing. + + \sa QWSCalibratedMouseHandler::calibrate(), clearCalibration() +*/ + +/*! \fn QWSMouseHandler::getCalibration(QWSPointerCalibrationData *data) const + This virtual function allows subclasses of QWSMouseHandler + to fill in the device coordinates in \a data with values + that correspond to screen coordinates that are already in + \a data. Note that the default implementation does nothing. + */ + +/*! + \class QWSCalibratedMouseHandler + \ingroup qws + + \brief The QWSCalibratedMouseHandler class provides mouse + calibration and noise reduction in Qt for Embedded Linux. + + Note that this class is only available in \l{Qt for Embedded Linux}. + + \l{Qt for Embedded Linux} provides ready-made drivers for several mouse + protocols, see the \l{Qt for Embedded Linux Pointer Handling}{pointer + handling} documentation for details. In general, custom mouse + drivers can be implemented by subclassing the QWSMouseHandler + class. But when the system device does not have a fixed mapping + between device and screen coordinates and/or produces noisy events + (e.g., a touchscreen), you should derive from the + QWSCalibratedMouseHandler class instead to take advantage of its + calibration functionality. As always, you must also create a mouse + driver plugin (derived from QMouseDriverPlugin); + the implementation of the QMouseDriverFactory class will then + automatically detect the plugin, and load the driver into the + server application at run-time using Qt's + \l{How to Create Qt Plugins}{plugin system}. + + QWSCalibratedMouseHandler provides an implementation of the + calibrate() function to update the calibration parameters based on + coordinate mapping of the given calibration data. The calibration + data is represented by an QWSPointerCalibrationData object. The + linear transformation between device coordinates and screen + coordinates is performed by calling the transform() function + explicitly on the points passed to the + QWSMouseHandler::mouseChanged() function. Use the + clearCalibration() function to make the mouse driver return mouse + events in raw device coordinates and not in screen coordinates. + + The calibration parameters are recalculated whenever calibrate() + is called, and they can be stored using the writeCalibration() + function. Previously written parameters can be retrieved at any + time using the readCalibration() function (calibration parameters + are always read when the class is instantiated). Note that the + calibration parameters is written to and read from the file + currently specified by the POINTERCAL_FILE environment variable; + the default file is \c /etc/pointercal. + + To achieve noise reduction, QWSCalibratedMouseHandler provides the + sendFiltered() function. Use this function instead of + mouseChanged() whenever a mouse event occurs. The filter's size + can be manipulated using the setFilterSize() function. + + \sa QWSMouseHandler, QWSPointerCalibrationData, + {Mouse Calibration Example} +*/ + + +/*! + \internal + */ + +QWSCalibratedMouseHandler::QWSCalibratedMouseHandler(const QString &, const QString &) + : samples(5), currSample(0), numSamples(0) +{ + clearCalibration(); + readCalibration(); +} + +/*! + Fills \a cd with the device coordinates corresponding to the given + screen coordinates. + + \internal +*/ +void QWSCalibratedMouseHandler::getCalibration(QWSPointerCalibrationData *cd) const +{ + const qint64 scale = qint64(a) * qint64(e) - qint64(b) * qint64(d); + const qint64 xOff = qint64(b) * qint64(f) - qint64(c) * qint64(e); + const qint64 yOff = qint64(c) * qint64(d) - qint64(a) * qint64(f); + for (int i = 0; i <= QWSPointerCalibrationData::LastLocation; ++i) { + const qint64 sX = cd->screenPoints[i].x(); + const qint64 sY = cd->screenPoints[i].y(); + const qint64 dX = (s*(e*sX - b*sY) + xOff) / scale; + const qint64 dY = (s*(a*sY - d*sX) + yOff) / scale; + cd->devPoints[i] = QPoint(dX, dY); + } +} + +/*! + Clears the current calibration, i.e., makes the mouse + driver return mouse events in raw device coordinates instead of + screen coordinates. + + \sa calibrate() +*/ +void QWSCalibratedMouseHandler::clearCalibration() +{ + a = 1; + b = 0; + c = 0; + d = 0; + e = 1; + f = 0; + s = 1; +} + + +/*! + Saves the current calibration parameters in \c /etc/pointercal + (separated by whitespace and in alphabetical order). + + You can override the default \c /etc/pointercal by specifying + another file using the POINTERCAL_FILE environment variable. + + \sa readCalibration() +*/ +void QWSCalibratedMouseHandler::writeCalibration() +{ + QString calFile; + calFile = QString::fromLocal8Bit(qgetenv("POINTERCAL_FILE")); + if (calFile.isEmpty()) + calFile = QLatin1String("/etc/pointercal"); + +#ifndef QT_NO_TEXTSTREAM + QFile file(calFile); + if (file.open(QIODevice::WriteOnly)) { + QTextStream t(&file); + t << a << " " << b << " " << c << " "; + t << d << " " << e << " " << f << " " << s << endl; + } else +#endif + { + qCritical("QWSCalibratedMouseHandler::writeCalibration: " + "Could not save calibration into %s", qPrintable(calFile)); + } +} + +/*! + Reads previously written calibration parameters which are stored + in \c /etc/pointercal (separated by whitespace and in alphabetical + order). + + You can override the default \c /etc/pointercal by specifying + another file using the POINTERCAL_FILE environment variable. + + + \sa writeCalibration() +*/ +void QWSCalibratedMouseHandler::readCalibration() +{ + QString calFile = QString::fromLocal8Bit(qgetenv("POINTERCAL_FILE")); + if (calFile.isEmpty()) + calFile = QLatin1String("/etc/pointercal"); + +#ifndef QT_NO_TEXTSTREAM + QFile file(calFile); + if (file.open(QIODevice::ReadOnly)) { + QTextStream t(&file); + t >> a >> b >> c >> d >> e >> f >> s; + if (s == 0 || t.status() != QTextStream::Ok) { + qCritical("Corrupt calibration data"); + clearCalibration(); + } + } else +#endif + { + qDebug() << "Could not read calibration:" <<calFile; + } +} + +static int ilog2(quint32 n) +{ + int result = 0; + + if (n & 0xffff0000) { + n >>= 16; + result += 16; + } + if (n & 0xff00) { + n >>= 8; + result += 8;} + if (n & 0xf0) { + n >>= 4; + result += 4; + } + if (n & 0xc) { + n >>= 2; + result += 2; + } + if (n & 0x2) + result += 1; + + return result; +} + +/*! + Updates the calibration parameters based on coordinate mapping of + the given \a data. + + Create an instance of the QWSPointerCalibrationData class, fill in + the device and screen coordinates and pass that object to the mouse + driver using this function. + + \sa clearCalibration(), transform() +*/ +void QWSCalibratedMouseHandler::calibrate(const QWSPointerCalibrationData *data) +{ + // Algorithm derived from + // "How To Calibrate Touch Screens" by Carlos E. Vidales, + // printed in Embedded Systems Programming, Vol. 15 no 6, June 2002 + // URL: http://www.embedded.com/showArticle.jhtml?articleID=9900629 + + const QPoint pd0 = data->devPoints[QWSPointerCalibrationData::TopLeft]; + const QPoint pd1 = data->devPoints[QWSPointerCalibrationData::TopRight]; + const QPoint pd2 = data->devPoints[QWSPointerCalibrationData::BottomRight]; + const QPoint p0 = data->screenPoints[QWSPointerCalibrationData::TopLeft]; + const QPoint p1 = data->screenPoints[QWSPointerCalibrationData::TopRight]; + const QPoint p2 = data->screenPoints[QWSPointerCalibrationData::BottomRight]; + + const qint64 xd0 = pd0.x(); + const qint64 xd1 = pd1.x(); + const qint64 xd2 = pd2.x(); + const qint64 yd0 = pd0.y(); + const qint64 yd1 = pd1.y(); + const qint64 yd2 = pd2.y(); + const qint64 x0 = p0.x(); + const qint64 x1 = p1.x(); + const qint64 x2 = p2.x(); + const qint64 y0 = p0.y(); + const qint64 y1 = p1.y(); + const qint64 y2 = p2.y(); + + qint64 scale = ((xd0 - xd2)*(yd1 - yd2) - (xd1 - xd2)*(yd0 - yd2)); + int shift = 0; + qint64 absScale = qAbs(scale); + // use maximum 16 bit precision to reduce risk of integer overflow + if (absScale > (1 << 16)) { + shift = ilog2(absScale >> 16) + 1; + scale >>= shift; + } + + s = scale; + a = ((x0 - x2)*(yd1 - yd2) - (x1 - x2)*(yd0 - yd2)) >> shift; + b = ((xd0 - xd2)*(x1 - x2) - (x0 - x2)*(xd1 - xd2)) >> shift; + c = (yd0*(xd2*x1 - xd1*x2) + yd1*(xd0*x2 - xd2*x0) + yd2*(xd1*x0 - xd0*x1)) >> shift; + d = ((y0 - y2)*(yd1 - yd2) - (y1 - y2)*(yd0 - yd2)) >> shift; + e = ((xd0 - xd2)*(y1 - y2) - (y0 - y2)*(xd1 - xd2)) >> shift; + f = (yd0*(xd2*y1 - xd1*y2) + yd1*(xd0*y2 - xd2*y0) + yd2*(xd1*y0 - xd0*y1)) >> shift; + + writeCalibration(); +} + +/*! + Transforms the given \a position from device coordinates to screen + coordinates, and returns the transformed position. + + This function is typically called explicitly on the points passed + to the QWSMouseHandler::mouseChanged() function. + + This implementation is a linear transformation using 7 parameters + (\c a, \c b, \c c, \c d, \c e, \c f and \c s) to transform the + device coordinates (\c Xd, \c Yd) into screen coordinates (\c Xs, + \c Ys) using the following equations: + + \snippet doc/src/snippets/code/src_gui_embedded_qmouse_qws.cpp 0 + + \sa mouseChanged() +*/ +QPoint QWSCalibratedMouseHandler::transform(const QPoint &position) +{ + QPoint tp; + + tp.setX((a * position.x() + b * position.y() + c) / s); + tp.setY((d * position.x() + e * position.y() + f) / s); + + return tp; +} + +/*! + Sets the size of the filter used in noise reduction to the given + \a size. + + The sendFiltered() function reduces noice by calculating an + average position from a collection of mouse event positions. The + filter size determines the number of positions that forms the + basis for these calculations. + + \sa sendFiltered() +*/ +void QWSCalibratedMouseHandler::setFilterSize(int size) +{ + samples.resize(qMax(1, size)); + numSamples = 0; + currSample = 0; +} + +/*! + \fn bool QWSCalibratedMouseHandler::sendFiltered(const QPoint &position, int state) + + Notifies the system of a new mouse event \e after applying a noise + reduction filter. Returns true if the filtering process is + successful; otherwise returns false. Note that if the filtering + process failes, the system is not notified about the event. + + The given \a position is the global position of the mouse. The \a + state parameter is a bitmask of the Qt::MouseButton enum's values + indicating which mouse buttons are pressed. + + The noice is reduced by calculating an average position from a + collection of mouse event positions and then calling the + mouseChanged() function with the new position. The number of + positions that is used is determined by the filter size. + + \sa mouseChanged(), setFilterSize() +*/ +bool QWSCalibratedMouseHandler::sendFiltered(const QPoint &position, int button) +{ + if (!button) { + if (numSamples >= samples.count()) + mouseChanged(transform(position), 0); + currSample = 0; + numSamples = 0; + return true; + } + + bool sent = false; + samples[currSample] = position; + numSamples++; + if (numSamples >= samples.count()) { + + int ignore = -1; + if (samples.count() > 2) { // throw away the "worst" sample + int maxd = 0; + for (int i = 0; i < samples.count(); i++) { + int d = (mousePos - samples[i]).manhattanLength(); + if (d > maxd) { + maxd = d; + ignore = i; + } + } + } + + // average the rest + QPoint pos(0, 0); + int numAveraged = 0; + for (int i = 0; i < samples.count(); i++) { + if (ignore == i) + continue; + pos += samples[i]; + ++numAveraged; + } + if (numAveraged) + pos /= numAveraged; + + mouseChanged(transform(pos), button); + sent = true; + } + currSample++; + if (currSample >= samples.count()) + currSample = 0; + + return sent; +} + +QT_END_NAMESPACE diff --git a/src/gui/embedded/qmouse_qws.h b/src/gui/embedded/qmouse_qws.h new file mode 100644 index 0000000..d003b4e --- /dev/null +++ b/src/gui/embedded/qmouse_qws.h @@ -0,0 +1,123 @@ +/**************************************************************************** +** +** 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$ +** +****************************************************************************/ + +#ifndef QMOUSE_QWS_H +#define QMOUSE_QWS_H + +#include <QtCore/qobject.h> +#include <QtGui/qpolygon.h> + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +QT_MODULE(Gui) + +class QWSMouseHandlerPrivate; +class QScreen; + +class Q_GUI_EXPORT QWSPointerCalibrationData +{ +public: + enum Location { TopLeft = 0, BottomLeft = 1, BottomRight = 2, TopRight = 3, + Center = 4, LastLocation = Center }; + QPoint devPoints[5]; + QPoint screenPoints[5]; +}; + +class Q_GUI_EXPORT QWSMouseHandler +{ +public: + explicit QWSMouseHandler(const QString &driver = QString(), + const QString &device = QString()); + virtual ~QWSMouseHandler(); + + virtual void clearCalibration() {} + virtual void calibrate(const QWSPointerCalibrationData *) {} + virtual void getCalibration(QWSPointerCalibrationData *) const {} + + virtual void resume() = 0; + virtual void suspend() = 0; + + void limitToScreen(QPoint &pt); + void mouseChanged(const QPoint& pos, int bstate, int wheel = 0); + const QPoint &pos() const { return mousePos; } + + void setScreen(const QScreen *screen); + +protected: + QPoint &mousePos; + QWSMouseHandlerPrivate *d_ptr; +}; + + +class Q_GUI_EXPORT QWSCalibratedMouseHandler : public QWSMouseHandler +{ +public: + explicit QWSCalibratedMouseHandler(const QString &driver = QString(), + const QString &device = QString()); + + virtual void clearCalibration(); + virtual void calibrate(const QWSPointerCalibrationData *); + virtual void getCalibration(QWSPointerCalibrationData *) const; + +protected: + bool sendFiltered(const QPoint &, int button); + QPoint transform(const QPoint &); + + void readCalibration(); + void writeCalibration(); + void setFilterSize(int); + +private: + int a, b, c; + int d, e, f; + int s; + QPolygon samples; + int currSample; + int numSamples; +}; + +QT_END_NAMESPACE + +QT_END_HEADER + +#endif // QMOUSE_QWS_H diff --git a/src/gui/embedded/qmousebus_qws.cpp b/src/gui/embedded/qmousebus_qws.cpp new file mode 100644 index 0000000..6b26349 --- /dev/null +++ b/src/gui/embedded/qmousebus_qws.cpp @@ -0,0 +1,238 @@ +/**************************************************************************** +** +** 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 "qmousebus_qws.h" + +#ifndef QT_NO_QWS_MOUSE_BUS + +#include "qwindowsystem_qws.h" +#include "qsocketnotifier.h" + +#include "qapplication.h" + +#include <unistd.h> +#include <stdlib.h> +#include <stdio.h> +#include <sys/ioctl.h> +#include <sys/types.h> +#include <sys/stat.h> +#include <fcntl.h> +#include <errno.h> +#include <termios.h> + +QT_BEGIN_NAMESPACE + +/* + * bus mouse driver (a.k.a. Logitech busmouse) + */ + +class QWSBusMouseHandlerPrivate : public QObject +{ + Q_OBJECT +public: + QWSBusMouseHandlerPrivate(QWSBusMouseHandler *h, const QString &driver, const QString &device); + ~QWSBusMouseHandlerPrivate(); + + void suspend(); + void resume(); + +private slots: + void readMouseData(); + +protected: + enum { mouseBufSize = 128 }; + QWSBusMouseHandler *handler; + QSocketNotifier *mouseNotifier; + int mouseFD; + int mouseIdx; + int obstate; + uchar mouseBuf[mouseBufSize]; +}; + +QWSBusMouseHandler::QWSBusMouseHandler(const QString &driver, const QString &device) + : QWSMouseHandler(driver, device) +{ + d = new QWSBusMouseHandlerPrivate(this, driver, device); +} + +QWSBusMouseHandler::~QWSBusMouseHandler() +{ + delete d; +} + +void QWSBusMouseHandler::suspend() +{ + d->suspend(); +} + +void QWSBusMouseHandler::resume() +{ + d->resume(); +} + + +QWSBusMouseHandlerPrivate::QWSBusMouseHandlerPrivate(QWSBusMouseHandler *h, + const QString &, const QString &device) + : handler(h) + +{ + QString mouseDev = device; + if (mouseDev.isEmpty()) + mouseDev = QLatin1String("/dev/mouse"); + obstate = -1; + mouseFD = -1; + mouseFD = open(mouseDev.toLocal8Bit(), O_RDWR | O_NDELAY); + if (mouseFD < 0) + mouseFD = open(mouseDev.toLocal8Bit(), O_RDONLY | O_NDELAY); + if (mouseFD < 0) + qDebug("Cannot open %s (%s)", qPrintable(mouseDev), strerror(errno)); + + // Clear pending input + tcflush(mouseFD,TCIFLUSH); + usleep(50000); + + char buf[100]; // busmouse driver will not read if bufsize < 3, YYD + while (read(mouseFD, buf, 100) > 0) { } // eat unwanted replies + + mouseIdx = 0; + + mouseNotifier = new QSocketNotifier(mouseFD, QSocketNotifier::Read, this); + connect(mouseNotifier, SIGNAL(activated(int)),this, SLOT(readMouseData())); +} + +QWSBusMouseHandlerPrivate::~QWSBusMouseHandlerPrivate() +{ + if (mouseFD >= 0) { + tcflush(mouseFD,TCIFLUSH); // yyd. + close(mouseFD); + } +} + + +void QWSBusMouseHandlerPrivate::suspend() +{ + mouseNotifier->setEnabled(false); +} + + +void QWSBusMouseHandlerPrivate::resume() +{ + mouseIdx = 0; + obstate = -1; + mouseNotifier->setEnabled(true); +} + +void QWSBusMouseHandlerPrivate::readMouseData() +{ + int n; + // It'll only read 3 bytes a time and return all other buffer zeroed, thus cause protocol errors + for (;;) { + if (mouseBufSize - mouseIdx < 3) + break; + n = read(mouseFD, mouseBuf+mouseIdx, 3); + if (n != 3) + break; + mouseIdx += 3; + } + + static const int accel_limit = 5; + static const int accel = 2; + + int idx = 0; + int bstate = 0; + int dx = 0, dy = 0; + bool sendEvent = false; + int tdx = 0, tdy = 0; + + while (mouseIdx-idx >= 3) { +#if 0 // debug + qDebug("Got mouse data"); +#endif + uchar *mb = mouseBuf+idx; + bstate = 0; + dx = 0; + dy = 0; + sendEvent = false; + if (((mb[0] & 0x04))) + bstate |= Qt::LeftButton; + if (((mb[0] & 0x01))) + bstate |= Qt::RightButton; + + dx=(signed char)mb[1]; + dy=(signed char)mb[2]; + sendEvent=true; + + if (sendEvent) { + if (qAbs(dx) > accel_limit || qAbs(dy) > accel_limit) { + dx *= accel; + dy *= accel; + } + tdx += dx; + tdy += dy; + if (bstate != obstate) { + QPoint pos = handler->pos() + QPoint(tdx,-tdy); + handler->limitToScreen(pos); + handler->mouseChanged(pos,bstate); + sendEvent = false; + tdx = 0; + tdy = 0; + obstate = bstate; + } + } + idx += 3; + } + if (sendEvent) { + QPoint pos = handler->pos() + QPoint(tdx,-tdy); + handler->limitToScreen(pos); + handler->mouseChanged(pos,bstate); + } + + int surplus = mouseIdx - idx; + for (int i = 0; i < surplus; i++) + mouseBuf[i] = mouseBuf[idx+i]; + mouseIdx = surplus; +} + +QT_END_NAMESPACE + +#include "qmousebus_qws.moc" + +#endif // QT_NO_QWS_MOUSE_BUS diff --git a/src/gui/embedded/qmousebus_qws.h b/src/gui/embedded/qmousebus_qws.h new file mode 100644 index 0000000..636b466 --- /dev/null +++ b/src/gui/embedded/qmousebus_qws.h @@ -0,0 +1,76 @@ +/**************************************************************************** +** +** 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$ +** +****************************************************************************/ + +#ifndef QMOUSEBUS_QWS_H +#define QMOUSEBUS_QWS_H + +#include <QtGui/qmouse_qws.h> + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +QT_MODULE(Gui) + +#ifndef QT_NO_QWS_MOUSE_BUS + +class QWSBusMouseHandlerPrivate; + +class QWSBusMouseHandler : public QWSMouseHandler +{ +public: + explicit QWSBusMouseHandler(const QString & = QString(), + const QString & = QString()); + ~QWSBusMouseHandler(); + + void suspend(); + void resume(); +protected: + QWSBusMouseHandlerPrivate *d; +}; + +#endif // QT_NO_QWS_MOUSE_BUS + +QT_END_NAMESPACE + +QT_END_HEADER + +#endif // QMOUSEBUS_QWS_H diff --git a/src/gui/embedded/qmousedriverfactory_qws.cpp b/src/gui/embedded/qmousedriverfactory_qws.cpp new file mode 100644 index 0000000..db1d7a4 --- /dev/null +++ b/src/gui/embedded/qmousedriverfactory_qws.cpp @@ -0,0 +1,195 @@ +/**************************************************************************** +** +** 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 "qmousedriverfactory_qws.h" + +#include "qapplication.h" +#include "qmousepc_qws.h" +#include "qmousebus_qws.h" +#include "qmousevr41xx_qws.h" +#include "qmouseyopy_qws.h" +#include "qmouselinuxtp_qws.h" +#include "qmousevfb_qws.h" +#include "qmousetslib_qws.h" +#include <stdlib.h> +#include "private/qfactoryloader_p.h" +#include "qmousedriverplugin_qws.h" +#include "qdebug.h" + +QT_BEGIN_NAMESPACE + +#if !defined(Q_OS_WIN32) || defined(QT_MAKEDLL) +#ifndef QT_NO_LIBRARY + +Q_GLOBAL_STATIC_WITH_ARGS(QFactoryLoader, loader, + (QWSMouseHandlerFactoryInterface_iid, + QLatin1String("/mousedrivers"), Qt::CaseInsensitive)) + +#endif //QT_NO_LIBRARY +#endif //QT_MAKEDLL + +/*! + \class QMouseDriverFactory + \ingroup qws + + \brief The QMouseDriverFactory class creates mouse drivers in + Qt for Embedded Linux. + + Note that this class is only available in \l{Qt for Embedded Linux}. + + QMouseDriverFactory is used to detect and instantiate the + available mouse drivers, allowing \l{Qt for Embedded Linux} to load the + preferred driver into the server application at runtime. The + create() function returns a QWSMouseHandler object representing + the mouse driver identified by a given key. The valid keys + (i.e. the supported drivers) can be retrieved using the keys() + function. + + \l{Qt for Embedded Linux} provides several built-in mouse drivers. In + addition, custom mouse drivers can be added using Qt's plugin + mechanism, i.e. by subclassing the QWSMouseHandler class and + creating a mouse driver plugin (QMouseDriverPlugin). See the + \l{Qt for Embedded Linux Pointer Handling}{pointer handling} + documentation for details. + + \sa QWSMouseHandler, QMouseDriverPlugin +*/ + +/*! + Creates the mouse driver specified by the given \a key, using the + display specified by the given \a device. + + Note that the keys are case-insensitive. + + \sa keys() +*/ +QWSMouseHandler *QMouseDriverFactory::create(const QString& key, const QString &device) +{ + QString driver = key.toLower(); +#ifndef QT_NO_QWS_MOUSE_LINUXTP + if (driver == QLatin1String("linuxtp") || driver.isEmpty()) + return new QWSLinuxTPMouseHandler(key, device); +#endif +#ifndef QT_NO_QWS_MOUSE_YOPY + if (driver == QLatin1String("yopy") || driver.isEmpty()) + return new QWSYopyMouseHandler(key, device); +#endif +#ifndef QT_NO_QWS_MOUSE_VR41XX + if (driver == QLatin1String("vr41xx") || driver.isEmpty()) + return new QWSVr41xxMouseHandler(key, device); +#endif +#ifndef QT_NO_QWS_MOUSE_PC + if (driver == QLatin1String("auto") + || driver == QLatin1String("intellimouse") + || driver == QLatin1String("microsoft") + || driver == QLatin1String("mousesystems") + || driver == QLatin1String("mouseman") + || driver.isEmpty()) { + return new QWSPcMouseHandler(key, device); + } +#endif +#ifndef QT_NO_QWS_MOUSE_BUS + if (driver == QLatin1String("bus")) + return new QWSBusMouseHandler(key, device); +#endif +#ifndef QT_NO_QWS_MOUSE_TSLIB + if (driver == QLatin1String("tslib") || driver.isEmpty()) + return new QWSTslibMouseHandler(key, device); +#endif +#ifndef QT_NO_QWS_MOUSE_QVFB + if (driver == QLatin1String("qvfbmouse") || driver == QLatin1String("qvfb")) + return new QVFbMouseHandler(key, device); +#endif + +#if !defined(Q_OS_WIN32) || defined(QT_MAKEDLL) +#ifndef QT_NO_LIBRARY + if (QWSMouseHandlerFactoryInterface *factory = qobject_cast<QWSMouseHandlerFactoryInterface*>(loader()->instance(driver))) + return factory->create(driver, device); +#endif +#endif + return 0; +} + +/*! + Returns the list of valid keys, i.e. the available mouse drivers. + + \sa create() +*/ +QStringList QMouseDriverFactory::keys() +{ + QStringList list; + +#ifndef QT_NO_QWS_MOUSE_LINUXTP + list << QLatin1String("LinuxTP"); +#endif +#ifndef QT_NO_QWS_MOUSE_YOPY + list << QLatin1String("Yopy"); +#endif +#ifndef QT_NO_QWS_MOUSE_VR41XX + list << QLatin1String("VR41xx"); +#endif +#ifndef QT_NO_QWS_MOUSE_PC + list << QLatin1String("Auto") + << QLatin1String("IntelliMouse") + << QLatin1String("Microsoft") + << QLatin1String("MouseSystems") + << QLatin1String("MouseMan"); +#endif +#ifndef QT_NO_QWS_MOUSE_BUS + list << QLatin1String("Bus"); +#endif +#ifndef QT_NO_QWS_MOUSE_TSLIB + list << QLatin1String("Tslib"); +#endif + +#if !defined(Q_OS_WIN32) || defined(QT_MAKEDLL) +#ifndef QT_NO_LIBRARY + QStringList plugins = loader()->keys(); + for (int i = 0; i < plugins.size(); ++i) { + if (!list.contains(plugins.at(i))) + list += plugins.at(i); + } +#endif //QT_NO_LIBRARY +#endif //QT_MAKEDLL + return list; +} + +QT_END_NAMESPACE diff --git a/src/gui/embedded/qmousedriverfactory_qws.h b/src/gui/embedded/qmousedriverfactory_qws.h new file mode 100644 index 0000000..6a13bb7 --- /dev/null +++ b/src/gui/embedded/qmousedriverfactory_qws.h @@ -0,0 +1,67 @@ +/**************************************************************************** +** +** 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$ +** +****************************************************************************/ + +#ifndef QMOUSEDRIVERFACTORY_QWS_H +#define QMOUSEDRIVERFACTORY_QWS_H + +#include <QtCore/qstringlist.h> + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +QT_MODULE(Gui) + +class QString; +class QWSMouseHandler; + +class Q_GUI_EXPORT QMouseDriverFactory +{ +public: + static QStringList keys(); + static QWSMouseHandler *create(const QString&, const QString &); +}; + +QT_END_NAMESPACE + +QT_END_HEADER + +#endif // QMOUSEDRIVERFACTORY_QWS_H diff --git a/src/gui/embedded/qmousedriverplugin_qws.cpp b/src/gui/embedded/qmousedriverplugin_qws.cpp new file mode 100644 index 0000000..e7f11b3 --- /dev/null +++ b/src/gui/embedded/qmousedriverplugin_qws.cpp @@ -0,0 +1,124 @@ +/**************************************************************************** +** +** 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 "qmousedriverplugin_qws.h" + +#ifndef QT_NO_LIBRARY + +#include "qmouse_qws.h" + +QT_BEGIN_NAMESPACE + +/*! + \class QMouseDriverPlugin + \ingroup plugins + \ingroup qws + + \brief The QMouseDriverPlugin class is an abstract base class for + mouse driver plugins in Qt for Embedded Linux. + + Note that this class is only available in \l{Qt for Embedded Linux}. + + \l{Qt for Embedded Linux} provides ready-made drivers for several mouse + protocols, see the \l{Qt for Embedded Linux Pointer Handling}{pointer + handling} documentation for details. Custom mouse drivers can be + implemented by subclassing the QWSMouseHandler class and creating + a mouse driver plugin. + + A mouse driver plugin can be created by subclassing + QMouseDriverPlugin and reimplementing the pure virtual keys() and + create() functions. By exporting the derived class using the + Q_EXPORT_PLUGIN2() macro, The default implementation of the + QMouseDriverFactory class will automatically detect the plugin and + load the driver into the server application at run-time. See \l + {How to Create Qt Plugins} for details. + + \sa QWSMouseHandler, QMouseDriverFactory +*/ + +/*! + \fn QStringList QMouseDriverPlugin::keys() const + + Implement this function to return the list of valid keys, i.e. the + mouse drivers supported by this plugin. + + \l{Qt for Embedded Linux} provides ready-made drivers for several mouse + protocols, see the \l {Qt for Embedded Linux Pointer Handling}{pointer + handling} documentation for details. + + \sa create() +*/ + +/*! + Constructs a mouse driver plugin with the given \a parent. + + Note that this constructor is invoked automatically by the + Q_EXPORT_PLUGIN2() macro, so there is no need for calling it + explicitly. +*/ +QMouseDriverPlugin::QMouseDriverPlugin(QObject *parent) + : QObject(parent) +{ +} + +/*! + Destroys the mouse driver plugin. + + Note that Qt destroys a plugin automatically when it is no longer + used, so there is no need for calling the destructor explicitly. +*/ +QMouseDriverPlugin::~QMouseDriverPlugin() +{ +} + +/*! + \fn QScreen* QMouseDriverPlugin::create(const QString &key, const QString& device) + + Implement this function to create a driver matching the type + specified by the given \a key and \a device parameters. Note that + keys are case-insensitive. + + \sa keys() +*/ + +QT_END_NAMESPACE + +#endif // QT_NO_LIBRARY diff --git a/src/gui/embedded/qmousedriverplugin_qws.h b/src/gui/embedded/qmousedriverplugin_qws.h new file mode 100644 index 0000000..4a864f9 --- /dev/null +++ b/src/gui/embedded/qmousedriverplugin_qws.h @@ -0,0 +1,84 @@ +/**************************************************************************** +** +** 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$ +** +****************************************************************************/ + +#ifndef QMOUSEDRIVERPLUGIN_QWS_H +#define QMOUSEDRIVERPLUGIN_QWS_H + +#include <QtCore/qplugin.h> +#include <QtCore/qfactoryinterface.h> + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +QT_MODULE(Gui) + +#ifndef QT_NO_LIBRARY + +class QWSMouseHandler; + +struct Q_GUI_EXPORT QWSMouseHandlerFactoryInterface : public QFactoryInterface +{ + virtual QWSMouseHandler* create(const QString &name, const QString &device) = 0; +}; + +#define QWSMouseHandlerFactoryInterface_iid "com.trolltech.Qt.QWSMouseHandlerFactoryInterface" +Q_DECLARE_INTERFACE(QWSMouseHandlerFactoryInterface, QWSMouseHandlerFactoryInterface_iid) + +class Q_GUI_EXPORT QMouseDriverPlugin : public QObject, public QWSMouseHandlerFactoryInterface +{ + Q_OBJECT + Q_INTERFACES(QWSMouseHandlerFactoryInterface:QFactoryInterface) +public: + explicit QMouseDriverPlugin(QObject *parent = 0); + ~QMouseDriverPlugin(); + + virtual QStringList keys() const = 0; + virtual QWSMouseHandler* create(const QString& driver, const QString &device) = 0; +}; + +#endif // QT_NO_LIBRARY + +QT_END_NAMESPACE + +QT_END_HEADER + +#endif // QMOUSEDRIVERPLUGIN_QWS_H diff --git a/src/gui/embedded/qmouselinuxtp_qws.cpp b/src/gui/embedded/qmouselinuxtp_qws.cpp new file mode 100644 index 0000000..7683be3 --- /dev/null +++ b/src/gui/embedded/qmouselinuxtp_qws.cpp @@ -0,0 +1,334 @@ +/**************************************************************************** +** +** 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 "qmouselinuxtp_qws.h" + +#ifndef QT_NO_QWS_MOUSE_LINUXTP +#include "qwindowsystem_qws.h" +#include "qsocketnotifier.h" +#include "qtimer.h" +#include "qapplication.h" +#include "qscreen_qws.h" + +#include <unistd.h> +#include <stdlib.h> +#include <stdio.h> +#include <sys/ioctl.h> +#include <sys/types.h> +#include <sys/stat.h> +#include <fcntl.h> +#include <errno.h> +#include <termios.h> + +QT_BEGIN_NAMESPACE + +#if defined(QT_QWS_IPAQ) + #define QT_QWS_IPAQ_RAW + #define QT_QWS_SCREEN_COORDINATES + typedef struct { + unsigned short pressure; + unsigned short x; + unsigned short y; + unsigned short pad; + } TS_EVENT; +#elif defined(QT_QWS_EBX) + #define QT_QWS_EBX_RAW + #define QT_QWS_SCREEN_COORDINATES +#ifndef QT_QWS_SHARP + typedef struct { + unsigned short pressure; + unsigned short x; + unsigned short y; + unsigned short pad; + } TS_EVENT; + #else + typedef struct { + long y; + long x; + long pressure; + long long millisecs; + } TS_EVENT; + #define QT_QWS_TP_SAMPLE_SIZE 10 + #define QT_QWS_TP_MINIMUM_SAMPLES 4 + #define QT_QWS_TP_PRESSURE_THRESHOLD 500 + #define QT_QWS_TP_MOVE_LIMIT 50 + #define QT_QWS_TP_JITTER_LIMIT 2 + #endif +#else // not IPAQ, not SHARP + typedef struct { + unsigned short pressure; + unsigned short x; + unsigned short y; + unsigned short pad; + } TS_EVENT; +#endif + +#ifndef QT_QWS_TP_SAMPLE_SIZE +#define QT_QWS_TP_SAMPLE_SIZE 5 +#endif + +#ifndef QT_QWS_TP_MINIMUM_SAMPLES +#define QT_QWS_TP_MINIMUM_SAMPLES 5 +#endif + +#ifndef QT_QWS_TP_PRESSURE_THRESHOLD +#define QT_QWS_TP_PRESSURE_THRESHOLD 1 +#endif + +#ifndef QT_QWS_TP_MOVE_LIMIT +#define QT_QWS_TP_MOVE_LIMIT 100 +#endif + +#ifndef QT_QWS_TP_JITTER_LIMIT +#define QT_QWS_TP_JITTER_LIMIT 2 +#endif + +class QWSLinuxTPMouseHandlerPrivate : public QObject +{ + Q_OBJECT +public: + QWSLinuxTPMouseHandlerPrivate(QWSLinuxTPMouseHandler *h, const QString &); + ~QWSLinuxTPMouseHandlerPrivate(); + + void suspend(); + void resume(); +private: + static const int mouseBufSize = 2048; + int mouseFD; + QPoint oldmouse; + QPoint oldTotalMousePos; + bool waspressed; + QPolygon samples; + int currSample; + int lastSample; + int numSamples; + int skipCount; + int mouseIdx; + uchar mouseBuf[mouseBufSize]; + QWSLinuxTPMouseHandler *handler; + QSocketNotifier *mouseNotifier; + +private slots: + void readMouseData(); +}; + +QWSLinuxTPMouseHandler::QWSLinuxTPMouseHandler(const QString &driver, const QString &device) + : QWSCalibratedMouseHandler(driver, device) +{ + d = new QWSLinuxTPMouseHandlerPrivate(this, device); +} + +QWSLinuxTPMouseHandler::~QWSLinuxTPMouseHandler() +{ + delete d; +} + +void QWSLinuxTPMouseHandler::suspend() +{ + d->suspend(); +} + +void QWSLinuxTPMouseHandler::resume() +{ + d->resume(); +} + +QWSLinuxTPMouseHandlerPrivate::QWSLinuxTPMouseHandlerPrivate(QWSLinuxTPMouseHandler *h, + const QString &device) + : samples(QT_QWS_TP_SAMPLE_SIZE), currSample(0), lastSample(0), + numSamples(0), skipCount(0), handler(h) +{ + QString mousedev; + if (device.isEmpty()) { +#if defined(QT_QWS_IPAQ) +# ifdef QT_QWS_IPAQ_RAW + mousedev = QLatin1String("/dev/h3600_tsraw"); +# else + mousedev = QLatin1String("/dev/h3600_ts"); +# endif +#else + mousedev = QLatin1String("/dev/ts"); +#endif + } else { + mousedev = device; + } + if ((mouseFD = open(mousedev.toLatin1().constData(), O_RDONLY | O_NDELAY)) < 0) { + qWarning("Cannot open %s (%s)", qPrintable(mousedev), strerror(errno)); + return; + } + + mouseNotifier = new QSocketNotifier(mouseFD, QSocketNotifier::Read, + this); + connect(mouseNotifier, SIGNAL(activated(int)),this, SLOT(readMouseData())); + waspressed=false; + mouseIdx = 0; +} + +QWSLinuxTPMouseHandlerPrivate::~QWSLinuxTPMouseHandlerPrivate() +{ + if (mouseFD >= 0) + close(mouseFD); +} + +void QWSLinuxTPMouseHandlerPrivate::suspend() +{ + if (mouseNotifier) + mouseNotifier->setEnabled(false); +} + +void QWSLinuxTPMouseHandlerPrivate::resume() +{ + mouseIdx=0; + currSample=0; + lastSample=0; + numSamples=0; + skipCount=0; + if (mouseNotifier) + mouseNotifier->setEnabled(true); +} + + +void QWSLinuxTPMouseHandlerPrivate::readMouseData() +{ + if(!qt_screen) + return; + + int n; + do { + n = read(mouseFD, mouseBuf+mouseIdx, mouseBufSize-mouseIdx); + if (n > 0) + mouseIdx += n; + } while (n > 0 && mouseIdx < mouseBufSize); + + //qDebug("readMouseData()"); + + TS_EVENT *data; + int idx = 0; + + // perhaps we shouldn't be reading EVERY SAMPLE. + while (mouseIdx-idx >= (int)sizeof(TS_EVENT)) { + uchar *mb = mouseBuf+idx; + data = (TS_EVENT *) mb; + + if(data->pressure >= QT_QWS_TP_PRESSURE_THRESHOLD) { +#ifdef QT_QWS_SHARP + samples[currSample] = QPoint(1000 - data->x, data->y); +#else + samples[currSample] = QPoint(data->x, data->y); +#endif + numSamples++; + if (numSamples >= QT_QWS_TP_MINIMUM_SAMPLES) { + int sampleCount = qMin(numSamples + 1,samples.count()); + + // average the rest + QPoint mousePos = QPoint(0, 0); + QPoint totalMousePos = oldTotalMousePos; + totalMousePos += samples[currSample]; + if(numSamples >= samples.count()) + totalMousePos -= samples[lastSample]; + + mousePos = totalMousePos / (sampleCount - 1); +#if defined(QT_QWS_SCREEN_COORDINATES) + mousePos = handler->transform(mousePos); +#endif + if(!waspressed) + oldmouse = mousePos; + QPoint dp = mousePos - oldmouse; + int dxSqr = dp.x() * dp.x(); + int dySqr = dp.y() * dp.y(); + if (dxSqr + dySqr < (QT_QWS_TP_MOVE_LIMIT * QT_QWS_TP_MOVE_LIMIT)) { + if (waspressed) { + if ((dxSqr + dySqr > (QT_QWS_TP_JITTER_LIMIT * QT_QWS_TP_JITTER_LIMIT)) || skipCount > 2) { + handler->mouseChanged(mousePos,Qt::LeftButton); + oldmouse = mousePos; + skipCount = 0; + } else { + skipCount++; + } + } else { + handler->mouseChanged(mousePos,Qt::LeftButton); + oldmouse=mousePos; + waspressed=true; + } + + // save recuring information + currSample++; + if (numSamples >= samples.count()) + lastSample++; + oldTotalMousePos = totalMousePos; + } else { + numSamples--; // don't use this sample, it was bad. + } + } else { + // build up the average + oldTotalMousePos += samples[currSample]; + currSample++; + } + if (currSample >= samples.count()) + currSample = 0; + if (lastSample >= samples.count()) + lastSample = 0; + } else { + currSample = 0; + lastSample = 0; + numSamples = 0; + skipCount = 0; + oldTotalMousePos = QPoint(0,0); + if (waspressed) { + handler->mouseChanged(oldmouse,0); + oldmouse = QPoint(-100, -100); + waspressed=false; + } + } + idx += sizeof(TS_EVENT); + } + + int surplus = mouseIdx - idx; + for (int i = 0; i < surplus; i++) + mouseBuf[i] = mouseBuf[idx+i]; + mouseIdx = surplus; +} + +QT_END_NAMESPACE + +#include "qmouselinuxtp_qws.moc" + +#endif //QT_NO_QWS_MOUSE_LINUXTP diff --git a/src/gui/embedded/qmouselinuxtp_qws.h b/src/gui/embedded/qmouselinuxtp_qws.h new file mode 100644 index 0000000..2385455 --- /dev/null +++ b/src/gui/embedded/qmouselinuxtp_qws.h @@ -0,0 +1,77 @@ +/**************************************************************************** +** +** 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$ +** +****************************************************************************/ + +#ifndef QMOUSELINUXTP_QWS_H +#define QMOUSELINUXTP_QWS_H + +#include <QtGui/qmouse_qws.h> + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +QT_MODULE(Gui) + +#ifndef QT_NO_QWS_MOUSE_LINUXTP + +class QWSLinuxTPMouseHandlerPrivate; + +class QWSLinuxTPMouseHandler : public QWSCalibratedMouseHandler +{ + friend class QWSLinuxTPMouseHandlerPrivate; +public: + explicit QWSLinuxTPMouseHandler(const QString & = QString(), + const QString & = QString()); + ~QWSLinuxTPMouseHandler(); + + void suspend(); + void resume(); +protected: + QWSLinuxTPMouseHandlerPrivate *d; +}; + +#endif // QT_NO_QWS_MOUSE_LINUXTP + +QT_END_NAMESPACE + +QT_END_HEADER + +#endif // QMOUSELINUXTP_QWS_H diff --git a/src/gui/embedded/qmousepc_qws.cpp b/src/gui/embedded/qmousepc_qws.cpp new file mode 100644 index 0000000..a0cb032 --- /dev/null +++ b/src/gui/embedded/qmousepc_qws.cpp @@ -0,0 +1,793 @@ +/**************************************************************************** +** +** 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 "qmousepc_qws.h" + +#ifndef QT_NO_QWS_MOUSE_PC + +#include "qwindowsystem_qws.h" +#include "qsocketnotifier.h" +#include "qwsevent_qws.h" +#include "qwscommand_qws_p.h" +#include "qwsutils_qws.h" + +#include "qapplication.h" +#include "qpolygon.h" +#include "qtimer.h" +#include "qfile.h" +#include "qtextstream.h" +#include "qstringlist.h" + +#include <unistd.h> +#include <stdlib.h> +#include <stdio.h> +#include <sys/ioctl.h> +#include <sys/types.h> +#include <sys/stat.h> +#include <fcntl.h> +#include <errno.h> +#include <termios.h> + +#include <qscreen_qws.h> + +QT_BEGIN_NAMESPACE + +//#define QWS_MOUSE_DEBUG + +/* + * Automatic-detection mouse driver + */ + +class QWSPcMouseSubHandler { +protected: + enum { max_buf=32 }; + + int fd; + + uchar buffer[max_buf]; + int nbuf; + + QPoint motion; + int bstate; + int wheel; + + int goodness; + int badness; + + virtual int tryData()=0; + +public: + QWSPcMouseSubHandler(int f) : fd(f) + { + initState(); + } + virtual ~QWSPcMouseSubHandler() {} + + int file() const { return fd; } + + void closeIfNot(int& f) + { + if (fd != f) { + f = fd; + close(fd); + } + } + + void initState() { nbuf = bstate = goodness = badness = 0; } + + void worse(int by=1) { badness+=by; } + bool reliable() const { return goodness >= 5 && badness < 50; } + int buttonState() const { return bstate; } + bool motionPending() const { return motion!=QPoint(0,0); } + QPoint takeMotion() { QPoint r=motion; motion=QPoint(0,0); return r; } + int takeWheel() { int result = wheel; wheel = 0; return result; } + + void appendData(uchar* data, int length) + { + memcpy(buffer+nbuf, data, length); + nbuf += length; + } + + enum UsageResult { Insufficient, Motion, Button }; + + UsageResult useData() + { + int pbstate = bstate; + int n = tryData(); +#ifdef QWS_MOUSE_DEBUG + if (n) { + fprintf(stderr, "QWSPcMouseSubHandler tryData read %d bytes:", n); + for (int i=0; i<n; ++i) + fprintf(stderr, " %02x", buffer[i]); + fprintf(stderr, "\n"); + } +#endif + if (n > 0) { + if (n<nbuf) + memmove(buffer, buffer+n, nbuf-n); + nbuf -= n; + return (wheel || pbstate != bstate) ? Button : Motion; + } + return Insufficient; + } +}; + +class QWSPcMouseSubHandler_intellimouse : public QWSPcMouseSubHandler { + int packetsize; +public: + QWSPcMouseSubHandler_intellimouse(int f) : QWSPcMouseSubHandler(f) + { + init(); + } + + void init() + { + int n; + uchar reply[20]; + + if (tcflush(fd,TCIOFLUSH) == -1) { +#ifdef QWS_MOUSE_DEBUG + perror("QWSPcMouseSubHandler_intellimouse: pre-init tcflush"); +#endif + } + static const uchar initseq[] = { 243, 200, 243, 100, 243, 80 }; + static const uchar query[] = { 0xf2 }; + if (write(fd, initseq, sizeof(initseq))!=sizeof(initseq)) { + badness = 100; + return; + } + usleep(10000); + if (tcflush(fd,TCIOFLUSH) == -1) { +#ifdef QWS_MOUSE_DEBUG + perror("QWSPcMouseSubHandler_intellimouse: post-init tcflush"); +#endif + } + if (write(fd, query, sizeof(query))!=sizeof(query)) { + badness = 100; + return; + } + usleep(10000); + n = read(fd, reply, 20); + if (n > 0) { + goodness = 10; + switch (reply[n-1]) { + case 3: + case 4: + packetsize = 4; + break; + default: + packetsize = 3; + } + } else { + badness = 100; + } + } + + int tryData() + { + if (nbuf >= packetsize) { + //int overflow = (buffer[0]>>6)& 0x03; + + if (/*overflow ||*/ !(buffer[0] & 8)) { +#ifdef QWS_MOUSE_DEBUG + qDebug("Intellimouse: skipping (overflow)"); +#endif + badness++; + return 1; + } else { + QPoint delta((buffer[0] & 0x10) ? buffer[1]-256 : buffer[1], + (buffer[0] & 0x20) ? 256-buffer[2] : -buffer[2]); + motion += delta; + int nbstate = buffer[0] & 0x7; +#ifdef QWS_MOUSE_DEBUG + int debugwheel = +#endif + wheel = packetsize > 3 ? -(signed char)buffer[3] : 0; + if (wheel < -2 || wheel > 2) + wheel = 0; + wheel *= 120; // WHEEL_DELTA? +#ifdef QWS_MOUSE_DEBUG + qDebug("Intellimouse: motion %d,%d, state %d, raw wheel %d, wheel %d", motion.x(), motion.y(), nbstate, debugwheel, wheel); +#endif + if (motion.x() || motion.y() || bstate != nbstate || wheel) { + bstate = nbstate; + goodness++; + } else { + badness++; + return 1; + } + } + return packetsize; + } + return 0; + } +}; + +class QWSPcMouseSubHandler_mouseman : public QWSPcMouseSubHandler { + int packetsize; +public: + QWSPcMouseSubHandler_mouseman(int f) : QWSPcMouseSubHandler(f) + { + init(); + } + + void init() + { + if (tcflush(fd,TCIOFLUSH) == -1) { +#ifdef QWS_MOUSE_DEBUG + perror("QWSPcMouseSubHandler_mouseman: initial tcflush"); +#endif + } + write(fd,"",1); + usleep(50000); + write(fd,"@EeI!",5); + usleep(10000); + static const char ibuf[] = { 246, 244 }; + write(fd,ibuf,1); + write(fd,ibuf+1,1); + if (tcflush(fd,TCIOFLUSH) == -1) { +#ifdef QWS_MOUSE_DEBUG + perror("QWSPcMouseSubHandler_mouseman: tcflush"); +#endif + } + usleep(10000); + + char buf[100]; + while (read(fd, buf, 100) > 0) { } // eat unwanted replies + } + + int tryData() + { + if (nbuf >= 3) { + int nbstate = 0; + if (buffer[0] & 0x01) + nbstate |= Qt::LeftButton; + if (buffer[0] & 0x02) + nbstate |= Qt::RightButton; + if (buffer[0] & 0x04) + nbstate |= Qt::MidButton; + + int overflow = (buffer[0]>>6)& 0x03; + if (overflow) { + //### wheel events signalled with overflow bit, ignore for now + badness++; + return 1; + } else { + bool xs = buffer[0] & 0x10; + bool ys = buffer[0] & 0x20; + int dx = xs ? buffer[1]-256 : buffer[1]; + int dy = ys ? buffer[2]-256 : buffer[2]; + + motion += QPoint(dx, -dy); + if (motion.x() || motion.y() || bstate != nbstate) { + bstate = nbstate; + goodness++; + } else { + badness++; + return 1; + } + } + return 3; + } + return 0; + } +}; + +class QWSPcMouseSubHandler_serial : public QWSPcMouseSubHandler { +public: + QWSPcMouseSubHandler_serial(int f) : QWSPcMouseSubHandler(f) + { + initSerial(); + } + +protected: + void setflags(int f) + { + termios tty; + if (tcgetattr(fd, &tty) == -1) { +#ifdef QWS_MOUSE_DEBUG + perror("QWSPcMouseSubHandler_serial: tcgetattr"); +#endif + } + tty.c_iflag = IGNBRK | IGNPAR; + tty.c_oflag = 0; + tty.c_lflag = 0; + tty.c_cflag = f | CREAD | CLOCAL | HUPCL; +#if !defined(Q_OS_DARWIN) && !defined(Q_OS_SOLARIS) && !defined(Q_OS_INTEGRITY) + tty.c_line = 0; +#endif + tty.c_cc[VTIME] = 0; + tty.c_cc[VMIN] = 1; + if (tcsetattr(fd, TCSANOW, &tty) == -1) { +#ifdef QWS_MOUSE_DEBUG + perror("QWSPcMouseSubHandler_serial: tcgetattr"); +#endif + } + } + +private: + void initSerial() + { + int speed[4] = { B9600, B4800, B2400, B1200 }; + + for (int n = 0; n < 4; n++) { + setflags(CSTOPB | speed[n]); + write(fd, "*q", 2); + usleep(10000); + } + } +}; + +class QWSPcMouseSubHandler_mousesystems : public QWSPcMouseSubHandler_serial { +public: + // ##### This driver has not been tested + + QWSPcMouseSubHandler_mousesystems(int f) : QWSPcMouseSubHandler_serial(f) + { + init(); + } + + void init() + { + setflags(B1200|CS8|CSTOPB); + // 60Hz + if (write(fd, "R", 1)!=1) { + badness = 100; + return; + } + if (tcflush(fd,TCIOFLUSH) == -1) { +#ifdef QT_QWS_VNC_DEBUG + perror("QWSPcMouseSubHandler_mousesystems: tcflush"); +#endif + } + } + + int tryData() + { + if (nbuf >= 5) { + if ((buffer[0] & 0xf8) != 0x80) { + badness++; + return 1; + } + motion += + QPoint((signed char)buffer[1] + (signed char)buffer[3], + -(signed char)buffer[2] + (signed char)buffer[4]); + int t = ~buffer[0]; + int nbstate = ((t&3) << 1) | ((t&4) >> 2); + if (motion.x() || motion.y() || bstate != nbstate) { + bstate = nbstate; + goodness++; + } else { + badness++; + return 1; + } + return 5; + } + return 0; + } +}; + +class QWSPcMouseSubHandler_ms : public QWSPcMouseSubHandler_serial { + int mman; +public: + QWSPcMouseSubHandler_ms(int f) : QWSPcMouseSubHandler_serial(f) + { + mman=0; + init(); + } + + void init() + { + setflags(B1200|CS7); + // 60Hz + if (write(fd, "R", 1)!=1) { + badness = 100; + return; + } + if (tcflush(fd,TCIOFLUSH) == -1) { +#ifdef QWS_MOUSE_DEBUG + perror("QWSPcMouseSubHandler_ms: tcflush"); +#endif + } + } + + int tryData() + { + if (!(buffer[0] & 0x40)) { + if (buffer[0] == 0x20 && (bstate & Qt::MidButton)) { + mman=1; // mouseman extension + } + return 1; + } + int extra = mman&&(bstate & Qt::MidButton); + if (nbuf >= 3+extra) { + int nbstate = 0; + if (buffer[0] == 0x40 && !bstate && !buffer[1] && !buffer[2]) { + nbstate = Qt::MidButton; + } else { + nbstate = ((buffer[0] & 0x20) >> 5) + | ((buffer[0] & 0x10) >> 3); + if (extra && buffer[3] == 0x20) + nbstate = Qt::MidButton; + } + + if (buffer[1] & 0x40) { + badness++; + return 1; + } else { + motion += + QPoint((signed char)((buffer[0]&0x3)<<6) + |(signed char)(buffer[1]&0x3f), + (signed char)((buffer[0]&0xc)<<4) + |(signed char)(buffer[2]&0x3f)); + if (motion.x() || motion.y() || bstate != nbstate) { + bstate = nbstate; + goodness++; + } else { + badness++; + return 1; + } + return 3+extra; + } + } + return 0; + } +}; + +//=========================================================================== + +class QWSPcMouseHandlerPrivate : public QObject +{ + Q_OBJECT +public: + QWSPcMouseHandlerPrivate(QWSPcMouseHandler *h, const QString &, const QString &); + ~QWSPcMouseHandlerPrivate(); + + void suspend(); + void resume(); + +private: + enum { max_dev=32 }; + QWSPcMouseSubHandler *sub[max_dev]; + QList<QSocketNotifier*> notifiers; + int nsub; + int retries; + +private slots: + void readMouseData(int); + +private: + void openDevices(); + void closeDevices(); + void notify(int fd); + bool sendEvent(QWSPcMouseSubHandler& h); + +private: + QWSPcMouseHandler *handler; + QString driver; + QString device; + qreal accel; + int accel_limit; +}; + +QWSPcMouseHandler::QWSPcMouseHandler(const QString &driver, const QString &device) + : QWSMouseHandler(driver, device) +{ + d = new QWSPcMouseHandlerPrivate(this, driver, device); +} + +QWSPcMouseHandler::~QWSPcMouseHandler() +{ + delete d; +} + +void QWSPcMouseHandler::suspend() +{ + d->suspend(); +} + +void QWSPcMouseHandler::resume() +{ + d->resume(); +} + + +QWSPcMouseHandlerPrivate::QWSPcMouseHandlerPrivate(QWSPcMouseHandler *h, + const QString &drv, const QString &arg) + : handler(h), driver(drv) +{ + QStringList args = arg.split(QLatin1Char(':'), QString::SkipEmptyParts); + + int index; + + accel = qreal(2.0); + QRegExp accelRegex(QLatin1String("^accel=(\\d+\\.?\\d*)$")); + index = args.indexOf(accelRegex); + if (index >= 0) { + accel = qreal(accelRegex.cap(1).toDouble()); + args.removeAt(index); + } + + accel_limit = 5; + QRegExp accelLimitRegex(QLatin1String("^accel_limit=(\\d+)$")); + index = args.indexOf(accelLimitRegex); + if (index >= 0) { + accel_limit = accelLimitRegex.cap(1).toInt(); + args.removeAt(index); + } + + device = args.join(QString()); + + retries = 0; + openDevices(); +} + +QWSPcMouseHandlerPrivate::~QWSPcMouseHandlerPrivate() +{ + closeDevices(); +} + +/* +QWSPcMouseHandler::UsageResult QWSPcMouseHandler::useDev(Dev& d) +{ + if (d.nbuf >= mouseData[d.protocol].bytesPerPacket) { + uchar *mb = d.buf; + int bstate = 0; + int dx = 0; + int dy = 0; + + switch (mouseProtocol) { + case MouseMan: + case IntelliMouse: + { + bstate = mb[0] & 0x7; // assuming Qt::*Button order + + int overflow = (mb[0]>>6)& 0x03; + if (mouseProtocol == MouseMan && overflow) { + //### wheel events signalled with overflow bit, ignore for now + } + else { + bool xs = mb[0] & 0x10; + bool ys = mb[0] & 0x20; + dx = xs ? mb[1]-256 : mb[1]; + dy = ys ? mb[2]-256 : mb[2]; + } + break; + } + case Microsoft: + if (((mb[0] & 0x20) >> 3)) { + bstate |= Qt::LeftButton; + } + if (((mb[0] & 0x10) >> 4)) { + bstate |= Qt::RightButton; + } + + dx=(signed char)(((mb[0] & 0x03) << 6) | (mb[1] & 0x3f)); + dy=-(signed char)(((mb[0] & 0x0c) << 4) | (mb[2] & 0x3f)); + + break; + } + } + } +*/ + + +bool QWSPcMouseHandlerPrivate::sendEvent(QWSPcMouseSubHandler& h) +{ + if (h.reliable()) { + QPoint motion = h.takeMotion(); + if (qAbs(motion.x()) > accel_limit || qAbs(motion.y()) > accel_limit) + motion *= accel; + QPoint newPos = handler->pos() + motion; + if (qt_screen->isTransformed()) { + QSize s = QSize(qt_screen->width(), qt_screen->height()); + newPos = qt_screen->mapToDevice(newPos, s); + } + handler->limitToScreen(newPos); + + handler->mouseChanged(newPos, h.buttonState(), h.takeWheel()); + return true; + } else { + h.takeMotion(); + if (h.buttonState() & (Qt::RightButton|Qt::MidButton)) { + // Strange for the user to press right or middle without + // a moving mouse! + h.worse(); + } + return false; + } +} + +void QWSPcMouseHandlerPrivate::openDevices() +{ + nsub=0; + int fd = -1; + + QString drv = driver.toLower(); + if (!drv.isEmpty() && drv != QLatin1String("auto")) { + // Manually specified mouse + QByteArray dev = device.toLatin1(); + if (drv == QLatin1String("intellimouse")) { + if (dev.isEmpty()) + dev = "/dev/psaux"; + fd = open(dev, O_RDWR | O_NDELAY); + if (fd >= 0) + sub[nsub++] = new QWSPcMouseSubHandler_intellimouse(fd); + } else if (drv == QLatin1String("microsoft")) { + if (dev.isEmpty()) + dev = "/dev/ttyS0"; + fd = open(dev, O_RDWR | O_NDELAY); + if (fd >= 0) + sub[nsub++] = new QWSPcMouseSubHandler_ms(fd); + } else if (drv == QLatin1String("mousesystems")) { + if (dev.isEmpty()) + dev = "/dev/ttyS0"; + fd = open(dev, O_RDWR | O_NDELAY); + if (fd >= 0) + sub[nsub++] = new QWSPcMouseSubHandler_mousesystems(fd); + } else if (drv == QLatin1String("mouseman")) { + if (dev.isEmpty()) + dev = "/dev/psaux"; + fd = open(dev, O_RDWR | O_NDELAY); + if (fd >= 0) + sub[nsub++] = new QWSPcMouseSubHandler_mouseman(fd); + } + if (fd >= 0) + notify(fd); + else + qCritical("Error opening mouse device '%s': %s", + dev.constData(), strerror(errno)); + } else { + // Try automatically + fd = open("/dev/psaux", O_RDWR | O_NDELAY); + if (fd >= 0) { + sub[nsub++] = new QWSPcMouseSubHandler_intellimouse(fd); + notify(fd); + } + fd = open("/dev/input/mice", O_RDWR | O_NDELAY); + if (fd >= 0) { + sub[nsub++] = new QWSPcMouseSubHandler_intellimouse(fd); + notify(fd); + //qDebug("/dev/input/mice fd %d #%d", fd, nsub-1); + } + +// include the code below to auto-detect serial mice, and to mess up +// any sort of serial communication +#if 0 + const char fn[4][11] = { "/dev/ttyS0", "/dev/ttyS1", "/dev/ttyS2", "/dev/ttyS3" }; + for (int ch = 0; ch < 4; ++ch) { + fd = open(fn[ch], O_RDWR | O_NDELAY); + if (fd >= 0) { + //sub[nsub++] = new QWSPcMouseSubHandler_intellimouse(fd); + sub[nsub++] = new QWSPcMouseSubHandler_mousesystems(fd); + sub[nsub++] = new QWSPcMouseSubHandler_ms(fd); + notify(fd); + } + } +#endif + } +} + +void QWSPcMouseHandlerPrivate::closeDevices() +{ + int pfd=-1; + for (int i=0; i<nsub; i++) { + sub[i]->closeIfNot(pfd); + delete sub[i]; + } + qDeleteAll(notifiers); + notifiers.clear(); +} + +void QWSPcMouseHandlerPrivate::suspend() +{ + for (int i=0; i<notifiers.size(); ++i) + notifiers.at(i)->setEnabled(false); +} + +void QWSPcMouseHandlerPrivate::resume() +{ + for (int i=0; i<nsub; i++) + sub[i]->initState(); + + for (int i=0; i<notifiers.size(); ++i) + notifiers.at(i)->setEnabled(true); +} + + + +void QWSPcMouseHandlerPrivate::notify(int fd) +{ + QSocketNotifier *mouseNotifier + = new QSocketNotifier(fd, QSocketNotifier::Read, this); + connect(mouseNotifier, SIGNAL(activated(int)),this, SLOT(readMouseData(int))); + notifiers.append(mouseNotifier); +} + +void QWSPcMouseHandlerPrivate::readMouseData(int fd) +{ + for (;;) { + uchar buf[8]; + int n = read(fd, buf, 8); + if (n<=0) + break; + for (int i=0; i<nsub; i++) { + QWSPcMouseSubHandler& h = *sub[i]; + if (h.file() == fd) { + h.appendData(buf,n); + for (;;) { + switch (h.useData()) { + case QWSPcMouseSubHandler::Button: + sendEvent(h); + break; + case QWSPcMouseSubHandler::Insufficient: + goto breakbreak; + case QWSPcMouseSubHandler::Motion: + break; + } + } + breakbreak: + ; + } + } + } + bool any_reliable=false; + for (int i=0; i<nsub; i++) { + QWSPcMouseSubHandler& h = *sub[i]; + if (h.motionPending()) + sendEvent(h); + any_reliable = any_reliable || h.reliable(); + } + if (any_reliable) { + // ... get rid of all unreliable ones? All bad ones? + } else if (retries < 2) { + // Try again - maybe the mouse was being moved when we tried to init. + closeDevices(); + openDevices(); + retries++; + } +} + +QT_END_NAMESPACE + +#include "qmousepc_qws.moc" + +#endif // QT_NO_MOUSE_PC diff --git a/src/gui/embedded/qmousepc_qws.h b/src/gui/embedded/qmousepc_qws.h new file mode 100644 index 0000000..6a08878 --- /dev/null +++ b/src/gui/embedded/qmousepc_qws.h @@ -0,0 +1,76 @@ +/**************************************************************************** +** +** 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$ +** +****************************************************************************/ + +#ifndef QMOUSEPC_QWS_H +#define QMOUSEPC_QWS_H + +#include <QtGui/qmouse_qws.h> + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +QT_MODULE(Gui) + +#ifndef QT_NO_QWS_MOUSE_PC + +class QWSPcMouseHandlerPrivate; + +class QWSPcMouseHandler : public QWSMouseHandler +{ +public: + explicit QWSPcMouseHandler(const QString & = QString(), + const QString & = QString()); + ~QWSPcMouseHandler(); + + void suspend(); + void resume(); +protected: + QWSPcMouseHandlerPrivate *d; +}; + +#endif // QT_NO_QWS_MOUSE_PC + +QT_END_NAMESPACE + +QT_END_HEADER + +#endif // QMOUSEPC_QWS_H diff --git a/src/gui/embedded/qmousetslib_qws.cpp b/src/gui/embedded/qmousetslib_qws.cpp new file mode 100644 index 0000000..8edbc62 --- /dev/null +++ b/src/gui/embedded/qmousetslib_qws.cpp @@ -0,0 +1,371 @@ +/**************************************************************************** +** +** 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 "qmousetslib_qws.h" + +#if !defined(QT_NO_QWS_MOUSE_TSLIB) || defined(QT_PLUGIN) + +#include <QtCore/qregexp.h> +#include <QtCore/qstringlist.h> +#include "qsocketnotifier.h" +#include "qscreen_qws.h" + +#include <tslib.h> +#include <errno.h> + +QT_BEGIN_NAMESPACE + +#ifdef TSLIBMOUSEHANDLER_DEBUG +# include <QtCore/QDebug> +#endif + +/*! + \internal + + \class QWSTslibMouseHandler + \ingroup qws + + \brief The QWSTslibMouseHandler class implements a mouse driver + for the Universal Touch Screen Library, tslib. + + QWSTslibMouseHandler inherits the QWSCalibratedMouseHandler class, + providing calibration and noise reduction functionality in + addition to generating mouse events, for devices using the + Universal Touch Screen Library. + + To be able to compile this mouse handler, \l{Qt for Embedded Linux} + must be configured with the \c -qt-mouse-tslib option, see the + \l{Pointer Handling} documentation for details. In addition, the tslib + headers and library must be present in the build environment. The + tslib sources can be downloaded from \l + {http://tslib.berlios.de/}. Use the \c -L and \c -I options + with \c configure to explicitly specify the location of the + library and its headers: + + \snippet doc/src/snippets/code/src_gui_embedded_qmousetslib_qws.cpp 0 + + In order to use this mouse handler, tslib must also be correctly + installed on the target machine. This includes providing a \c + ts.conf configuration file and setting the necessary environment + variables, see the README file provided with tslib for details. + + The ts.conf file will usually contain the following two lines + + \snippet doc/src/snippets/code/src_gui_embedded_qmousetslib_qws.cpp 1 + + To make \l{Qt for Embedded Linux} explicitly choose the tslib mouse + handler, set the QWS_MOUSE_PROTO environment variable. + + \sa {Pointer Handling}, {Qt for Embedded Linux} +*/ + +class QWSTslibMouseHandlerPrivate : public QObject +{ + Q_OBJECT +public: + QWSTslibMouseHandlerPrivate(QWSTslibMouseHandler *h, + const QString &device); + ~QWSTslibMouseHandlerPrivate(); + + void suspend(); + void resume(); + + void calibrate(const QWSPointerCalibrationData *data); + void clearCalibration(); + +private: + QWSTslibMouseHandler *handler; + struct tsdev *dev; + QSocketNotifier *mouseNotifier; + int jitter_limit; + + struct ts_sample lastSample; + bool wasPressed; + int lastdx; + int lastdy; + + bool calibrated; + QString devName; + + bool open(); + void close(); + inline bool get_sample(struct ts_sample *sample); + +private slots: + void readMouseData(); +}; + +QWSTslibMouseHandlerPrivate::QWSTslibMouseHandlerPrivate(QWSTslibMouseHandler *h, + const QString &device) + : handler(h), dev(0), mouseNotifier(0), jitter_limit(3) +{ + QStringList args = device.split(QLatin1Char(':'), QString::SkipEmptyParts); + QRegExp jitterRegex(QLatin1String("^jitter_limit=(\\d+)$")); + int index = args.indexOf(jitterRegex); + if (index >= 0) { + jitter_limit = jitterRegex.cap(1).toInt(); + args.removeAt(index); + } + + devName = args.join(QString()); + + if (devName.isNull()) { + const char *str = getenv("TSLIB_TSDEVICE"); + if (str) + devName = QString::fromLocal8Bit(str); + } + + if (devName.isNull()) + devName = QLatin1String("/dev/ts"); + + if (!open()) + return; + + calibrated = true; + + int fd = ts_fd(dev); + mouseNotifier = new QSocketNotifier(fd, QSocketNotifier::Read, this); + connect(mouseNotifier, SIGNAL(activated(int)),this, SLOT(readMouseData())); + resume(); +} + +QWSTslibMouseHandlerPrivate::~QWSTslibMouseHandlerPrivate() +{ + close(); +} + +bool QWSTslibMouseHandlerPrivate::open() +{ + dev = ts_open(devName.toLocal8Bit().constData(), 1); + if (!dev) { + qCritical("QWSTslibMouseHandlerPrivate: ts_open() failed" + " with error: '%s'", strerror(errno)); + qCritical("Please check your tslib installation!"); + return false; + } + + if (ts_config(dev)) { + qCritical("QWSTslibMouseHandlerPrivate: ts_config() failed" + " with error: '%s'", strerror(errno)); + qCritical("Please check your tslib installation!"); + close(); + return false; + } + + return true; +} + +void QWSTslibMouseHandlerPrivate::close() +{ + if (dev) + ts_close(dev); +} + +void QWSTslibMouseHandlerPrivate::suspend() +{ + if (mouseNotifier) + mouseNotifier->setEnabled(false); +} + +void QWSTslibMouseHandlerPrivate::resume() +{ + memset(&lastSample, 0, sizeof(lastSample)); + wasPressed = false; + lastdx = 0; + lastdy = 0; + if (mouseNotifier) + mouseNotifier->setEnabled(true); +} + +bool QWSTslibMouseHandlerPrivate::get_sample(struct ts_sample *sample) +{ + if (!calibrated) + return (ts_read_raw(dev, sample, 1) == 1); + + return (ts_read(dev, sample, 1) == 1); +} + +void QWSTslibMouseHandlerPrivate::readMouseData() +{ + if (!qt_screen) + return; + + for(;;) { + struct ts_sample sample = lastSample; + bool pressed = wasPressed; + + // Fast return if there's no events. + if (!get_sample(&sample)) + return; + pressed = (sample.pressure > 0); + + // Only return last sample unless there's a press/release event. + while (pressed == wasPressed) { + if (!get_sample(&sample)) + break; + pressed = (sample.pressure > 0); + } + + // work around missing coordinates on mouse release in raw mode + if (!calibrated && !pressed && sample.x == 0 && sample.y == 0) { + sample.x = lastSample.x; + sample.y = lastSample.y; + } + + int dx = sample.x - lastSample.x; + int dy = sample.y - lastSample.y; + + // Remove small movements in oppsite direction + if (dx * lastdx < 0 && qAbs(dx) < jitter_limit) { + sample.x = lastSample.x; + dx = 0; + } + if (dy * lastdy < 0 && qAbs(dy) < jitter_limit) { + sample.y = lastSample.y; + dy = 0; + } + + if (wasPressed == pressed && dx == 0 && dy == 0) + return; + +#ifdef TSLIBMOUSEHANDLER_DEBUG + qDebug() << "last" << QPoint(lastSample.x, lastSample.y) + << "curr" << QPoint(sample.x, sample.y) + << "dx,dy" << QPoint(dx, dy) + << "ddx,ddy" << QPoint(dx*lastdx, dy*lastdy) + << "pressed" << wasPressed << pressed; +#endif + + lastSample = sample; + wasPressed = pressed; + if (dx != 0) + lastdx = dx; + if (dy != 0) + lastdy = dy; + + const QPoint p(sample.x, sample.y); + if (calibrated) { + // tslib should do all the translation and filtering, so we send a + // "raw" mouse event + handler->QWSMouseHandler::mouseChanged(p, pressed); + } else { + handler->sendFiltered(p, pressed); + } + } +} + +void QWSTslibMouseHandlerPrivate::clearCalibration() +{ + suspend(); + close(); + handler->QWSCalibratedMouseHandler::clearCalibration(); + calibrated = false; + open(); + resume(); +} + +void QWSTslibMouseHandlerPrivate::calibrate(const QWSPointerCalibrationData *data) +{ + suspend(); + close(); + // default implementation writes to /etc/pointercal + // using the same format as the tslib linear module. + handler->QWSCalibratedMouseHandler::calibrate(data); + calibrated = true; + open(); + resume(); +} + +/*! + \internal +*/ +QWSTslibMouseHandler::QWSTslibMouseHandler(const QString &driver, + const QString &device) + : QWSCalibratedMouseHandler(driver, device) +{ + d = new QWSTslibMouseHandlerPrivate(this, device); +} + +/*! + \internal +*/ +QWSTslibMouseHandler::~QWSTslibMouseHandler() +{ + delete d; +} + +/*! + \reimp +*/ +void QWSTslibMouseHandler::suspend() +{ + d->suspend(); +} + +/*! + \reimp +*/ +void QWSTslibMouseHandler::resume() +{ + d->resume(); +} + +/*! + \reimp +*/ +void QWSTslibMouseHandler::clearCalibration() +{ + d->clearCalibration(); +} + +/*! + \reimp +*/ +void QWSTslibMouseHandler::calibrate(const QWSPointerCalibrationData *data) +{ + d->calibrate(data); +} + +QT_END_NAMESPACE + +#include "qmousetslib_qws.moc" + +#endif //QT_NO_QWS_MOUSE_TSLIB diff --git a/src/gui/embedded/qmousetslib_qws.h b/src/gui/embedded/qmousetslib_qws.h new file mode 100644 index 0000000..07bacde --- /dev/null +++ b/src/gui/embedded/qmousetslib_qws.h @@ -0,0 +1,80 @@ +/**************************************************************************** +** +** 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$ +** +****************************************************************************/ + +#ifndef QMOUSETSLIB_QWS_H +#define QMOUSETSLIB_QWS_H + +#include <QtGui/qmouse_qws.h> + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +QT_MODULE(Gui) + +#if !defined(QT_NO_QWS_MOUSE_TSLIB) || defined(QT_PLUGIN) + +class QWSTslibMouseHandlerPrivate; + +class QWSTslibMouseHandler : public QWSCalibratedMouseHandler +{ +public: + explicit QWSTslibMouseHandler(const QString &driver = QString(), + const QString &device = QString()); + ~QWSTslibMouseHandler(); + + void suspend(); + void resume(); + + void calibrate(const QWSPointerCalibrationData *data); + void clearCalibration(); + +protected: + friend class QWSTslibMouseHandlerPrivate; + QWSTslibMouseHandlerPrivate *d; +}; + + +QT_END_NAMESPACE +#endif // QT_NO_QWS_MOUSE_TSLIB +QT_END_HEADER + +#endif // QMOUSETSLIB_QWS_H diff --git a/src/gui/embedded/qmousevfb_qws.cpp b/src/gui/embedded/qmousevfb_qws.cpp new file mode 100644 index 0000000..9d81201 --- /dev/null +++ b/src/gui/embedded/qmousevfb_qws.cpp @@ -0,0 +1,132 @@ +/**************************************************************************** +** +** 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$ +** +****************************************************************************/ + +#ifndef QT_NO_QWS_MOUSE_QVFB + +#include <stdlib.h> +#include <sys/types.h> +#include <unistd.h> +#include <fcntl.h> +#include <errno.h> +#include <stdio.h> + +#include <qvfbhdr.h> +#include <qmousevfb_qws.h> +#include <qwindowsystem_qws.h> +#include <qsocketnotifier.h> +#include <qapplication.h> +#include <qtimer.h> + +QT_BEGIN_NAMESPACE + +QVFbMouseHandler::QVFbMouseHandler(const QString &driver, const QString &device) + : QObject(), QWSMouseHandler(driver, device) +{ + QString mouseDev = device; + if (device.isEmpty()) + mouseDev = QLatin1String("/dev/vmouse"); + + mouseFD = open(mouseDev.toLatin1().constData(), O_RDWR | O_NDELAY); + if (mouseFD == -1) { + perror("QVFbMouseHandler::QVFbMouseHandler"); + qWarning("QVFbMouseHander: Unable to open device %s", + qPrintable(mouseDev)); + return; + } + + // Clear pending input + char buf[2]; + while (read(mouseFD, buf, 1) > 0) { } + + mouseIdx = 0; + + mouseNotifier = new QSocketNotifier(mouseFD, QSocketNotifier::Read, this); + connect(mouseNotifier, SIGNAL(activated(int)),this, SLOT(readMouseData())); +} + +QVFbMouseHandler::~QVFbMouseHandler() +{ + if (mouseFD >= 0) + close(mouseFD); +} + +void QVFbMouseHandler::resume() +{ + mouseNotifier->setEnabled(true); +} + +void QVFbMouseHandler::suspend() +{ + mouseNotifier->setEnabled(false); +} + +void QVFbMouseHandler::readMouseData() +{ + int n; + do { + n = read(mouseFD, mouseBuf+mouseIdx, mouseBufSize-mouseIdx); + if (n > 0) + mouseIdx += n; + } while (n > 0); + + int idx = 0; + static const int packetsize = sizeof(QPoint) + 2*sizeof(int); + while (mouseIdx-idx >= packetsize) { + uchar *mb = mouseBuf+idx; + QPoint mousePos = *reinterpret_cast<QPoint *>(mb); + mb += sizeof(QPoint); + int bstate = *reinterpret_cast<int *>(mb); + mb += sizeof(int); + int wheel = *reinterpret_cast<int *>(mb); +// limitToScreen(mousePos); + mouseChanged(mousePos, bstate, wheel); + idx += packetsize; + } + + int surplus = mouseIdx - idx; + for (int i = 0; i < surplus; i++) + mouseBuf[i] = mouseBuf[idx+i]; + mouseIdx = surplus; +} + +QT_END_NAMESPACE + +#endif // QT_NO_QWS_MOUSE_QVFB diff --git a/src/gui/embedded/qmousevfb_qws.h b/src/gui/embedded/qmousevfb_qws.h new file mode 100644 index 0000000..fbf1efe --- /dev/null +++ b/src/gui/embedded/qmousevfb_qws.h @@ -0,0 +1,83 @@ +/**************************************************************************** +** +** 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$ +** +****************************************************************************/ + +#ifndef QMOUSEVFB_QWS_H +#define QMOUSEVFB_QWS_H + +#include <QtGui/qmouse_qws.h> + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +QT_MODULE(Gui) + +#ifndef QT_NO_QWS_MOUSE_QVFB + +class QSocketNotifier; + +class QVFbMouseHandler : public QObject, public QWSMouseHandler { + Q_OBJECT +public: + QVFbMouseHandler(const QString &driver = QString(), + const QString &device = QString()); + ~QVFbMouseHandler(); + + void resume(); + void suspend(); + +private: + int mouseFD; + int mouseIdx; + enum {mouseBufSize = 128}; + uchar mouseBuf[mouseBufSize]; + QSocketNotifier *mouseNotifier; + +private Q_SLOTS: + void readMouseData(); +}; +#endif // QT_NO_QWS_MOUSE_QVFB + +QT_END_NAMESPACE + +QT_END_HEADER + +#endif // QMOUSEVFB_QWS_H diff --git a/src/gui/embedded/qmousevr41xx_qws.cpp b/src/gui/embedded/qmousevr41xx_qws.cpp new file mode 100644 index 0000000..b4828bf --- /dev/null +++ b/src/gui/embedded/qmousevr41xx_qws.cpp @@ -0,0 +1,250 @@ +/**************************************************************************** +** +** 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 "qmousevr41xx_qws.h" + +#ifndef QT_NO_QWS_MOUSE_VR41XX +#include "qwindowsystem_qws.h" +#include "qsocketnotifier.h" +#include "qtimer.h" +#include "qapplication.h" +#include "qscreen_qws.h" +#include <qstringlist.h> +#include <qvarlengtharray.h> + +#include <unistd.h> +#include <stdlib.h> +#include <stdio.h> +#include <sys/ioctl.h> +#include <sys/types.h> +#include <sys/stat.h> +#include <fcntl.h> +#include <errno.h> +#include <termios.h> + +QT_BEGIN_NAMESPACE + +static const int defaultFilterSize = 3; + +class QWSVr41xxMouseHandlerPrivate : public QObject +{ + Q_OBJECT +public: + QWSVr41xxMouseHandlerPrivate(QWSVr41xxMouseHandler *, const QString &, const QString &); + ~QWSVr41xxMouseHandlerPrivate(); + + void resume(); + void suspend(); + +private slots: + void sendRelease(); + void readMouseData(); + +private: + bool getSample(); + ushort currSample[6]; + uint currLength; + + int mouseFD; + int mouseIdx; + QTimer *rtimer; + QSocketNotifier *mouseNotifier; + QWSVr41xxMouseHandler *handler; + QPoint lastPos; + bool isPressed; + int filterSize; + int pressLimit; +}; + +QWSVr41xxMouseHandler::QWSVr41xxMouseHandler(const QString &drv, const QString &dev) + : QWSCalibratedMouseHandler(drv, dev) +{ + d = new QWSVr41xxMouseHandlerPrivate(this, drv, dev); +} + +QWSVr41xxMouseHandler::~QWSVr41xxMouseHandler() +{ + delete d; +} + +void QWSVr41xxMouseHandler::resume() +{ + d->resume(); +} + +void QWSVr41xxMouseHandler::suspend() +{ + d->suspend(); +} + +QWSVr41xxMouseHandlerPrivate::QWSVr41xxMouseHandlerPrivate(QWSVr41xxMouseHandler *h, const QString &, const QString &device) + : currLength(0), handler(h) +{ + QStringList options = device.split(QLatin1String(":")); + int index = -1; + + filterSize = defaultFilterSize; + QRegExp filterRegExp(QLatin1String("filter=(\\d+)")); + index = options.indexOf(filterRegExp); + if (index != -1) { + filterSize = qMax(1, filterRegExp.cap(1).toInt()); + options.removeAt(index); + } + handler->setFilterSize(filterSize); + + pressLimit = 750; + QRegExp pressRegExp(QLatin1String("press=(\\d+)")); + index = options.indexOf(pressRegExp); + if (index != -1) { + pressLimit = filterRegExp.cap(1).toInt(); + options.removeAt(index); + } + + QString dev; + if (options.isEmpty()) + dev = QLatin1String("/dev/vrtpanel"); + else + dev = options.first(); + + if ((mouseFD = open(dev.toLocal8Bit().constData(), O_RDONLY)) < 0) { + qWarning("Cannot open %s (%s)", qPrintable(dev), strerror(errno)); + return; + } + sleep(1); + + if (fcntl(mouseFD, F_SETFL, O_NONBLOCK) < 0) { + qWarning("Error initializing touch panel."); + return; + } + + mouseNotifier = new QSocketNotifier(mouseFD, QSocketNotifier::Read, this); + connect(mouseNotifier, SIGNAL(activated(int)),this, SLOT(readMouseData())); + + rtimer = new QTimer(this); + rtimer->setSingleShot(true); + connect(rtimer, SIGNAL(timeout()), this, SLOT(sendRelease())); + mouseIdx = 0; +} + +QWSVr41xxMouseHandlerPrivate::~QWSVr41xxMouseHandlerPrivate() +{ + if (mouseFD >= 0) + close(mouseFD); +} + +void QWSVr41xxMouseHandlerPrivate::suspend() +{ + mouseNotifier->setEnabled(false); +} + + +void QWSVr41xxMouseHandlerPrivate::resume() +{ + mouseIdx = 0; + mouseNotifier->setEnabled(true); +} + +void QWSVr41xxMouseHandlerPrivate::sendRelease() +{ + handler->sendFiltered(lastPos, Qt::NoButton); + isPressed = false; +} + +bool QWSVr41xxMouseHandlerPrivate::getSample() +{ + const int n = read(mouseFD, + reinterpret_cast<uchar*>(currSample) + currLength, + sizeof(currSample) - currLength); + + if (n > 0) + currLength += n; + + if (currLength < sizeof(currSample)) + return false; + + currLength = 0; + return true; +} + +void QWSVr41xxMouseHandlerPrivate::readMouseData() +{ + const int sampleLength = sizeof(currSample) / sizeof(ushort); + QVarLengthArray<ushort, sampleLength * defaultFilterSize> samples(sampleLength * filterSize); + + // Only return last 'filterSize' samples + int head = 0; + int tail = 0; + int nSamples = 0; + while (getSample()) { + if (!(currSample[0] & 0x8000) || (currSample[5] < pressLimit)) + continue; + + ushort *data = samples.data() + head * sampleLength; + memcpy(data, currSample, sizeof(currSample)); + ++nSamples; + head = (head + 1) % filterSize; + if (nSamples >= filterSize) + tail = (tail + 1) % filterSize; + } + + if (nSamples == 0) + return; + + // send mouse events + while (tail != head || filterSize == 1) { + const ushort *data = samples.data() + tail * sampleLength; + lastPos = QPoint(data[3] - data[4], data[2] - data[1]); + handler->sendFiltered(lastPos, Qt::LeftButton); + isPressed = true; + tail = (tail + 1) % filterSize; + if (filterSize == 1) + break; + } + + if (isPressed) + rtimer->start(50); // release unreliable +} + +QT_END_NAMESPACE + +#include "qmousevr41xx_qws.moc" + +#endif //QT_NO_QWS_MOUSE_VR41 diff --git a/src/gui/embedded/qmousevr41xx_qws.h b/src/gui/embedded/qmousevr41xx_qws.h new file mode 100644 index 0000000..ad21013 --- /dev/null +++ b/src/gui/embedded/qmousevr41xx_qws.h @@ -0,0 +1,80 @@ +/**************************************************************************** +** +** 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$ +** +****************************************************************************/ + +#ifndef QMOUSEVR41XX_QWS_H +#define QMOUSEVR41XX_QWS_H + +#include <QtGui/qmouse_qws.h> + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +QT_MODULE(Gui) + +#ifndef QT_NO_QWS_MOUSE_VR41XX + +class QWSVr41xxMouseHandlerPrivate; + +class QWSVr41xxMouseHandler : public QWSCalibratedMouseHandler +{ +public: + explicit QWSVr41xxMouseHandler(const QString & = QString(), + const QString & = QString()); + ~QWSVr41xxMouseHandler(); + + void resume(); + void suspend(); + +protected: + QWSVr41xxMouseHandlerPrivate *d; + +private: + friend class QWSVr41xxMouseHandlerPrivate; +}; + +#endif // QT_NO_QWS_MOUSE_VR41XX + +QT_END_NAMESPACE + +QT_END_HEADER + +#endif // QMOUSEVR41XX_QWS_H diff --git a/src/gui/embedded/qmouseyopy_qws.cpp b/src/gui/embedded/qmouseyopy_qws.cpp new file mode 100644 index 0000000..fcf5193 --- /dev/null +++ b/src/gui/embedded/qmouseyopy_qws.cpp @@ -0,0 +1,184 @@ +/**************************************************************************** +** +** 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 "qmouseyopy_qws.h" + +#ifndef QT_NO_QWS_MOUSE_YOPY +#include "qwindowsystem_qws.h" +#include "qsocketnotifier.h" +#include "qapplication.h" +#include "qscreen_qws.h" + +#include <unistd.h> +#include <stdlib.h> +#include <stdio.h> +#include <sys/ioctl.h> +#include <sys/types.h> +#include <sys/stat.h> +#include <fcntl.h> +#include <errno.h> +#include <termios.h> + +QT_BEGIN_NAMESPACE + +class QWSYopyMouseHandlerPrivate : public QObject +{ + Q_OBJECT +public: + QWSYopyMouseHandlerPrivate(QWSYopyMouseHandler *h); + ~QWSYopyMouseHandlerPrivate(); + + void suspend(); + void resume(); + +private slots: + void readMouseData(); + +private: + int mouseFD; + int prevstate; + QSocketNotifier *mouseNotifier; + QWSYopyMouseHandler *handler; +}; + +QWSYopyMouseHandler::QWSYopyMouseHandler(const QString &driver, const QString &device) + : QWSMouseHandler(driver, device) +{ + d = new QWSYopyMouseHandlerPrivate(this); +} + +QWSYopyMouseHandler::~QWSYopyMouseHandler() +{ + delete d; +} + +void QWSYopyMouseHandler::resume() +{ + d->resume(); +} + +void QWSYopyMouseHandler::suspend() +{ + d->suspend(); +} + +QWSYopyMouseHandlerPrivate::QWSYopyMouseHandlerPrivate(QWSYopyMouseHandler *h) + : handler(h) +{ + if ((mouseFD = open("/dev/ts", O_RDONLY)) < 0) { + qWarning("Cannot open /dev/ts (%s)", strerror(errno)); + return; + } else { + sleep(1); + } + prevstate=0; + mouseNotifier = new QSocketNotifier(mouseFD, QSocketNotifier::Read, + this); + connect(mouseNotifier, SIGNAL(activated(int)),this, SLOT(readMouseData())); +} + +QWSYopyMouseHandlerPrivate::~QWSYopyMouseHandlerPrivate() +{ + if (mouseFD >= 0) + close(mouseFD); +} + +#define YOPY_XPOS(d) (d[1]&0x3FF) +#define YOPY_YPOS(d) (d[2]&0x3FF) +#define YOPY_PRES(d) (d[0]&0xFF) +#define YOPY_STAT(d) (d[3]&0x01) + +struct YopyTPdata { + + unsigned char status; + unsigned short xpos; + unsigned short ypos; + +}; + +void QWSYopyMouseHandlerPrivate::suspend() +{ + mouseNotifier->setEnabled(false); +} + + +void QWSYopyMouseHandlerPrivate::resume() +{ + prevstate = 0; + mouseNotifier->setEnabled(true); +} + +void QWSYopyMouseHandlerPrivate::readMouseData() +{ + if(!qt_screen) + return; + YopyTPdata data; + + unsigned int yopDat[4]; + + int ret; + + ret=read(mouseFD,&yopDat,sizeof(yopDat)); + + if(ret) { + data.status= (YOPY_PRES(yopDat)) ? 1 : 0; + data.xpos=YOPY_XPOS(yopDat); + data.ypos=YOPY_YPOS(yopDat); + QPoint q; + q.setX(data.xpos); + q.setY(data.ypos); + if (data.status && !prevstate) { + handler->mouseChanged(q,Qt::LeftButton); + } else if(!data.status && prevstate) { + handler->mouseChanged(q,0); + } + prevstate = data.status; + } + if(ret<0) { + qDebug("Error %s",strerror(errno)); + } +} + +QT_END_NAMESPACE + +#include "qmouseyopy_qws.moc" + +#endif //QT_NO_QWS_MOUSE_YOPY diff --git a/src/gui/embedded/qmouseyopy_qws.h b/src/gui/embedded/qmouseyopy_qws.h new file mode 100644 index 0000000..0209f10 --- /dev/null +++ b/src/gui/embedded/qmouseyopy_qws.h @@ -0,0 +1,80 @@ +/**************************************************************************** +** +** 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$ +** +****************************************************************************/ + +#ifndef QMOUSEYOPY_QWS_H +#define QMOUSEYOPY_QWS_H + +#include <QtGui/qmouse_qws.h> + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +QT_MODULE(Gui) + +#ifndef QT_NO_QWS_MOUSE_YOPY + +// YOPY touch panel support based on changes contributed by Ron Victorelli +// (victorrj at icubed.com) to Custom TP driver. + +class QWSYopyMouseHandlerPrivate; + +class QWSYopyMouseHandler : public QWSMouseHandler +{ +public: + explicit QWSYopyMouseHandler(const QString & = QString(), + const QString & = QString()); + ~QWSYopyMouseHandler(); + + void resume(); + void suspend(); + +protected: + QWSYopyMouseHandlerPrivate *d; +}; + +#endif // QT_NO_QWS_MOUSE_YOPY + +QT_END_NAMESPACE + +QT_END_HEADER + +#endif // QMOUSEYOPY_QWS_H diff --git a/src/gui/embedded/qscreen_qws.cpp b/src/gui/embedded/qscreen_qws.cpp new file mode 100644 index 0000000..6f32879 --- /dev/null +++ b/src/gui/embedded/qscreen_qws.cpp @@ -0,0 +1,3316 @@ +/**************************************************************************** +** +** 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 "qscreen_qws.h" + +#include "qcolormap.h" +#include "qscreendriverfactory_qws.h" +#include "qwindowsystem_qws.h" +#include "qwidget.h" +#include "qcolor.h" +#include "qpixmap.h" +#include "qvarlengtharray.h" +#include "qwsdisplay_qws.h" +#include <private/qdrawhelper_p.h> +#include <private/qpaintengine_raster_p.h> +#include <private/qpixmap_raster_p.h> +#include <private/qwindowsurface_qws_p.h> +#include <private/qpainter_p.h> +#include <private/qwidget_p.h> +#include <private/qgraphicssystem_qws_p.h> + +QT_BEGIN_NAMESPACE + +// #define QT_USE_MEMCPY_DUFF + +#ifndef QT_NO_QWS_CURSOR +bool qt_sw_cursor=false; +Q_GUI_EXPORT QScreenCursor * qt_screencursor = 0; +#endif +Q_GUI_EXPORT QScreen * qt_screen = 0; + +ClearCacheFunc QScreen::clearCacheFunc = 0; + +#ifndef QT_NO_QWS_CURSOR +/*! + \class QScreenCursor + \ingroup qws + + \brief The QScreenCursor class is a base class for screen cursors + in Qt for Embedded Linux. + + Note that this class is non-portable, and that it is only + available in \l{Qt for Embedded Linux}. + + QScreenCursor implements a software cursor, but can be subclassed + to support hardware cursors as well. When deriving from the + QScreenCursor class it is important to maintain the cursor's + image, position, hot spot (the point within the cursor's image + that will be the position of the associated mouse events) and + visibility as well as informing whether it is hardware accelerated + or not. + + Note that there may only be one screen cursor at a time. Use the + static instance() function to retrieve a pointer to the current + screen cursor. Typically, the cursor is constructed by the QScreen + class or one of its descendants when it is initializing the + device; the QScreenCursor class should never be instantiated + explicitly. + + Use the move() function to change the position of the cursor, and + the set() function to alter its image or its hot spot. In + addition, you can find out whether the cursor is accelerated or + not, using the isAccelerated() function, and the boundingRect() + function returns the cursor's bounding rectangle. + + The cursor's appearance can be controlled using the isVisible(), + hide() and show() functions; alternatively the QWSServer class + provides some means of controlling the cursor's appearance using + the QWSServer::isCursorVisible() and QWSServer::setCursorVisible() + functions. + + \sa QScreen, QWSServer +*/ + +/*! + \fn static QScreenCursor* QScreenCursor::instance() + \since 4.2 + + Returns a pointer to the application's unique screen cursor. +*/ + +extern bool qws_sw_cursor; + +/*! + Constructs a screen cursor +*/ +QScreenCursor::QScreenCursor() +{ + pos = QPoint(qt_screen->deviceWidth()/2, qt_screen->deviceHeight()/2); + size = QSize(0,0); + enable = true; + hwaccel = false; + supportsAlpha = true; +} + +/*! + Destroys the screen cursor. +*/ +QScreenCursor::~QScreenCursor() +{ +} + +/*! + Hides the cursor from the screen. + + \sa show() +*/ +void QScreenCursor::hide() +{ + if (enable) { + enable = false; + if (!hwaccel) + qt_screen->exposeRegion(boundingRect(), 0); + } +} + +/*! + Shows the mouse cursor. + + \sa hide() +*/ +void QScreenCursor::show() +{ + if (!enable) { + enable = true; + if (!hwaccel) + qt_screen->exposeRegion(boundingRect(), 0); + } +} + +/*! + Sets the cursor's image to be the given \a image. + + The \a hotx and \a hoty parameters define the cursor's hot spot, + i.e., the point within the cursor's image that will be the + position of the associated mouse events. + + \sa move() +*/ +void QScreenCursor::set(const QImage &image, int hotx, int hoty) +{ + const QRect r = boundingRect(); + + hotspot = QPoint(hotx, hoty); + cursor = image; + size = image.size(); + + if (enable && !hwaccel) + qt_screen->exposeRegion(r | boundingRect(), 0); +} + +/*! + Moves the mouse cursor to the given position, i.e., (\a x, \a y). + + Note that the given position defines the top-left corner of the + cursor's image, i.e., not the cursor's hot spot (the position of + the associated mouse events). + + \sa set() +*/ +void QScreenCursor::move(int x, int y) +{ + const QRegion r = boundingRect(); + pos = QPoint(x,y); + if (enable && !hwaccel) + qt_screen->exposeRegion(r | boundingRect(), 0); +} + + +/*! + \fn void QScreenCursor::initSoftwareCursor () + + Initializes the screen cursor. + + This function is typically called from the screen driver when + initializing the device. Alternatively, the cursor can be set + directly using the pointer returned by the static instance() + function. + + \sa QScreen::initDevice() +*/ +void QScreenCursor::initSoftwareCursor() +{ + qt_screencursor = new QScreenCursor; +} + + +#endif // QT_NO_QWS_CURSOR + + +/*! + \fn QRect QScreenCursor::boundingRect () const + + Returns the cursor's bounding rectangle. +*/ + +/*! + \internal + \fn bool QScreenCursor::enabled () +*/ + +/*! + \fn QImage QScreenCursor::image () const + + Returns the cursor's image. +*/ + + +/*! + \fn bool QScreenCursor::isAccelerated () const + + Returns true if the cursor is accelerated; otherwise false. +*/ + +/*! + \fn bool QScreenCursor::isVisible () const + + Returns true if the cursor is visible; otherwise false. +*/ + +/*! + \internal + \fn bool QScreenCursor::supportsAlphaCursor () const +*/ + +/* + \variable QScreenCursor::cursor + + \brief the cursor's image. + + \sa image() +*/ + +/* + \variable QScreenCursor::size + + \brief the cursor's size +*/ + +/* + \variable QScreenCursor::pos + + \brief the cursor's position, i.e., the position of the top-left + corner of the crsor's image + + \sa set(), move() +*/ + +/* + \variable QScreenCursor::hotspot + + \brief the cursor's hotspot, i.e., the point within the cursor's + image that will be the position of the associated mouse events. + + \sa set(), move() +*/ + +/* + \variable QScreenCursor::enable + + \brief whether the cursor is visible or not + + \sa isVisible() +*/ + +/* + \variable QScreenCursor::hwaccel + + \brief holds whether the cursor is accelerated or not + + If the cursor is not accelerated, its image will be included by + the screen when it composites the window surfaces. + + \sa isAccelerated() + +*/ + +/* + \variable QScreenCursor::supportsAlpha +*/ + +/*! + \internal + \macro qt_screencursor + \relates QScreenCursor + + A global pointer referring to the unique screen cursor. It is + equivalent to the pointer returned by the + QScreenCursor::instance() function. +*/ + + + +class QScreenPrivate +{ +public: + QScreenPrivate(QScreen *parent, QScreen::ClassId id = QScreen::CustomClass); + ~QScreenPrivate(); + + inline QImage::Format preferredImageFormat() const; + + typedef void (*SolidFillFunc)(QScreen*, const QColor&, const QRegion&); + typedef void (*BlitFunc)(QScreen*, const QImage&, const QPoint&, const QRegion&); + + SolidFillFunc solidFill; + BlitFunc blit; + + QPoint offset; + QList<QScreen*> subScreens; + QPixmapDataFactory* pixmapFactory; + QGraphicsSystem* graphicsSystem; + QWSGraphicsSystem defaultGraphicsSystem; //### + QImage::Format pixelFormat; +#if Q_BYTE_ORDER == Q_BIG_ENDIAN + bool fb_is_littleEndian; +#endif +#ifdef QT_QWS_CLIENTBLIT + bool supportsBlitInClients; +#endif + int classId; + QScreen *q_ptr; +}; + +template <typename T> +static void solidFill_template(QScreen *screen, const QColor &color, + const QRegion ®ion) +{ + T *dest = reinterpret_cast<T*>(screen->base()); + const T c = qt_colorConvert<T, quint32>(color.rgba(), 0); + const int stride = screen->linestep(); + const QVector<QRect> rects = region.rects(); + + for (int i = 0; i < rects.size(); ++i) { + const QRect r = rects.at(i); + qt_rectfill(dest, c, r.x(), r.y(), r.width(), r.height(), stride); + } +} + +#ifdef QT_QWS_DEPTH_GENERIC +static void solidFill_rgb_32bpp(QScreen *screen, const QColor &color, + const QRegion ®ion) +{ + quint32 *dest = reinterpret_cast<quint32*>(screen->base()); + const quint32 c = qt_convertToRgb<quint32>(color.rgba()); + + const int stride = screen->linestep(); + const QVector<QRect> rects = region.rects(); + + for (int i = 0; i < rects.size(); ++i) { + const QRect r = rects.at(i); + qt_rectfill(dest, c, r.x(), r.y(), r.width(), r.height(), stride); + } +} + +static void solidFill_rgb_16bpp(QScreen *screen, const QColor &color, + const QRegion ®ion) +{ + quint16 *dest = reinterpret_cast<quint16*>(screen->base()); + const quint16 c = qt_convertToRgb<quint32>(color.rgba()); + + const int stride = screen->linestep(); + const QVector<QRect> rects = region.rects(); + + for (int i = 0; i < rects.size(); ++i) { + const QRect r = rects.at(i); + qt_rectfill(dest, c, r.x(), r.y(), r.width(), r.height(), stride); + } +} +#endif // QT_QWS_DEPTH_GENERIC + +#ifdef QT_QWS_DEPTH_4 +static inline void qt_rectfill_gray4(quint8 *dest, quint8 value, + int x, int y, int width, int height, + int stride) +{ + const int pixelsPerByte = 2; + dest += y * stride + x / pixelsPerByte; + const int doAlign = x & 1; + const int doTail = (width - doAlign) & 1; + const int width8 = (width - doAlign) / pixelsPerByte; + + for (int j = 0; j < height; ++j) { + if (doAlign) + *dest = (*dest & 0xf0) | (value & 0x0f); + if (width8) + qt_memfill<quint8>(dest + doAlign, value, width8); + if (doTail) { + quint8 *d = dest + doAlign + width8; + *d = (*d & 0x0f) | (value & 0xf0); + } + dest += stride; + } +} + +static void solidFill_gray4(QScreen *screen, const QColor &color, + const QRegion ®ion) +{ + quint8 *dest = reinterpret_cast<quint8*>(screen->base()); + const quint8 c = qGray(color.rgba()) >> 4; + const quint8 c8 = (c << 4) | c; + + const int stride = screen->linestep(); + const QVector<QRect> rects = region.rects(); + + for (int i = 0; i < rects.size(); ++i) { + const QRect r = rects.at(i); + qt_rectfill_gray4(dest, c8, r.x(), r.y(), r.width(), r.height(), + stride); + } +} +#endif // QT_QWS_DEPTH_4 + +#ifdef QT_QWS_DEPTH_1 +static inline void qt_rectfill_mono(quint8 *dest, quint8 value, + int x, int y, int width, int height, + int stride) +{ + const int pixelsPerByte = 8; + const int alignWidth = qMin(width, (8 - (x & 7)) & 7); + const int doAlign = (alignWidth > 0 ? 1 : 0); + const int alignStart = pixelsPerByte - 1 - (x & 7); + const int alignStop = alignStart - (alignWidth - 1); + const quint8 alignMask = ((1 << alignWidth) - 1) << alignStop; + const int tailWidth = (width - alignWidth) & 7; + const int doTail = (tailWidth > 0 ? 1 : 0); + const quint8 tailMask = (1 << (pixelsPerByte - tailWidth)) - 1; + const int width8 = (width - alignWidth) / pixelsPerByte; + + dest += y * stride + x / pixelsPerByte; + stride -= (doAlign + width8); + + for (int j = 0; j < height; ++j) { + if (doAlign) { + *dest = (*dest & ~alignMask) | (value & alignMask); + ++dest; + } + if (width8) { + qt_memfill<quint8>(dest, value, width8); + dest += width8; + } + if (doTail) + *dest = (*dest & tailMask) | (value & ~tailMask); + dest += stride; + } +} + +static void solidFill_mono(QScreen *screen, const QColor &color, + const QRegion ®ion) +{ + quint8 *dest = reinterpret_cast<quint8*>(screen->base()); + const quint8 c8 = (qGray(color.rgba()) >> 7) * 0xff; + + const int stride = screen->linestep(); + const QVector<QRect> rects = region.rects(); + + for (int i = 0; i < rects.size(); ++i) { + const QRect r = rects.at(i); + qt_rectfill_mono(dest, c8, r.x(), r.y(), r.width(), r.height(), + stride); + } +} +#endif // QT_QWS_DEPTH_1 + +void qt_solidFill_setup(QScreen *screen, const QColor &color, + const QRegion ®ion) +{ + switch (screen->depth()) { +#ifdef QT_QWS_DEPTH_32 + case 32: + if (screen->pixelType() == QScreen::NormalPixel) + screen->d_ptr->solidFill = solidFill_template<quint32>; + else + screen->d_ptr->solidFill = solidFill_template<qabgr8888>; + break; +#endif +#ifdef QT_QWS_DEPTH_24 + case 24: + if (screen->pixelType() == QScreen::NormalPixel) + screen->d_ptr->solidFill = solidFill_template<qrgb888>; + else + screen->d_ptr->solidFill = solidFill_template<quint24>; + break; +#endif +#ifdef QT_QWS_DEPTH_18 + case 18: + screen->d_ptr->solidFill = solidFill_template<quint18>; + break; +#endif +#ifdef QT_QWS_DEPTH_16 + case 16: + if (screen->pixelType() == QScreen::NormalPixel) + screen->d_ptr->solidFill = solidFill_template<quint16>; + else + screen->d_ptr->solidFill = solidFill_template<qbgr565>; + break; +#endif +#ifdef QT_QWS_DEPTH_15 + case 15: + if (screen->pixelType() == QScreen::NormalPixel) + screen->d_ptr->solidFill = solidFill_template<qrgb555>; + else + screen->d_ptr->solidFill = solidFill_template<qbgr555>; + break; +#endif +#ifdef QT_QWS_DEPTH_12 + case 12: + screen->d_ptr->solidFill = solidFill_template<qrgb444>; + break; +#endif +#ifdef QT_QWS_DEPTH_8 + case 8: + screen->d_ptr->solidFill = solidFill_template<quint8>; + break; +#endif +#ifdef QT_QWS_DEPTH_4 + case 4: + screen->d_ptr->solidFill = solidFill_gray4; + break; +#endif +#ifdef QT_QWS_DEPTH_1 + case 1: + screen->d_ptr->solidFill = solidFill_mono; + break; +#endif + default: + qFatal("solidFill_setup(): Screen depth %d not supported!", + screen->depth()); + screen->d_ptr->solidFill = 0; + break; + } + screen->d_ptr->solidFill(screen, color, region); +} + +template <typename DST, typename SRC> +static void blit_template(QScreen *screen, const QImage &image, + const QPoint &topLeft, const QRegion ®ion) +{ + DST *dest = reinterpret_cast<DST*>(screen->base()); + const int screenStride = screen->linestep(); + const int imageStride = image.bytesPerLine(); + + if (region.numRects() == 1) { + const QRect r = region.boundingRect(); + const SRC *src = reinterpret_cast<const SRC*>(image.scanLine(r.y())) + + r.x(); + qt_rectconvert<DST, SRC>(dest, src, + r.x() + topLeft.x(), r.y() + topLeft.y(), + r.width(), r.height(), + screenStride, imageStride); + } else { + const QVector<QRect> rects = region.rects(); + + for (int i = 0; i < rects.size(); ++i) { + const QRect r = rects.at(i); + const SRC *src = reinterpret_cast<const SRC*>(image.scanLine(r.y())) + + r.x(); + qt_rectconvert<DST, SRC>(dest, src, + r.x() + topLeft.x(), r.y() + topLeft.y(), + r.width(), r.height(), + screenStride, imageStride); + } + } +} + +#ifdef QT_QWS_DEPTH_32 +static void blit_32(QScreen *screen, const QImage &image, + const QPoint &topLeft, const QRegion ®ion) +{ + switch (image.format()) { + case QImage::Format_RGB32: + case QImage::Format_ARGB32: + case QImage::Format_ARGB32_Premultiplied: + blit_template<quint32, quint32>(screen, image, topLeft, region); + return; +#ifdef QT_QWS_DEPTH_16 + case QImage::Format_RGB16: + blit_template<quint32, quint16>(screen, image, topLeft, region); + return; +#endif + default: + qCritical("blit_32(): Image format %d not supported!", image.format()); + } +} +#endif // QT_QWS_DEPTH_32 + +#ifdef QT_QWS_DEPTH_24 +static void blit_24(QScreen *screen, const QImage &image, + const QPoint &topLeft, const QRegion ®ion) +{ + switch (image.format()) { + case QImage::Format_RGB32: + case QImage::Format_ARGB32: + case QImage::Format_ARGB32_Premultiplied: + blit_template<quint24, quint32>(screen, image, topLeft, region); + return; + case QImage::Format_RGB888: + blit_template<quint24, qrgb888>(screen, image, topLeft, region); + return; +#ifdef QT_QWS_DEPTH_16 + case QImage::Format_RGB16: + blit_template<quint24, quint16>(screen, image, topLeft, region); + return; +#endif + default: + qCritical("blit_24(): Image format %d not supported!", image.format()); + } +} + +static void blit_qrgb888(QScreen *screen, const QImage &image, + const QPoint &topLeft, const QRegion ®ion) +{ + switch (image.format()) { + case QImage::Format_RGB32: + case QImage::Format_ARGB32: + case QImage::Format_ARGB32_Premultiplied: + blit_template<qrgb888, quint32>(screen, image, topLeft, region); + return; + case QImage::Format_RGB888: + blit_template<qrgb888, qrgb888>(screen, image, topLeft, region); + return; +#ifdef QT_QWS_DEPTH_16 + case QImage::Format_RGB16: + blit_template<qrgb888, quint16>(screen, image, topLeft, region); + return; +#endif + default: + qCritical("blit_24(): Image format %d not supported!", image.format()); + break; + } +} +#endif // QT_QWS_DEPTH_24 + +#ifdef QT_QWS_DEPTH_18 +static void blit_18(QScreen *screen, const QImage &image, + const QPoint &topLeft, const QRegion ®ion) +{ + switch (image.format()) { + case QImage::Format_RGB32: + case QImage::Format_ARGB32: + case QImage::Format_ARGB32_Premultiplied: + blit_template<qrgb666, quint32>(screen, image, topLeft, region); + return; + case QImage::Format_RGB666: + blit_template<qrgb666, qrgb666>(screen, image, topLeft, region); + return; +#ifdef QT_QWS_DEPTH_16 + case QImage::Format_RGB16: + blit_template<qrgb666, quint16>(screen, image, topLeft, region); + return; +#endif + default: + qCritical("blit_18(): Image format %d not supported!", image.format()); + } +} +#endif // QT_QWS_DEPTH_18 + +#if (Q_BYTE_ORDER == Q_BIG_ENDIAN) && (defined(QT_QWS_DEPTH_16) || defined(QT_QWS_DEPTH_15)) +class quint16LE +{ +public: + inline quint16LE(quint32 v) { + data = ((v & 0xff00) >> 8) | ((v & 0x00ff) << 8); + } + + inline quint16LE(int v) { + data = ((v & 0xff00) >> 8) | ((v & 0x00ff) << 8); + } + + inline quint16LE(quint16 v) { + data = ((v & 0xff00) >> 8) | ((v & 0x00ff) << 8); + } + + inline quint16LE(qrgb555 v) { + data = (( (quint16)v & 0xff00) >> 8) | + (( (quint16)v & 0x00ff) << 8); + } + + inline bool operator==(const quint16LE &v) const + { + return data == v.data; + } + +private: + quint16 data; +}; +#endif + +#ifdef QT_QWS_DEPTH_16 +static void blit_16(QScreen *screen, const QImage &image, + const QPoint &topLeft, const QRegion ®ion) +{ + switch (image.format()) { + case QImage::Format_RGB32: + case QImage::Format_ARGB32: + case QImage::Format_ARGB32_Premultiplied: + // ### This probably doesn't work but it's a case which should never happen + blit_template<quint16, quint32>(screen, image, topLeft, region); + return; + case QImage::Format_RGB16: + blit_template<quint16, quint16>(screen, image, topLeft, region); + return; + default: + qCritical("blit_16(): Image format %d not supported!", image.format()); + } +} + +#if Q_BYTE_ORDER == Q_BIG_ENDIAN +static void blit_16_bigToLittleEndian(QScreen *screen, const QImage &image, + const QPoint &topLeft, + const QRegion ®ion) +{ + switch (image.format()) { + case QImage::Format_RGB32: + case QImage::Format_ARGB32: + case QImage::Format_ARGB32_Premultiplied: + blit_template<quint16LE, quint32>(screen, image, topLeft, region); + return; + case QImage::Format_RGB16: + blit_template<quint16LE, quint16>(screen, image, topLeft, region); + return; + default: + qCritical("blit_16_bigToLittleEndian(): Image format %d not supported!", image.format()); + } +} + +#endif // Q_BIG_ENDIAN +#endif // QT_QWS_DEPTH_16 + +#ifdef QT_QWS_DEPTH_15 +static void blit_15(QScreen *screen, const QImage &image, + const QPoint &topLeft, const QRegion ®ion) +{ + switch (image.format()) { + case QImage::Format_RGB32: + case QImage::Format_ARGB32: + case QImage::Format_ARGB32_Premultiplied: + blit_template<qrgb555, quint32>(screen, image, topLeft, region); + return; + case QImage::Format_RGB555: + blit_template<qrgb555, qrgb555>(screen, image, topLeft, region); + return; + case QImage::Format_RGB16: + blit_template<qrgb555, quint16>(screen, image, topLeft, region); + return; + default: + qCritical("blit_15(): Image format %d not supported!", image.format()); + } +} + +#if Q_BYTE_ORDER == Q_BIG_ENDIAN +static void blit_15_bigToLittleEndian(QScreen *screen, const QImage &image, + const QPoint &topLeft, + const QRegion ®ion) +{ + switch (image.format()) { + case QImage::Format_RGB555: + blit_template<quint16LE, qrgb555>(screen, image, topLeft, region); + return; + default: + qCritical("blit_15_bigToLittleEndian(): Image format %d not supported!", image.format()); + } +} +#endif // Q_BIG_ENDIAN +#endif // QT_QWS_DEPTH_15 + + +#ifdef QT_QWS_DEPTH_12 +static void blit_12(QScreen *screen, const QImage &image, + const QPoint &topLeft, const QRegion ®ion) +{ + switch (image.format()) { + case QImage::Format_ARGB4444_Premultiplied: + blit_template<qrgb444, qargb4444>(screen, image, topLeft, region); + return; + case QImage::Format_RGB444: + blit_template<qrgb444, qrgb444>(screen, image, topLeft, region); + return; + default: + qCritical("blit_12(): Image format %d not supported!", image.format()); + } +} +#endif // QT_QWS_DEPTH_12 + +#ifdef QT_QWS_DEPTH_8 +static void blit_8(QScreen *screen, const QImage &image, + const QPoint &topLeft, const QRegion ®ion) +{ + switch (image.format()) { + case QImage::Format_RGB32: + case QImage::Format_ARGB32: + case QImage::Format_ARGB32_Premultiplied: + blit_template<quint8, quint32>(screen, image, topLeft, region); + return; + case QImage::Format_RGB16: + blit_template<quint8, quint16>(screen, image, topLeft, region); + return; + case QImage::Format_ARGB4444_Premultiplied: + blit_template<quint8, qargb4444>(screen, image, topLeft, region); + return; + case QImage::Format_RGB444: + blit_template<quint8, qrgb444>(screen, image, topLeft, region); + return; + default: + qCritical("blit_8(): Image format %d not supported!", image.format()); + } +} +#endif // QT_QWS_DEPTH_8 + +#ifdef QT_QWS_DEPTH_4 + +struct qgray4 { quint8 dummy; } Q_PACKED; + +template <typename SRC> +Q_STATIC_TEMPLATE_FUNCTION inline quint8 qt_convertToGray4(SRC color); + +template <> +inline quint8 qt_convertToGray4(quint32 color) +{ + return qGray(color) >> 4; +} + +template <> +inline quint8 qt_convertToGray4(quint16 color) +{ + const int r = (color & 0xf800) >> 11; + const int g = (color & 0x07e0) >> 6; // only keep 5 bit + const int b = (color & 0x001f); + return (r * 11 + g * 16 + b * 5) >> 6; +} + +template <> +inline quint8 qt_convertToGray4(qrgb444 color) +{ + return qt_convertToGray4(quint32(color)); +} + +template <> +inline quint8 qt_convertToGray4(qargb4444 color) +{ + return qt_convertToGray4(quint32(color)); +} + +template <typename SRC> +Q_STATIC_TEMPLATE_FUNCTION inline void qt_rectconvert_gray4(qgray4 *dest4, const SRC *src, + int x, int y, int width, int height, + int dstStride, int srcStride) +{ + const int pixelsPerByte = 2; + quint8 *dest8 = reinterpret_cast<quint8*>(dest4) + + y * dstStride + x / pixelsPerByte; + const int doAlign = x & 1; + const int doTail = (width - doAlign) & 1; + const int width8 = (width - doAlign) / pixelsPerByte; + const int count8 = (width8 + 3) / 4; + + srcStride = srcStride / sizeof(SRC) - width; + dstStride -= (width8 + doAlign); + + for (int i = 0; i < height; ++i) { + if (doAlign) { + *dest8 = (*dest8 & 0xf0) | qt_convertToGray4<SRC>(*src++); + ++dest8; + } + if (count8) { + int n = count8; + switch (width8 & 0x03) // duff's device + { + case 0: do { *dest8++ = qt_convertToGray4<SRC>(src[0]) << 4 + | qt_convertToGray4<SRC>(src[1]); + src += 2; + case 3: *dest8++ = qt_convertToGray4<SRC>(src[0]) << 4 + | qt_convertToGray4<SRC>(src[1]); + src += 2; + case 2: *dest8++ = qt_convertToGray4<SRC>(src[0]) << 4 + | qt_convertToGray4<SRC>(src[1]); + src += 2; + case 1: *dest8++ = qt_convertToGray4<SRC>(src[0]) << 4 + | qt_convertToGray4<SRC>(src[1]); + src += 2; + } while (--n > 0); + } + } + + if (doTail) + *dest8 = qt_convertToGray4<SRC>(*src++) << 4 | (*dest8 & 0x0f); + + dest8 += dstStride; + src += srcStride; + } +} + +template <> +void qt_rectconvert(qgray4 *dest, const quint32 *src, + int x, int y, int width, int height, + int dstStride, int srcStride) +{ + qt_rectconvert_gray4<quint32>(dest, src, x, y, width, height, + dstStride, srcStride); +} + +template <> +void qt_rectconvert(qgray4 *dest, const quint16 *src, + int x, int y, int width, int height, + int dstStride, int srcStride) +{ + qt_rectconvert_gray4<quint16>(dest, src, x, y, width, height, + dstStride, srcStride); +} + +template <> +void qt_rectconvert(qgray4 *dest, const qrgb444 *src, + int x, int y, int width, int height, + int dstStride, int srcStride) +{ + qt_rectconvert_gray4<qrgb444>(dest, src, x, y, width, height, + dstStride, srcStride); +} + +template <> +void qt_rectconvert(qgray4 *dest, const qargb4444 *src, + int x, int y, int width, int height, + int dstStride, int srcStride) +{ + qt_rectconvert_gray4<qargb4444>(dest, src, x, y, width, height, + dstStride, srcStride); +} + +static void blit_4(QScreen *screen, const QImage &image, + const QPoint &topLeft, const QRegion ®ion) +{ + switch (image.format()) { + case QImage::Format_ARGB32_Premultiplied: + blit_template<qgray4, quint32>(screen, image, topLeft, region); + return; + case QImage::Format_RGB16: + blit_template<qgray4, quint16>(screen, image, topLeft, region); + return; + case QImage::Format_RGB444: + blit_template<qgray4, qrgb444>(screen, image, topLeft, region); + return; + case QImage::Format_ARGB4444_Premultiplied: + blit_template<qgray4, qargb4444>(screen, image, topLeft, region); + return; + default: + qCritical("blit_4(): Image format %d not supported!", image.format()); + } +} +#endif // QT_QWS_DEPTH_4 + +#ifdef QT_QWS_DEPTH_1 + +struct qmono { quint8 dummy; } Q_PACKED; + +template <typename SRC> +Q_STATIC_TEMPLATE_FUNCTION inline quint8 qt_convertToMono(SRC color); + +template <> +inline quint8 qt_convertToMono(quint32 color) +{ + return qGray(color) >> 7; +} + +template <> +inline quint8 qt_convertToMono(quint16 color) +{ + return (qGray(qt_colorConvert<quint32, quint16>(color, 0)) >> 7); +} + +template <> +inline quint8 qt_convertToMono(qargb4444 color) +{ + return (qGray(quint32(color)) >> 7); +} + +template <> +inline quint8 qt_convertToMono(qrgb444 color) +{ + return (qGray(quint32(color)) >> 7); +} + +template <typename SRC> +inline void qt_rectconvert_mono(qmono *dest, const SRC *src, + int x, int y, int width, int height, + int dstStride, int srcStride) +{ + const int pixelsPerByte = 8; + quint8 *dest8 = reinterpret_cast<quint8*>(dest) + + y * dstStride + x / pixelsPerByte; + const int alignWidth = qMin(width, (8 - (x & 7)) & 7); + const int doAlign = (alignWidth > 0 ? 1 : 0); + const int alignStart = pixelsPerByte - 1 - (x & 7); + const int alignStop = alignStart - (alignWidth - 1); + const quint8 alignMask = ((1 << alignWidth) - 1) << alignStop; + const int tailWidth = (width - alignWidth) & 7; + const int doTail = (tailWidth > 0 ? 1 : 0); + const quint8 tailMask = (1 << (pixelsPerByte - tailWidth)) - 1; + const int width8 = (width - alignWidth) / pixelsPerByte; + + srcStride = srcStride / sizeof(SRC) - (width8 * 8 + alignWidth); + dstStride -= (width8 + doAlign); + + for (int j = 0; j < height; ++j) { + if (doAlign) { + quint8 d = *dest8 & ~alignMask; + for (int i = alignStart; i >= alignStop; --i) + d |= qt_convertToMono<SRC>(*src++) << i; + *dest8++ = d; + } + for (int i = 0; i < width8; ++i) { + *dest8 = (qt_convertToMono<SRC>(src[0]) << 7) + | (qt_convertToMono<SRC>(src[1]) << 6) + | (qt_convertToMono<SRC>(src[2]) << 5) + | (qt_convertToMono<SRC>(src[3]) << 4) + | (qt_convertToMono<SRC>(src[4]) << 3) + | (qt_convertToMono<SRC>(src[5]) << 2) + | (qt_convertToMono<SRC>(src[6]) << 1) + | (qt_convertToMono<SRC>(src[7])); + src += 8; + ++dest8; + } + if (doTail) { + quint8 d = *dest8 & tailMask; + switch (tailWidth) { + case 7: d |= qt_convertToMono<SRC>(src[6]) << 1; + case 6: d |= qt_convertToMono<SRC>(src[5]) << 2; + case 5: d |= qt_convertToMono<SRC>(src[4]) << 3; + case 4: d |= qt_convertToMono<SRC>(src[3]) << 4; + case 3: d |= qt_convertToMono<SRC>(src[2]) << 5; + case 2: d |= qt_convertToMono<SRC>(src[1]) << 6; + case 1: d |= qt_convertToMono<SRC>(src[0]) << 7; + } + *dest8 = d; + } + + dest8 += dstStride; + src += srcStride; + } +} + +template <> +void qt_rectconvert(qmono *dest, const quint32 *src, + int x, int y, int width, int height, + int dstStride, int srcStride) +{ + qt_rectconvert_mono<quint32>(dest, src, x, y, width, height, + dstStride, srcStride); +} + +template <> +void qt_rectconvert(qmono *dest, const quint16 *src, + int x, int y, int width, int height, + int dstStride, int srcStride) +{ + qt_rectconvert_mono<quint16>(dest, src, x, y, width, height, + dstStride, srcStride); +} + +template <> +void qt_rectconvert(qmono *dest, const qrgb444 *src, + int x, int y, int width, int height, + int dstStride, int srcStride) +{ + qt_rectconvert_mono<qrgb444>(dest, src, x, y, width, height, + dstStride, srcStride); +} + +template <> +void qt_rectconvert(qmono *dest, const qargb4444 *src, + int x, int y, int width, int height, + int dstStride, int srcStride) +{ + qt_rectconvert_mono<qargb4444>(dest, src, x, y, width, height, + dstStride, srcStride); +} + +static void blit_1(QScreen *screen, const QImage &image, + const QPoint &topLeft, const QRegion ®ion) +{ + switch (image.format()) { + case QImage::Format_ARGB32_Premultiplied: + blit_template<qmono, quint32>(screen, image, topLeft, region); + return; + case QImage::Format_RGB16: + blit_template<qmono, quint16>(screen, image, topLeft, region); + return; + case QImage::Format_RGB444: + blit_template<qmono, qrgb444>(screen, image, topLeft, region); + return; + case QImage::Format_ARGB4444_Premultiplied: + blit_template<qmono, qargb4444>(screen, image, topLeft, region); + return; + default: + qCritical("blit_1(): Image format %d not supported!", image.format()); + } +} +#endif // QT_QWS_DEPTH_1 + +#ifdef QT_QWS_DEPTH_GENERIC + +static void blit_rgb(QScreen *screen, const QImage &image, + const QPoint &topLeft, const QRegion ®ion) +{ + switch (image.format()) { + case QImage::Format_ARGB32_Premultiplied: + blit_template<qrgb, quint32>(screen, image, topLeft, region); + return; + case QImage::Format_RGB16: + blit_template<qrgb, quint16>(screen, image, topLeft, region); + return; + default: + qCritical("blit_rgb(): Image format %d not supported!", image.format()); + } +} + +void qt_set_generic_blit(QScreen *screen, int bpp, + int len_red, int len_green, int len_blue, int len_alpha, + int off_red, int off_green, int off_blue, int off_alpha) +{ + qrgb::bpp = bpp / 8; + qrgb::len_red = len_red; + qrgb::len_green = len_green; + qrgb::len_blue = len_blue; + qrgb::len_alpha = len_alpha; + qrgb::off_red = off_red; + qrgb::off_green = off_green; + qrgb::off_blue = off_blue; + qrgb::off_alpha = off_alpha; + screen->d_ptr->blit = blit_rgb; + if (bpp == 16) + screen->d_ptr->solidFill = solidFill_rgb_16bpp; + else if (bpp == 32) + screen->d_ptr->solidFill = solidFill_rgb_32bpp; +} + +#endif // QT_QWS_DEPTH_GENERIC + +void qt_blit_setup(QScreen *screen, const QImage &image, + const QPoint &topLeft, const QRegion ®ion) +{ + switch (screen->depth()) { +#ifdef QT_QWS_DEPTH_32 + case 32: + if (screen->pixelType() == QScreen::NormalPixel) + screen->d_ptr->blit = blit_32; + else + screen->d_ptr->blit = blit_template<qabgr8888, quint32>; + break; +#endif +#ifdef QT_QWS_DEPTH_24 + case 24: + if (screen->pixelType() == QScreen::NormalPixel) + screen->d_ptr->blit = blit_qrgb888; + else + screen->d_ptr->blit = blit_24; + break; +#endif +#ifdef QT_QWS_DEPTH_18 + case 18: + screen->d_ptr->blit = blit_18; + break; +#endif +#ifdef QT_QWS_DEPTH_16 + case 16: +#if Q_BYTE_ORDER == Q_BIG_ENDIAN + if (screen->d_ptr->fb_is_littleEndian) + screen->d_ptr->blit = blit_16_bigToLittleEndian; + else +#endif + if (screen->pixelType() == QScreen::NormalPixel) + screen->d_ptr->blit = blit_16; + else + screen->d_ptr->blit = blit_template<qbgr565, quint16>; + break; +#endif +#ifdef QT_QWS_DEPTH_15 + case 15: +#if Q_BYTE_ORDER == Q_BIG_ENDIAN + if (screen->d_ptr->fb_is_littleEndian) + screen->d_ptr->blit = blit_15_bigToLittleEndian; + else +#endif // Q_BIG_ENDIAN + if (screen->pixelType() == QScreen::NormalPixel) + screen->d_ptr->blit = blit_15; + else + screen->d_ptr->blit = blit_template<qbgr555, qrgb555>; + break; +#endif +#ifdef QT_QWS_DEPTH_12 + case 12: + screen->d_ptr->blit = blit_12; + break; +#endif +#ifdef QT_QWS_DEPTH_8 + case 8: + screen->d_ptr->blit = blit_8; + break; +#endif +#ifdef QT_QWS_DEPTH_4 + case 4: + screen->d_ptr->blit = blit_4; + break; +#endif +#ifdef QT_QWS_DEPTH_1 + case 1: + screen->d_ptr->blit = blit_1; + break; +#endif + default: + qFatal("blit_setup(): Screen depth %d not supported!", + screen->depth()); + screen->d_ptr->blit = 0; + break; + } + screen->d_ptr->blit(screen, image, topLeft, region); +} + +QScreenPrivate::QScreenPrivate(QScreen *parent, QScreen::ClassId id) + : defaultGraphicsSystem(QWSGraphicsSystem(parent)), + pixelFormat(QImage::Format_Invalid), +#ifdef QT_QWS_CLIENTBLIT + supportsBlitInClients(false), +#endif + classId(id), q_ptr(parent) +{ + solidFill = qt_solidFill_setup; + blit = qt_blit_setup; +#if Q_BYTE_ORDER == Q_BIG_ENDIAN + fb_is_littleEndian = false; +#endif + pixmapFactory = 0; + graphicsSystem = &defaultGraphicsSystem; +} + +QScreenPrivate::~QScreenPrivate() +{ +} + +QImage::Format QScreenPrivate::preferredImageFormat() const +{ + if (pixelFormat > QImage::Format_Indexed8) + return pixelFormat; + + if (q_ptr->depth() <= 16) + return QImage::Format_RGB16; + else + return QImage::Format_ARGB32_Premultiplied; +} + +/*! + \class QScreen + \ingroup qws + + \brief The QScreen class is a base class for screen drivers in + Qt for Embedded Linux. + + Note that this class is only available in \l{Qt for Embedded Linux}. + + \l{Qt for Embedded Linux} provides ready-made drivers for several screen + protocols, see the \l{Qt for Embedded Linux Display Management}{display + management} documentation for details. Custom screen drivers can + be implemented by subclassing the QScreen class and creating a + screen driver plugin (derived from QScreenDriverPlugin). The + default implementation of the QScreenDriverFactory class + will automatically detect the plugin, and load the driver into the + server application at run-time using Qt's \l {How to Create Qt + Plugins}{plugin system}. + + When rendering, the default behavior is for each + client to render its widgets as well as its decorations into + memory, while the server copies the memory content to the device's + framebuffer using the screen driver. See the \l{Qt for Embedded Linux + Architecture} overview for details (note that it is possible for + the clients to manipulate and control the underlying hardware + directly as well). + + Starting with Qt 4.2, it is also possible to add an + accelerated graphics driver to take advantage of available + hardware resources. See the \l{Adding an Accelerated Graphics + Driver to Qt for Embedded Linux} documentation for details. + + \tableofcontents + + \section1 Framebuffer Management + + When a \l{Qt for Embedded Linux} application starts running, it + calls the screen driver's connect() function to map the + framebuffer and the accelerated drivers that the graphics card + control registers. The connect() function should then read out the + parameters of the framebuffer and use them as required to set this + class's protected variables. + + The initDevice() function can be reimplemented to initialize the + graphics card. Note, however, that connect() is called \e before + the initDevice() function, so, for some hardware configurations, + some of the initialization that would normally be done in the + initDevice() function might have to be done in the connect() + function. + + Likewise, just before a \l{Qt for Embedded Linux} application + exits, it calls the screen driver's disconnect() function. The + server application will in addition call the shutdownDevice() + function before it calls disconnect(). Note that the default + implementation of the shutdownDevice() function only hides the + mouse cursor. + + QScreen also provides the save() and restore() functions, making + it possible to save and restore the state of the graphics + card. Note that the default implementations do nothing. Hardware + screen drivers should reimplement these functions to save (and + restore) its registers, enabling switching between virtual + consoles. + + In addition, you can use the base() function to retrieve a pointer + to the beginning of the framebuffer, and the region() function to + retrieve the framebuffer's region. Use the onCard() function to + determine whether the framebuffer is within the graphics card's + memory, and the totalSize() function to determine the size of the + available graphics card memory (including the screen). Finally, + you can use the offset() function to retrieve the offset between + the framebuffer's coordinates and the application's coordinate + system. + + \section1 Palette Management + + QScreen provides several functions to retrieve information about + the color palette: The clut() function returns a pointer to the + color lookup table (i.e. its color palette). Use the numCols() + function to determine the number of entries in this table, and the + alloc() function to retrieve the palette index of the color that + is the closest match to a given RGB value. + + To determine if the screen driver supports a given color depth, + use the supportsDepth() function that returns true of the + specified depth is supported. + + \section1 Drawing on Screen + + When a screen update is required, the \l{Qt for Embedded Linux} 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 calls + the exposeRegion() function that composes the window surfaces and + copies the content of memory to screen by calling the blit() and + solidFill() functions. + + The blit() function copies a given region in a given image to a + specified point using device coordinates, while the solidFill() + function fills the given region of the screen with the specified + color. Note that normally there is no need to call either of these + functions explicitly. + + In addition, QScreen provides the blank() function that can be + reimplemented to prevent any contents from being displayed on the + screen, and the setDirty() function that can be reimplemented to + indicate that a given rectangle of the screen has been + altered. Note that the default implementations of these functions + do nothing. + + Reimplement the the mapFromDevice() and mapToDevice() functions to + map objects from the framebuffer coordinate system to the + coordinate space used by the application, and vice versa. Be aware + that the default implementations simply return the given objects + as they are. + + \section1 Properties + + \table + \header \o Property \o Functions + \row + \o Size + \o + + The size of the screen can be retrieved using the screenSize() + function. The size is returned in bytes. + + The framebuffer's logical width and height can be retrieved using + width() and height(), respectively. These functions return values + are given in pixels. Alternatively, the physicalWidth() and + physicalHeight() function returns the same metrics in + millimeters. QScreen also provides the deviceWidth() and + deviceHeight() functions returning the physical width and height + of the device in pixels. Note that the latter metrics can differ + from the ones used if the display is centered within the + framebuffer. + + \row + \o Resolution + \o + + Reimplement the setMode() function to be able to set the + framebuffer to a new resolution (width and height) and bit depth. + + The current depth of the framebuffer can be always be retrieved + using the depth() function. Use the pixmapDepth() function to + obtain the preferred depth for pixmaps. + + \row + \o Pixmap Alignment + \o + + Use the pixmapOffsetAlignment() function to retrieve the value to + which the start address of pixmaps held in the graphics card's + memory, should be aligned. + + Use the pixmapLinestepAlignment() to retrieve the value to which + the \e {individual scanlines} of pixmaps should be aligned. + + \row + \o Image Display + \o + + The isInterlaced() function tells whether the screen is displaying + images progressively, and the isTransformed() function whether it + is rotated. The transformOrientation() function can be + reimplemented to return the current rotation. + + \row + \o Scanlines + \o + + Use the linestep() function to retrieve the length of each + scanline of the framebuffer. + + \row + \o Pixel Type + \o + + The pixelType() function returns the screen's pixel storage format as + described by the PixelType enum. + + \endtable + + \section1 Subclassing and Initial Values + + You need to set the following members when implementing a subclass of QScreen: + + \table + \header \o Member \o Initial Value + \row \o \l{QScreen::}{data} \o A pointer to the framebuffer if possible; + 0 otherwise. + \row \o \l{QScreen::}{lstep} \o The number of bytes between each scanline + in the framebuffer. + \row \o \l{QScreen::}{w} \o The logical screen width in pixels. + \row \o \l{QScreen::}{h} \o The logical screen height in pixels. + \row \o \l{QScreen::}{dw} \o The real screen width in pixels. + \row \o \l{QScreen::}{dh} \o The real screen height in pixels. + \row \o \l{QScreen::}{d} \o The number of bits per pixel. + \row \o \l{QScreen::}{physWidth} \o The screen width in millimeters. + \row \o \l{QScreen::}{physHeight} \o The screen height in millimeters. + \endtable + + The logical screen values are the same as the real screen values unless the + screen is transformed in some way; e.g., rotated. + + See also the \l{Accelerated Graphics Driver Example} for an example that + shows how to initialize these values. + + \sa QScreenDriverPlugin, QScreenDriverFactory, {Qt for Embedded Linux Display + Management} +*/ + +/*! + \enum QScreen::PixelType + + This enum describes the pixel storage format of the screen, + i.e. the order of the red (R), green (G) and blue (B) components + of a pixel. + + \value NormalPixel Red-green-blue (RGB) + \value BGRPixel Blue-green-red (BGR) + + \sa pixelType() +*/ + +/*! + \enum QScreen::ClassId + + This enum defines the class identifiers for the known screen subclasses. + + \value LinuxFBClass QLinuxFBScreen + \value TransformedClass QTransformedScreen + \value VNCClass QVNCScreen + \value MultiClass QMultiScreen + \value VFbClass QVFbScreen + \value DirectFBClass QDirectFBScreen + \value SvgalibClass QSvgalibScreen + \value ProxyClass QProxyScreen + \value GLClass QGLScreen + \value CustomClass Unknown QScreen subclass + + \sa classId() +*/ + +/*! + \variable QScreen::screenclut + \brief the color table + + Initialize this variable in a subclass using a paletted screen mode, + and initialize its partner, QScreen::screencols. + + \sa screencols +*/ + +/*! + \variable QScreen::screencols + \brief the number of entries in the color table + + Initialize this variable in a subclass using a paletted screen mode, + and initialize its partner, QScreen::screenclut. + + \sa screenclut +*/ + +/*! + \variable QScreen::data + \brief points to the first visible pixel in the frame buffer. + + You must initialize this variable if you are using the default + implementation of non-buffered painting Qt::WA_PaintOnScreen, + QPixmap::grabWindow() or QDirectPainter::frameBuffer(). If you + initialize this variable, you must also initialize QScreen::size and + QScreen::mapsize. + + \sa QScreen::size, QScreen::mapsize +*/ + +/*! + \variable QScreen::w + \brief the logical width of the screen. + + This variable \e{must} be initialized by a subclass. +*/ + +/*! + \variable QScreen::lstep + \brief the number of bytes representing a line in the frame buffer. + + i.e., \e{line step}. \c {data[lstep * 2]} is the address of the + first visible pixel in the third line of the frame buffer. + + \sa data +*/ + +/*! + \variable QScreen::h + \brief the logical height of the screen. + + This variable \e{must} be initialized by a subclass. +*/ + +/*! + \variable QScreen::d + \brief the pixel depth + + This is the number of significant bits used to set a pixel + color. This variable \e{must} be initialized by a subclass. +*/ + +/*! + \variable QScreen::pixeltype + \brief set to BGRPixel + + Set this variable to BGRPixel in a subclass, if the screen pixel + format is a BGR type and you have used setPixelFormat() to set the + pixel format to the corresponding RGB format. e.g., you have set the + pixel format to QImage::Format_RGB555, but your screen really uses + BGR, not RGB. +*/ + +/*! + \variable QScreen::grayscale + \brief the gray scale screen mode flag + + Set this variable to true in a subclass, if you are using a + grayscale screen mode. e.g., in an 8-bit mode where you don't want + to use the palette, but you want to use the grayscales. +*/ + +/*! + \variable QScreen::dw + \brief the device width + + This is the number of pixels in a row of the physical screen. It + \e{must} be initialized by a subclass. Normally, it should be set to + the logical width QScreen::w, but it might be different, e.g., if + you are doing rotations in software. + + \sa QScreen::w +*/ + +/*! + \variable QScreen::dh + \brief the device height + + This is the number of pixels in a column of the physical screen. It + \e{must} be initialized by a subclass. Normally, it should be set to + the logical height QScreen::h, but it might be different, e.g., if + you are doing rotations in software. + + \sa QScreen::h +*/ + +/*! + \variable QScreen::size + \brief the number of bytes in the visible region of the frame buffer + + This is the number of bytes in the visible part of the block pointed + to by the QScreen::data pointer. You must initialize this variable + if you initialize the QScreen::data pointer. + + \sa QScreen::data, QScreen::mapsize +*/ + +/*! + \variable QScreen::mapsize + \brief the total number of bytes in the frame buffer + + This is the total number of bytes in the block pointed to by the + QScreen::data pointer. You must initialize this variable if you + initialize the QScreen::data pointer. + + \sa QScreen::data, QScreen::size +*/ + +/*! + \variable QScreen::physWidth + \brief the physical width of the screen in millimeters. + + Currently, this variable is used when calculating the screen DPI, + which in turn is used when deciding the actual font size Qt is + using. +*/ + +/*! + \variable QScreen::physHeight + \brief the physical height of the screen in millimeters. + + Currently, this variable is used when calculating the screen DPI, + which in turn is used when deciding the actual font size Qt is + using. +*/ + +/*! + \fn static QScreen* QScreen::instance() + + Returns a pointer to the application's QScreen instance. + + If this screen consists of several subscreens, operations to the + returned instance will affect all its subscreens. Use the + subscreens() function to retrieve access to a particular + subscreen. + + \sa subScreens(), subScreenIndexAt() +*/ + +/*! + \fn QList<QScreen*> QScreen::subScreens() const + \since 4.2 + + Returns a list of this screen's subscreens. Use the + subScreenIndexAt() function to retrieve the index of a screen at a + given position. + + Note that if \e this screen consists of several subscreens, + operations to \e this instance will affect all subscreens by + default. + + \sa instance(), subScreenIndexAt() +*/ + +/*! + \fn int QScreen::physicalWidth() const + \since 4.2 + + Returns the physical width of the screen in millimeters. + + \sa width(), deviceWidth(), physicalHeight() +*/ + +/*! + \fn int QScreen::physicalHeight() const + \since 4.2 + + Returns the physical height of the screen in millimeters. + + \sa height(), deviceHeight(), physicalWidth() +*/ + +/*! + \fn virtual bool QScreen::initDevice() = 0 + + This function is called by the \l{Qt for Embedded Linux} server to + initialize the framebuffer. Note that a server application will call the + connect() function prior to this function. + + Implement this function to make accelerated drivers set up the + graphics card. Return true to indicate success and false to indicate + failure. + + \sa shutdownDevice(), connect() +*/ + +/*! + \fn virtual bool QScreen::connect(const QString &displaySpec) = 0 + + This function is called by every \l{Qt for Embedded Linux} + application on startup, and must be implemented to map in the + framebuffer and the accelerated drivers that the graphics card + control registers. Note that coonnect must be called \e before + the initDevice() function. + + Ensure that true is returned if a connection to the screen device + is made. Otherwise, return false. Upon making the connection, the + function should read out the parameters of the framebuffer and use + them as required to set this class's protected variables. + + The \a displaySpec argument is passed by the QWS_DISPLAY + environment variable or the -display command line parameter, and + has the following syntax: + + \snippet doc/src/snippets/code/src_gui_embedded_qscreen_qws.cpp 0 + + For example, to use the mach64 driver on fb1 as display 2: + + \snippet doc/src/snippets/code/src_gui_embedded_qscreen_qws.cpp 1 + + See \l{Qt for Embedded Linux Display Management} for more details. + + \sa disconnect(), initDevice(), {Running Qt for Embedded Linux Applications} +*/ + +/*! + \fn QScreen::disconnect() + + This function is called by every \l{Qt for Embedded Linux} application + before exiting, and must be implemented to unmap the + framebuffer. Note that a server application will call the + shutdownDevice() function prior to this function. + + \sa connect(), shutdownDevice(), {Running Qt for Embedded Linux + Applications} +*/ + +/*! + \fn QScreen::setMode(int width, int height, int depth) + + Implement this function to reset the framebuffer's resolution (\a + width and \a height) and bit \a depth. + + After the resolution has been set, existing paint engines will be + invalid and the framebuffer should be completely redrawn. In a + multiple-process situation, all other applications must be + notified to reset their mode and update themselves accordingly. +*/ + +/*! + \fn QScreen::blank(bool on) + + Prevents the screen driver form displaying any content on the + screen. + + Note that the default implementation does nothing. + + Reimplement this function to prevent the screen driver from + displaying any contents on the screen if \a on is true; otherwise + the contents is expected to be shown. + + \sa blit() +*/ + +/*! + \fn int QScreen::pixmapOffsetAlignment() + + Returns the value (in bits) to which the start address of pixmaps + held in the graphics card's memory, should be aligned. + + Note that the default implementation returns 64; reimplement this + function to override the return value, e.g., when implementing an + accelerated driver (see the \l {Adding an Accelerated Graphics + Driver to Qt for Embedded Linux}{Adding an Accelerated Graphics Driver} + documentation for details). + + \sa pixmapLinestepAlignment() +*/ + +/*! + \fn int QScreen::pixmapLinestepAlignment() + + Returns the value (in bits) to which individual scanlines of + pixmaps held in the graphics card's memory, should be + aligned. + + Note that the default implementation returns 64; reimplement this + function to override the return value, e.g., when implementing an + accelerated driver (see the \l {Adding an Accelerated Graphics + Driver to Qt for Embedded Linux}{Adding an Accelerated Graphics Driver} + documentation for details). + + \sa pixmapOffsetAlignment() +*/ + +/*! + \fn QScreen::width() const + + Returns the logical width of the framebuffer in pixels. + + \sa deviceWidth(), physicalWidth(), height() +*/ + +/*! + \fn int QScreen::height() const + + Returns the logical height of the framebuffer in pixels. + + \sa deviceHeight(), physicalHeight(), width() +*/ + +/*! + \fn QScreen::depth() const + + Returns the depth of the framebuffer, in bits per pixel. + + Note that the returned depth is the number of bits each pixel + fills rather than the number of significant bits, so 24bpp and + 32bpp express the same range of colors (8 bits of red, green and + blue). + + \sa clut(), pixmapDepth() +*/ + +/*! + \fn int QScreen::pixmapDepth() const + + Returns the preferred depth for pixmaps, in bits per pixel. + + \sa depth() +*/ + +/*! + \fn QScreen::linestep() const + + Returns the length of each scanline of the framebuffer in bytes. + + \sa isInterlaced() +*/ + +/*! + \fn QScreen::deviceWidth() const + + Returns the physical width of the framebuffer device in pixels. + + Note that the returned width can differ from the width which + \l{Qt for Embedded Linux} will actually use, that is if the display is + centered within the framebuffer. + + \sa width(), physicalWidth(), deviceHeight() +*/ + +/*! + \fn QScreen::deviceHeight() const + + Returns the full height of the framebuffer device in pixels. + + Note that the returned height can differ from the height which + \l{Qt for Embedded Linux} will actually use, that is if the display is + centered within the framebuffer. + + \sa height(), physicalHeight(), deviceWidth() +*/ + +/*! + \fn uchar *QScreen::base() const + + Returns a pointer to the beginning of the framebuffer. + + \sa onCard(), region(), totalSize() +*/ + +/*! + \fn uchar *QScreen::cache(int) + + \internal + + This function is used to store pixmaps in graphics memory for the + use of the accelerated drivers. See QLinuxFbScreen (where the + caching is implemented) for more information. +*/ + +/*! + \fn QScreen::uncache(uchar *) + + \internal + + This function is called on pixmap destruction to remove them from + graphics card memory. +*/ + +/*! + \fn QScreen::screenSize() const + + Returns the size of the screen in bytes. + + The screen size is always located at the beginning of framebuffer + memory, i.e. it can also be retrieved using the base() function. + + \sa base(), region() +*/ + +/*! + \fn QScreen::totalSize() const + + Returns the size of the available graphics card memory (including + the screen) in bytes. + + \sa onCard() +*/ + +// Unaccelerated screen/driver setup. Can be overridden by accelerated +// drivers + +/*! + \fn QScreen::QScreen(int displayId) + + Constructs a new screen driver. + + The \a displayId identifies the \l{Qt for Embedded Linux} server to connect + to. +*/ + +/*! + \fn QScreen::clut() + + Returns a pointer to the screen's color lookup table (i.e. its + color palette). + + Note that this function only apply in paletted modes like 8-bit, + i.e. in modes where only the palette indexes (and not the actual + color values) are stored in memory. + + \sa alloc(), depth(), numCols() +*/ + +/*! + \fn int QScreen::numCols() + + Returns the number of entries in the screen's color lookup table + (i.e. its color palette). A pointer to the color table can be + retrieved using the clut() function. + + \sa clut(), alloc() +*/ + +/*! + \since 4.4 + + Constructs a new screen driver. + + The \a display_id identifies the \l{Qt for Embedded Linux} + server to connect to. The \a classId specifies the class + identifier. +*/ +QScreen::QScreen(int display_id, ClassId classId) + : screencols(0), data(0), entries(0), entryp(0), lowest(0), + w(0), lstep(0), h(0), d(1), pixeltype(NormalPixel), grayscale(false), + dw(0), dh(0), size(0), mapsize(0), displayId(display_id), + physWidth(0), physHeight(0), d_ptr(new QScreenPrivate(this, classId)) +{ + clearCacheFunc = 0; +} + +QScreen::QScreen(int display_id) + : screencols(0), data(0), entries(0), entryp(0), lowest(0), + w(0), lstep(0), h(0), d(1), pixeltype(NormalPixel), grayscale(false), + dw(0), dh(0), size(0), mapsize(0), displayId(display_id), + physWidth(0), physHeight(0), d_ptr(new QScreenPrivate(this)) +{ + clearCacheFunc = 0; +} + +/*! + Destroys this screen driver. +*/ + +QScreen::~QScreen() +{ + delete d_ptr; +} + +/*! + This function is called by the \l{Qt for Embedded Linux} server before it + calls the disconnect() function when exiting. + + Note that the default implementation only hides the mouse cursor; + reimplement this function to do the necessary graphics card + specific cleanup. + + \sa initDevice(), disconnect() +*/ + +void QScreen::shutdownDevice() +{ +#ifndef QT_NO_QWS_CURSOR + qt_screencursor->hide(); +#endif +} + +extern bool qws_accel; //in qapplication_qws.cpp + +/*! + \fn PixelType QScreen::pixelType() const + + Returns the pixel storage format of the screen. +*/ + +/*! + Returns the pixel format of the screen, or \c QImage::Format_Invalid + if the pixel format is not a supported image format. + +*/ +QImage::Format QScreen::pixelFormat() const +{ + return d_ptr->pixelFormat; +} + +/*! + Sets the screen's pixel format to \a format. + */ +void QScreen::setPixelFormat(QImage::Format format) +{ + d_ptr->pixelFormat = format; +} + + +/*! + \fn int QScreen::alloc(unsigned int red, unsigned int green, unsigned int blue) + + Returns the index in the screen's palette which is the closest + match to the given RGB value (\a red, \a green, \a blue). + + Note that this function only apply in paletted modes like 8-bit, + i.e. in modes where only the palette indexes (and not the actual + color values) are stored in memory. + + \sa clut(), numCols() +*/ + +int QScreen::alloc(unsigned int r,unsigned int g,unsigned int b) +{ + int ret = 0; + if (d == 8) { + if (grayscale) + return qGray(r, g, b); + + // First we look to see if we match a default color + const int pos = (r + 25) / 51 * 36 + (g + 25) / 51 * 6 + (b + 25) / 51; + if (pos < screencols && screenclut[pos] == qRgb(r, g, b)) { + return pos; + } + + // search for nearest color + unsigned int mindiff = 0xffffffff; + unsigned int diff; + int dr,dg,db; + + for (int loopc = 0; loopc < screencols; ++loopc) { + dr = qRed(screenclut[loopc]) - r; + dg = qGreen(screenclut[loopc]) - g; + db = qBlue(screenclut[loopc]) - b; + diff = dr*dr + dg*dg + db*db; + + if (diff < mindiff) { + ret = loopc; + if (!diff) + break; + mindiff = diff; + } + } + } else if (d == 4) { + ret = qGray(r, g, b) >> 4; + } else if (d == 1) { + ret = qGray(r, g, b) >= 128; + } else { + qFatal("cannot alloc %dbpp color", d); + } + + return ret; +} + +/*! + Saves the current state of the graphics card. + + For example, hardware screen drivers should reimplement the save() + and restore() functions to save and restore its registers, + enabling swintching between virtual consoles. + + Note that the default implementation does nothing. + + \sa restore() +*/ + +void QScreen::save() +{ +} + +/*! + Restores the previously saved state of the graphics card. + + For example, hardware screen drivers should reimplement the save() + and restore() functions to save and restore its registers, + enabling swintching between virtual consoles. + + Note that the default implementation does nothing. + + \sa save() +*/ + +void QScreen::restore() +{ +} + +void QScreen::blank(bool) +{ +} + +/*! + \internal +*/ + +void QScreen::set(unsigned int, unsigned int, unsigned int, unsigned int) +{ +} + +/*! + \fn bool QScreen::supportsDepth(int depth) const + + Returns true if the screen supports the specified color \a depth; + otherwise returns false. + + \sa clut() +*/ + +bool QScreen::supportsDepth(int d) const +{ + if (false) { + //Just to simplify the ifdeffery +#ifdef QT_QWS_DEPTH_1 + } else if(d==1) { + return true; +#endif +#ifdef QT_QWS_DEPTH_4 + } else if(d==4) { + return true; +#endif +#ifdef QT_QWS_DEPTH_8 + } else if(d==8) { + return true; +#endif +#ifdef QT_QWS_DEPTH_16 + } else if(d==16) { + return true; +#endif +#ifdef QT_QWS_DEPTH_15 + } else if (d == 15) { + return true; +#endif +#ifdef QT_QWS_DEPTH_18 + } else if(d==18 || d==19) { + return true; +#endif +#ifdef QT_QWS_DEPTH_24 + } else if(d==24) { + return true; +#endif +#ifdef QT_QWS_DEPTH_32 + } else if(d==32) { + return true; +#endif + } + return false; +} + +/*! + \fn bool QScreen::onCard(const unsigned char *buffer) const + + Returns true if the specified \a buffer is within the graphics + card's memory; otherwise returns false (i.e. if it's in main RAM). + + \sa base(), totalSize() +*/ + +bool QScreen::onCard(const unsigned char * p) const +{ + long t=(unsigned long)p; + long bmin=(unsigned long)data; + if (t < bmin) + return false; + if(t >= bmin+mapsize) + return false; + return true; +} + +/*! + \fn bool QScreen::onCard(const unsigned char * buffer, ulong& offset) const + \overload + + If the specified \a buffer is within the graphics card's memory, + this function stores the offset from the start of graphics card + memory (in bytes), in the location specified by the \a offset + parameter. +*/ + +bool QScreen::onCard(const unsigned char * p, ulong& offset) const +{ + long t=(unsigned long)p; + long bmin=(unsigned long)data; + if (t < bmin) + return false; + long o = t - bmin; + if (o >= mapsize) + return false; + offset = o; + return true; +} + +/* +#if !defined(QT_NO_QWS_REPEATER) + { "Repeater", qt_get_screen_repeater, 0 }, +#endif +#if defined(QT_QWS_EE) + { "EE", qt_get_screen_ee, 0 }, +#endif + +*/ + +/* +Given a display_id (number of the \l{Qt for Embedded Linux} server to connect to) +and a spec (e.g. Mach64:/dev/fb0) return a QScreen-descendant. +The QScreenDriverFactory is queried for a suitable driver and, if found, +asked to create a driver. +People writing new graphics drivers should either hook their own +QScreen-descendant into QScreenDriverFactory or use the QScreenDriverPlugin +to make a dynamically loadable driver. +*/ + +Q_GUI_EXPORT QScreen* qt_get_screen(int display_id, const char *spec) +{ + QString displaySpec = QString::fromAscii(spec); + QString driver = displaySpec; + int colon = displaySpec.indexOf(QLatin1Char(':')); + if (colon >= 0) + driver.truncate(colon); + driver = driver.trimmed(); + + bool foundDriver = false; + QString driverName = driver; + + QStringList driverList; + if (!driver.isEmpty()) + driverList << driver; + else + driverList = QScreenDriverFactory::keys(); + + for (int i = 0; i < driverList.size(); ++i) { + const QString driverName = driverList.at(i); + qt_screen = QScreenDriverFactory::create(driverName, display_id); + if (qt_screen) { + foundDriver = true; + if (qt_screen->connect(displaySpec)) { + return qt_screen; + } else { + delete qt_screen; + qt_screen = 0; + } + } + } + + if (driver.isNull()) + qFatal("No suitable driver found"); + else if (foundDriver) + qFatal("%s: driver cannot connect", driver.toLatin1().constData()); + else + qFatal("%s: driver not found", driver.toLatin1().constData()); + + return 0; +} + +#ifndef QT_NO_QWS_CURSOR +static void blendCursor(QImage *dest, const QImage &cursor, const QPoint &offset) +{ + QRasterBuffer rb; + rb.prepare(dest); + + QSpanData spanData; + spanData.init(&rb, 0); + spanData.type = QSpanData::Texture; + spanData.initTexture(&cursor, 256); + spanData.dx = -offset.x(); + spanData.dy = -offset.y(); + if (!spanData.blend) + return; + + const QRect rect = QRect(offset, cursor.size()) + & QRect(QPoint(0, 0), dest->size()); + const int w = rect.width(); + const int h = rect.height(); + + QVarLengthArray<QT_FT_Span, 32> spans(h); + for (int i = 0; i < h; ++i) { + spans[i].x = rect.x(); + spans[i].len = w; + spans[i].y = rect.y() + i; + spans[i].coverage = 255; + } + spanData.blend(h, spans.constData(), &spanData); +} +#endif // QT_NO_QWS_CURSOR + +/*! + \fn void QScreen::exposeRegion(QRegion region, int windowIndex) + + This function is called by the \l{Qt for Embedded Linux} server whenever a + screen update is required. \a region is the area on the screen + that must be updated, and \a windowIndex is the index into + QWSServer::clientWindows() of the window that required the + update. QWSWindow::state() gives more information about the cause. + + The default implementation composes the + affected windows and paints the given \a region on screen by + calling the blit() and solidFill() functions + + This function can be reimplemented to perform composition in + hardware, or to perform transition effects. + For simpler hardware acceleration, or to interface with + this is typically done by reimplementing the blit() and + solidFill() functions instead. + + Note that there is no need to call this function explicitly. + + \sa blit(), solidFill(), blank() +*/ +void QScreen::exposeRegion(QRegion r, int windowIndex) +{ + r &= region(); + if (r.isEmpty()) + return; + + int changing = windowIndex; + // when we have just lowered a window, we have to expose all the windows below where the + // window used to be. + if (changing && qwsServer->clientWindows().at(changing)->state() == QWSWindow::Lowering) + changing = 0; +#ifdef QTOPIA_PERFTEST + static enum { PerfTestUnknown, PerfTestOn, PerfTestOff } perfTestState = PerfTestUnknown; + if(PerfTestUnknown == perfTestState) { + if(::getenv("QTOPIA_PERFTEST")) + perfTestState = PerfTestOn; + else + perfTestState = PerfTestOff; + } + if(PerfTestOn == perfTestState) { + QWSWindow *changed = qwsServer->clientWindows().at(changing); + if(!changed->client()->identity().isEmpty()) + qDebug() << "Performance : expose_region :" + << changed->client()->identity() + << r.boundingRect() << ": " + << qPrintable( QTime::currentTime().toString( "h:mm:ss.zzz" ) ); + } +#endif + + const QRect bounds = r.boundingRect(); + QRegion blendRegion; + QImage *blendBuffer = 0; + +#ifndef QT_NO_QWS_CURSOR + if (qt_screencursor && !qt_screencursor->isAccelerated()) { + blendRegion = r & qt_screencursor->boundingRect(); + } +#endif + compose(0, r, blendRegion, &blendBuffer, changing); + + if (blendBuffer) { + const QPoint offset = blendRegion.boundingRect().topLeft(); +#ifndef QT_NO_QWS_CURSOR + if (qt_screencursor && !qt_screencursor->isAccelerated()) { + const QRect cursorRect = qt_screencursor->boundingRect(); + if (blendRegion.intersects(cursorRect)) { + blendCursor(blendBuffer, qt_screencursor->image(), + cursorRect.topLeft() - offset); + } + } +#endif // QT_NO_QWS_CURSOR + blit(*blendBuffer, offset, blendRegion); + delete blendBuffer; + } + + if (r.numRects() == 1) { + setDirty(r.boundingRect()); + } else { + const QVector<QRect> rects = r.rects(); + for (int i = 0; i < rects.size(); ++i) + setDirty(rects.at(i)); + } +} + +/*! + \fn void QScreen::blit(const QImage &image, const QPoint &topLeft, const QRegion ®ion) + + Copies the given \a region in the given \a image to the point + specified by \a topLeft using device coordinates. + + This function is called from the exposeRegion() function; it is + not intended to be called explicitly. + + Reimplement this function to make use of \l{Adding an Accelerated + Graphics Driver to Qt for Embedded Linux}{accelerated hardware}. Note that + this function must be reimplemented if the framebuffer format is + not supported by \l{Qt for Embedded Linux} (See the + \l{Qt for Embedded Linux Display Management}{Display Management} + documentation for more details). + + \sa exposeRegion(), solidFill(), blank() +*/ +void QScreen::blit(const QImage &img, const QPoint &topLeft, const QRegion ®) +{ + const QRect bound = (region() & QRect(topLeft, img.size())).boundingRect(); + QWSDisplay::grab(); + d_ptr->blit(this, img, topLeft - offset(), + (reg & bound).translated(-topLeft)); + QWSDisplay::ungrab(); +} + +#ifdef QT_QWS_CLIENTBLIT +/*! + Returns true if this screen driver supports calling QScreen::blit() and + QScreen::setDirty() directly from non-server applications, otherwise returns + false. + + If available, this is used to optimize the performance of non-occluded, opaque + client windows by removing the server round trip when they are updated. + + \sa setSupportsBlitInClients() + */ +bool QScreen::supportsBlitInClients() const +{ + return d_ptr->supportsBlitInClients; +} + +/*! + If \a supported, the screen driver is marked as supporting blitting directly + from non-server applications. + + \sa supportsBlitInClients() + */ +void QScreen::setSupportsBlitInClients(bool supported) +{ + d_ptr->supportsBlitInClients = supported; +} +#endif + +/*! + \internal +*/ + +void QScreen::blit(QWSWindow *win, const QRegion &clip) +{ + QWSWindowSurface *surface = win->windowSurface(); + if (!surface) + return; + + const QImage &img = surface->image(); + if (img.isNull()) + return; + + const QRegion rgn = clip & win->paintedRegion(); + if (rgn.isEmpty()) + return; + + surface->lock(); + blit(img, win->requestedRegion().boundingRect().topLeft(), rgn); + surface->unlock(); +} + +struct fill_data { + quint32 color; + uchar *data; + int lineStep; + int x; + int y; + int w; + int h; +}; + +/*! + Fills the given \a region of the screen with the specified \a + color. + + This function is called from the exposeRegion() function; it is + not intended to be called explicitly. + + Reimplement this function to make use of \l{Adding an Accelerated + Graphics Driver to Qt for Embedded Linux}{accelerated hardware}. Note that + this function must be reimplemented if the framebuffer format is + not supported by \l{Qt for Embedded Linux} (See the + \l{Qt for Embedded Linux Display Management}{Display Management} + documentation for more details). + + \sa exposeRegion(), blit(), blank() +*/ +// the base class implementation works in device coordinates, so that transformed drivers can use it +void QScreen::solidFill(const QColor &color, const QRegion ®ion) +{ + QWSDisplay::grab(); + d_ptr->solidFill(this, color, + region.translated(-offset()) & QRect(0, 0, dw, dh)); + QWSDisplay::ungrab(); +} + +/*! + \since 4.2 + + Creates and returns a new window surface matching the given \a + key. + + The server application will call this function whenever it needs + to create a server side representation of a window, e.g. when + copying the content of memory to the screen using the screen + driver. + + Note that this function must be reimplemented when adding an + accelerated graphics driver. See the + \l{Adding an Accelerated Graphics Driver to Qt for Embedded Linux} + {Adding an Accelerated Graphics Driver} documentation for details. + + \sa {Qt for Embedded Linux Architecture} +*/ +QWSWindowSurface* QScreen::createSurface(const QString &key) const +{ +#ifndef QT_NO_PAINTONSCREEN + if (key == QLatin1String("OnScreen")) + return new QWSOnScreenSurface; + else +#endif + if (key == QLatin1String("mem")) + return new QWSLocalMemSurface; +#ifndef QT_NO_QWS_MULTIPROCESS + else if (key == QLatin1String("shm")) + return new QWSSharedMemSurface; +#endif +#ifndef QT_NO_PAINT_DEBUG + else if (key == QLatin1String("Yellow")) + return new QWSYellowSurface; +#endif +#ifndef QT_NO_DIRECTPAINTER + else if (key == QLatin1String("DirectPainter")) + return new QWSDirectPainterSurface; +#endif + + return 0; +} + +#ifndef QT_NO_PAINTONSCREEN +bool QScreen::isWidgetPaintOnScreen(const QWidget *w) +{ + static int doOnScreen = -1; + if (doOnScreen == -1) { + const QByteArray env = qgetenv("QT_ONSCREEN_PAINT"); + if (env == "force") + doOnScreen = 2; + else + doOnScreen = (env.toInt() > 0 ? 1 : 0); + } + + if (doOnScreen == 2) // force + return true; + + if (doOnScreen == 0 && !w->testAttribute(Qt::WA_PaintOnScreen)) + return false; + + return w->d_func()->isOpaque; +} +#endif + +/*! + \overload + + Creates and returns a new window surface for the given \a widget. +*/ +QWSWindowSurface* QScreen::createSurface(QWidget *widget) const +{ +#ifndef QT_NO_PAINTONSCREEN + if (isWidgetPaintOnScreen(widget) && base()) + return new QWSOnScreenSurface(widget); + else +#endif + if (QApplication::type() == QApplication::GuiServer) + return new QWSLocalMemSurface(widget); +#ifndef QT_NO_QWS_MULTIPROCESS + else + return new QWSSharedMemSurface(widget); +#endif + + return 0; +} + +void QScreen::compose(int level, const QRegion &exposed, QRegion &blend, + QImage **blendbuffer, int changing_level) +{ + QRect exposed_bounds = exposed.boundingRect(); + QWSWindow *win = 0; + do { + win = qwsServer->clientWindows().value(level); // null is background + ++level; + } while (win && !win->paintedRegion().boundingRect().intersects(exposed_bounds)); + + QWSWindowSurface *surface = (win ? win->windowSurface() : 0); + bool above_changing = level <= changing_level; // 0 is topmost + + QRegion exposedBelow = exposed; + bool opaque = true; + + if (win) { + opaque = win->isOpaque() || !surface->isBuffered(); + if (opaque) { + exposedBelow -= win->paintedRegion(); + if (above_changing || !surface->isBuffered()) + blend -= exposed & win->paintedRegion(); + } else { + blend += exposed & win->paintedRegion(); + } + } + if (win && !exposedBelow.isEmpty()) { + compose(level, exposedBelow, blend, blendbuffer, changing_level); + } else { + QSize blendSize = blend.boundingRect().size(); + if (!blendSize.isNull()) { + *blendbuffer = new QImage(blendSize, d_ptr->preferredImageFormat()); + } + } + + const QRegion blitRegion = exposed - blend; + if (!win) + paintBackground(blitRegion); + else if (!above_changing && surface->isBuffered()) + blit(win, blitRegion); + + QRegion blendRegion = exposed & blend; + + if (win) + blendRegion &= win->paintedRegion(); + if (!blendRegion.isEmpty()) { + + QPoint off = blend.boundingRect().topLeft(); + + QRasterBuffer rb; + rb.prepare(*blendbuffer); + QSpanData spanData; + spanData.init(&rb, 0); + if (!win) { + const QImage::Format format = (*blendbuffer)->format(); + switch (format) { + case QImage::Format_ARGB32_Premultiplied: + case QImage::Format_ARGB32: + case QImage::Format_ARGB8565_Premultiplied: + case QImage::Format_ARGB8555_Premultiplied: + case QImage::Format_ARGB6666_Premultiplied: + case QImage::Format_ARGB4444_Premultiplied: + spanData.rasterBuffer->compositionMode = QPainter::CompositionMode_Source; + break; + default: + break; + } + spanData.setup(qwsServer->backgroundBrush(), 256); + spanData.dx = off.x(); + spanData.dy = off.y(); + } else if (!surface->isBuffered()) { + return; + } else { + const QImage &img = surface->image(); + QPoint winoff = off - win->requestedRegion().boundingRect().topLeft(); + // convert win->opacity() from scale [0..255] to [0..256] + int const_alpha = win->opacity(); + const_alpha += (const_alpha >> 7); + spanData.type = QSpanData::Texture; + spanData.initTexture(&img, const_alpha); + spanData.dx = winoff.x(); + spanData.dy = winoff.y(); + } + if (!spanData.blend) + return; + + if (surface) + surface->lock(); + const QVector<QRect> rects = blendRegion.rects(); + const int nspans = 256; + QT_FT_Span spans[nspans]; + for (int i = 0; i < rects.size(); ++i) { + int y = rects.at(i).y() - off.y(); + int ye = y + rects.at(i).height(); + int x = rects.at(i).x() - off.x(); + int len = rects.at(i).width(); + while (y < ye) { + int n = qMin(nspans, ye - y); + int i = 0; + while (i < n) { + spans[i].x = x; + spans[i].len = len; + spans[i].y = y + i; + spans[i].coverage = 255; + ++i; + } + spanData.blend(n, spans, &spanData); + y += n; + } + } + if (surface) + surface->unlock(); + } +} + +void QScreen::paintBackground(const QRegion &r) +{ + const QBrush &bg = qwsServer->backgroundBrush(); + Qt::BrushStyle bs = bg.style(); + if (bs == Qt::NoBrush || r.isEmpty()) + return; + + if (bs == Qt::SolidPattern) { + solidFill(bg.color(), r); + } else { + const QRect br = r.boundingRect(); + QImage img(br.size(), d_ptr->preferredImageFormat()); + QPoint off = br.topLeft(); + QRasterBuffer rb; + rb.prepare(&img); + QSpanData spanData; + spanData.init(&rb, 0); + spanData.setup(bg, 256); + spanData.dx = off.x(); + spanData.dy = off.y(); + Q_ASSERT(spanData.blend); + + const QVector<QRect> rects = r.rects(); + const int nspans = 256; + QT_FT_Span spans[nspans]; + for (int i = 0; i < rects.size(); ++i) { + int y = rects.at(i).y() - off.y(); + int ye = y + rects.at(i).height(); + int x = rects.at(i).x() - off.x(); + int len = rects.at(i).width(); + while (y < ye) { + int n = qMin(nspans, ye - y); + int i = 0; + while (i < n) { + spans[i].x = x; + spans[i].len = len; + spans[i].y = y + i; + spans[i].coverage = 255; + ++i; + } + spanData.blend(n, spans, &spanData); + y += n; + } + } + blit(img, br.topLeft(), r); + } +} + +/*! + \fn virtual int QScreen::sharedRamSize(void *) + + \internal +*/ + +/*! + \fn QScreen::setDirty(const QRect& rectangle) + + Marks the given \a rectangle as dirty. + + Note that the default implementation does nothing; reimplement + this function to indicate that the given \a rectangle has been + altered. +*/ + +void QScreen::setDirty(const QRect&) +{ +} + +/*! + \fn QScreen::isTransformed() const + + Returns true if the screen is transformed (for instance, rotated + 90 degrees); otherwise returns false. + + \sa transformOrientation(), isInterlaced() +*/ + +bool QScreen::isTransformed() const +{ + return false; +} + +/*! + \fn QScreen::isInterlaced() const + + Returns true if the display is interlaced (i.e. is displaying + images progressively like a television screen); otherwise returns + false. + + If the display is interlaced, the drawing is altered to look + better. + + \sa isTransformed(), linestep() +*/ + +bool QScreen::isInterlaced() const +{ + return false;//qws_screen_is_interlaced;; +} + +/*! + \fn QScreen::mapToDevice(const QSize &size) const + + Maps the given \a size from the coordinate space used by the + application to the framebuffer coordinate system. Note that the + default implementation simply returns the given \a size as it is. + + Reimplement this function to use the given device's coordinate + system when mapping. + + \sa mapFromDevice() +*/ + +QSize QScreen::mapToDevice(const QSize &s) const +{ + return s; +} + +/*! + \fn QScreen::mapFromDevice(const QSize &size) const + + Maps the given \a size from the framebuffer coordinate system to + the coordinate space used by the application. Note that the + default implementation simply returns the given \a size as it is. + + Reimplement this function to use the given device's coordinate + system when mapping. + + \sa mapToDevice() +*/ + +QSize QScreen::mapFromDevice(const QSize &s) const +{ + return s; +} + +/*! + \fn QScreen::mapToDevice(const QPoint &point, const QSize &screenSize) const + \overload + + Maps the given \a point from the coordinate space used by the + application to the framebuffer coordinate system, passing the + device's \a screenSize as argument. Note that the default + implementation returns the given \a point as it is. +*/ + +QPoint QScreen::mapToDevice(const QPoint &p, const QSize &) const +{ + return p; +} + +/*! + \fn QScreen::mapFromDevice(const QPoint &point, const QSize &screenSize) const + \overload + + Maps the given \a point from the framebuffer coordinate system to + the coordinate space used by the application, passing the device's + \a screenSize as argument. Note that the default implementation + simply returns the given \a point as it is. +*/ + +QPoint QScreen::mapFromDevice(const QPoint &p, const QSize &) const +{ + return p; +} + +/*! + \fn QScreen::mapToDevice(const QRect &rectangle, const QSize &screenSize) const + \overload + + Maps the given \a rectangle from the coordinate space used by the + application to the framebuffer coordinate system, passing the + device's \a screenSize as argument. Note that the default + implementation returns the given \a rectangle as it is. +*/ + +QRect QScreen::mapToDevice(const QRect &r, const QSize &) const +{ + return r; +} + +/*! + \fn QScreen::mapFromDevice(const QRect &rectangle, const QSize &screenSize) const + \overload + + Maps the given \a rectangle from the framebuffer coordinate system to + the coordinate space used by the application, passing the device's + \a screenSize as argument. Note that the default implementation + simply returns the given \a rectangle as it is. +*/ + +QRect QScreen::mapFromDevice(const QRect &r, const QSize &) const +{ + return r; +} + +/*! + \fn QScreen::mapToDevice(const QImage &image) const + \overload + + Maps the given \a image from the coordinate space used by the + application to the framebuffer coordinate system. Note that the + default implementation returns the given \a image as it is. +*/ + +QImage QScreen::mapToDevice(const QImage &i) const +{ + return i; +} + +/*! + \fn QScreen::mapFromDevice(const QImage &image) const + \overload + + Maps the given \a image from the framebuffer coordinate system to + the coordinate space used by the application. Note that the + default implementation simply returns the given \a image as it is. +*/ + +QImage QScreen::mapFromDevice(const QImage &i) const +{ + return i; +} + +/*! + \fn QScreen::mapToDevice(const QRegion ®ion, const QSize &screenSize) const + \overload + + Maps the given \a region from the coordinate space used by the + application to the framebuffer coordinate system, passing the + device's \a screenSize as argument. Note that the default + implementation returns the given \a region as it is. +*/ + +QRegion QScreen::mapToDevice(const QRegion &r, const QSize &) const +{ + return r; +} + +/*! + \fn QScreen::mapFromDevice(const QRegion ®ion, const QSize &screenSize) const + \overload + + Maps the given \a region from the framebuffer coordinate system to + the coordinate space used by the application, passing the device's + \a screenSize as argument. Note that the default implementation + simply returns the given \a region as it is. +*/ + +QRegion QScreen::mapFromDevice(const QRegion &r, const QSize &) const +{ + return r; +} + +/*! + \fn QScreen::transformOrientation() const + + Returns the current rotation as an integer value. + + Note that the default implementation returns 0; reimplement this + function to override this value. + + \sa isTransformed() +*/ + +int QScreen::transformOrientation() const +{ + return 0; +} + +int QScreen::pixmapDepth() const +{ + return depth(); +} + +/*! + \internal +*/ +int QScreen::memoryNeeded(const QString&) +{ + return 0; +} + +/*! + \internal +*/ +void QScreen::haltUpdates() +{ +} + +/*! + \internal +*/ +void QScreen::resumeUpdates() +{ +} + +/*! + \fn QRegion QScreen::region() const + \since 4.2 + + Returns the region covered by this screen driver. + + \sa base(), screenSize() +*/ + +/*! + \internal +*/ +void QScreen::setOffset(const QPoint &p) +{ + d_ptr->offset = p; +} + +/*! + \since 4.2 + + Returns the logical offset of the screen, i.e., the offset between + (0,0) in screen coordinates and the application coordinate system. +*/ +QPoint QScreen::offset() const +{ + return d_ptr->offset; +} + +#if Q_BYTE_ORDER == Q_BIG_ENDIAN +void QScreen::setFrameBufferLittleEndian(bool littleEndian) +{ + d_ptr->fb_is_littleEndian = littleEndian; +} + +bool QScreen::frameBufferLittleEndian() const +{ + return d_ptr->fb_is_littleEndian; +} +#endif + +/*! + \fn int QScreen::subScreenIndexAt(const QPoint &position) const + \since 4.2 + + Returns the index of the subscreen at the given \a position; + returns -1 if no screen is found. + + The index identifies the subscreen in the list of pointers + returned by the subScreens() function. + + \sa instance(), subScreens() +*/ +int QScreen::subScreenIndexAt(const QPoint &p) const +{ + const QList<QScreen*> screens = subScreens(); + const int n = screens.count(); + for (int i = 0; i < n; ++i) { + if (screens.at(i)->region().contains(p)) + return i; + } + + return -1; +} + +#if 0 +#ifdef QT_LOADABLE_MODULES + +// ### needs update after driver init changes + +static QScreen * qt_dodriver(char * driver,char * a,unsigned char * b) + +{ + char buf[200]; + strcpy(buf,"/etc/qws/drivers/"); + qstrcpy(buf+17,driver); + qDebug("Attempting driver %s",driver); + + void * handle; + handle=dlopen(buf,RTLD_LAZY); + if(handle==0) { + qFatal("Module load error"); + } + QScreen *(*qt_get_screen_func)(char *,unsigned char *); + qt_get_screen_func=dlsym(handle,"qt_get_screen"); + if(qt_get_screen_func==0) { + qFatal("Couldn't get symbol"); + } + QScreen * ret=qt_get_screen_func(a,b); + return ret; +} + +static QScreen * qt_do_entry(char * entry) +{ + unsigned char config[256]; + + FILE * f=fopen(entry,"r"); + if(!f) { + return 0; + } + + int r=fread(config,256,1,f); + if(r<1) + return 0; + + fclose(f); + + unsigned short vendorid=*((unsigned short int *)config); + unsigned short deviceid=*(((unsigned short int *)config)+1); + if(config[0xb]!=3) + return 0; + + if(vendorid==0x1002) { + if(deviceid==0x4c4d) { + qDebug("Compaq Armada/IBM Thinkpad's Mach64 card"); + return qt_dodriver("mach64.so",entry,config); + } else if(deviceid==0x4742) { + qDebug("Desktop Rage Pro Mach64 card"); + return qt_dodriver("mach64.so",entry,config); + } else { + qDebug("Unrecognised ATI card id %x",deviceid); + return 0; + } + } else { + qDebug("Unrecognised vendor"); + } + return 0; +} + +extern bool qws_accel; + +/// ** NOT SUPPPORTED ** + +QScreen * qt_probe_bus() +{ + if(!qws_accel) { + return qt_dodriver("unaccel.so",0,0); + } + + DIR * dirptr=opendir("/proc/bus/pci"); + if(!dirptr) + return qt_dodriver("unaccel.so",0,0); + DIR * dirptr2; + dirent * cards; + + dirent * busses=readdir(dirptr); + + while(busses) { + if(busses->d_name[0]!='.') { + char buf[100]; + strcpy(buf,"/proc/bus/pci/"); + qstrcpy(buf+14,busses->d_name); + int p=strlen(buf); + dirptr2=opendir(buf); + if(dirptr2) { + cards=readdir(dirptr2); + while(cards) { + if(cards->d_name[0]!='.') { + buf[p]='/'; + qstrcpy(buf+p+1,cards->d_name); + QScreen * ret=qt_do_entry(buf); + if(ret) + return ret; + } + cards=readdir(dirptr2); + } + closedir(dirptr2); + } + } + busses=readdir(dirptr); + } + closedir(dirptr); + + return qt_dodriver("unaccel.so",0,0); +} + +#else + +char *qt_qws_hardcoded_slot = "/proc/bus/pci/01/00.0"; + +const unsigned char* qt_probe_bus() +{ + const char * slot; + slot=::getenv("QWS_CARD_SLOT"); + if(!slot) + slot=qt_qws_hardcoded_slot; + if (slot) { + static unsigned char config[256]; + FILE * f=fopen(slot,"r"); + if(!f) { + qDebug("Open failure for %s",slot); + slot=0; + } else { + int r=fread((char*)config,256,1,f); + fclose(f); + if(r<1) { + qDebug("Read failure"); + return 0; + } else { + return config; + } + } + } + return 0; +} + +#endif + +#endif // 0 + +/*! + \internal + \since 4.4 +*/ +void QScreen::setPixmapDataFactory(QPixmapDataFactory *factory) +{ + static bool shownWarning = false; + if (!shownWarning) { + qWarning("QScreen::setPixmapDataFactory() is deprecated - use setGraphicsSystem() instead"); + shownWarning = true; + } + + d_ptr->pixmapFactory = factory; +} + +/*! + \internal + \since 4.4 +*/ +QPixmapDataFactory* QScreen::pixmapDataFactory() const +{ + return d_ptr->pixmapFactory; +} + +/*! + \internal + \since 4.5 +*/ +void QScreen::setGraphicsSystem(QGraphicsSystem* system) +{ + d_ptr->graphicsSystem = system; +} + +/*! + \internal + \since 4.5 +*/ +QGraphicsSystem* QScreen::graphicsSystem() const +{ + return d_ptr->graphicsSystem; +} + +/*! + \since 4.4 + + Returns the class identifier for the screen object. +*/ +QScreen::ClassId QScreen::classId() const +{ + return static_cast<ClassId>(d_ptr->classId); +} + +QT_END_NAMESPACE diff --git a/src/gui/embedded/qscreen_qws.h b/src/gui/embedded/qscreen_qws.h new file mode 100644 index 0000000..e777317 --- /dev/null +++ b/src/gui/embedded/qscreen_qws.h @@ -0,0 +1,387 @@ +/**************************************************************************** +** +** 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$ +** +****************************************************************************/ + +#ifndef QSCREEN_QWS_H +#define QSCREEN_QWS_H + +#include <QtCore/qnamespace.h> +#include <QtCore/qpoint.h> +#include <QtCore/qlist.h> +#include <QtGui/qrgb.h> +#include <QtCore/qrect.h> +#include <QtGui/qimage.h> +#include <QtGui/qregion.h> + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +QT_MODULE(Gui) + +class QScreenCursor; +class QBrush; +class QWSWindow; +class QWSWindowSurface; +class QGraphicsSystem; +class QPixmapData; + +#ifndef QT_QWS_DEPTH16_RGB +#define QT_QWS_DEPTH16_RGB 565 +#endif +static const int qt_rbits = (QT_QWS_DEPTH16_RGB/100); +static const int qt_gbits = (QT_QWS_DEPTH16_RGB/10%10); +static const int qt_bbits = (QT_QWS_DEPTH16_RGB%10); +static const int qt_red_shift = qt_bbits+qt_gbits-(8-qt_rbits); +static const int qt_green_shift = qt_bbits-(8-qt_gbits); +static const int qt_neg_blue_shift = 8-qt_bbits; +static const int qt_blue_mask = (1<<qt_bbits)-1; +static const int qt_green_mask = (1<<(qt_gbits+qt_bbits))-(1<<qt_bbits); +static const int qt_red_mask = (1<<(qt_rbits+qt_gbits+qt_bbits))-(1<<(qt_gbits+qt_bbits)); + +static const int qt_red_rounding_shift = qt_red_shift + qt_rbits; +static const int qt_green_rounding_shift = qt_green_shift + qt_gbits; +static const int qt_blue_rounding_shift = qt_bbits - qt_neg_blue_shift; + + +inline ushort qt_convRgbTo16(const int r, const int g, const int b) +{ + const int tr = r << qt_red_shift; + const int tg = g << qt_green_shift; + const int tb = b >> qt_neg_blue_shift; + + return (tb & qt_blue_mask) | (tg & qt_green_mask) | (tr & qt_red_mask); +} + +inline ushort qt_convRgbTo16(QRgb c) +{ + const int tr = qRed(c) << qt_red_shift; + const int tg = qGreen(c) << qt_green_shift; + const int tb = qBlue(c) >> qt_neg_blue_shift; + + return (tb & qt_blue_mask) | (tg & qt_green_mask) | (tr & qt_red_mask); +} + +inline QRgb qt_conv16ToRgb(ushort c) +{ + const int r=(c & qt_red_mask); + const int g=(c & qt_green_mask); + const int b=(c & qt_blue_mask); + const int tr = r >> qt_red_shift | r >> qt_red_rounding_shift; + const int tg = g >> qt_green_shift | g >> qt_green_rounding_shift; + const int tb = b << qt_neg_blue_shift | b >> qt_blue_rounding_shift; + + return qRgb(tr,tg,tb); +} + +inline void qt_conv16ToRgb(ushort c, int& r, int& g, int& b) +{ + const int tr=(c & qt_red_mask); + const int tg=(c & qt_green_mask); + const int tb=(c & qt_blue_mask); + r = tr >> qt_red_shift | tr >> qt_red_rounding_shift; + g = tg >> qt_green_shift | tg >> qt_green_rounding_shift; + b = tb << qt_neg_blue_shift | tb >> qt_blue_rounding_shift; +} + +const int SourceSolid=0; +const int SourcePixmap=1; + +#ifndef QT_NO_QWS_CURSOR + +class QScreenCursor; +extern QScreenCursor *qt_screencursor; +extern bool qt_sw_cursor; + +class Q_GUI_EXPORT QScreenCursor +{ +public: + QScreenCursor(); + virtual ~QScreenCursor(); + + virtual void set(const QImage &image, int hotx, int hoty); + virtual void move(int x, int y); + virtual void show(); + virtual void hide(); + + bool supportsAlphaCursor() const { return supportsAlpha; } + + static bool enabled() { return qt_sw_cursor; } + + QRect boundingRect() const { return QRect(pos - hotspot, size); } + QImage image() const { return cursor; } + bool isVisible() const { return enable; } + bool isAccelerated() const { return hwaccel; } + + static void initSoftwareCursor(); + static QScreenCursor* instance() { return qt_screencursor; } + +protected: + QImage cursor; + + QSize size; + QPoint pos; + QPoint hotspot; + uint enable : 1; + uint hwaccel : 1; + uint supportsAlpha : 1; + +private: + friend class QProxyScreenCursor; +}; + +#endif // QT_NO_QWS_CURSOR + +struct fb_cmap; + +// A (used) chunk of offscreen memory + +class QPoolEntry +{ +public: + unsigned int start; + unsigned int end; + int clientId; +}; + +class QScreen; +class QScreenPrivate; +class QPixmapDataFactory; + +extern Q_GUI_EXPORT QScreen *qt_screen; +typedef void(*ClearCacheFunc)(QScreen *obj, int); + +class Q_GUI_EXPORT QScreen { + +public: + enum ClassId { LinuxFBClass, TransformedClass, VNCClass, MultiClass, + VFbClass, DirectFBClass, SvgalibClass, ProxyClass, + GLClass, CustomClass = 1024 }; + + QScreen(int display_id, ClassId classId); + explicit QScreen(int display_id); + virtual ~QScreen(); + static QScreen* instance() { return qt_screen; } + virtual bool initDevice() = 0; + virtual bool connect(const QString &displaySpec) = 0; + virtual void disconnect() = 0; + virtual void shutdownDevice(); + virtual void setMode(int,int,int) = 0; + virtual bool supportsDepth(int) const; + + virtual void save(); + virtual void restore(); + virtual void blank(bool on); + + virtual int pixmapOffsetAlignment() { return 64; } + virtual int pixmapLinestepAlignment() { return 64; } + virtual int sharedRamSize(void *) { return 0; } + + virtual bool onCard(const unsigned char *) const; + virtual bool onCard(const unsigned char *, ulong& out_offset) const; + + enum PixelType { NormalPixel, BGRPixel }; + + // sets a single color in the colormap + virtual void set(unsigned int,unsigned int,unsigned int,unsigned int); + // allocates a color + virtual int alloc(unsigned int,unsigned int,unsigned int); + + int width() const { return w; } + int height() const { return h; } + int depth() const { return d; } + virtual int pixmapDepth() const; + PixelType pixelType() const { return pixeltype; } + int linestep() const { return lstep; } + int deviceWidth() const { return dw; } + int deviceHeight() const { return dh; } + uchar * base() const { return data; } + // Ask for memory from card cache with alignment + virtual uchar * cache(int) { return 0; } + virtual void uncache(uchar *) {} + + QImage::Format pixelFormat() const; + + int screenSize() const { return size; } + int totalSize() const { return mapsize; } + + QRgb * clut() { return screenclut; } + int numCols() { return screencols; } + + virtual QSize mapToDevice(const QSize &) const; + virtual QSize mapFromDevice(const QSize &) const; + virtual QPoint mapToDevice(const QPoint &, const QSize &) const; + virtual QPoint mapFromDevice(const QPoint &, const QSize &) const; + virtual QRect mapToDevice(const QRect &, const QSize &) const; + virtual QRect mapFromDevice(const QRect &, const QSize &) const; + virtual QImage mapToDevice(const QImage &) const; + virtual QImage mapFromDevice(const QImage &) const; + virtual QRegion mapToDevice(const QRegion &, const QSize &) const; + virtual QRegion mapFromDevice(const QRegion &, const QSize &) const; + virtual int transformOrientation() const; + virtual bool isTransformed() const; + virtual bool isInterlaced() const; + + virtual void setDirty(const QRect&); + + virtual int memoryNeeded(const QString&); + + virtual void haltUpdates(); + virtual void resumeUpdates(); + + // composition manager methods + virtual void exposeRegion(QRegion r, int changing); + + // these work directly on the screen + virtual void blit(const QImage &img, const QPoint &topLeft, const QRegion ®ion); + virtual void solidFill(const QColor &color, const QRegion ®ion); + void blit(QWSWindow *bs, const QRegion &clip); + + virtual QWSWindowSurface* createSurface(QWidget *widget) const; + virtual QWSWindowSurface* createSurface(const QString &key) const; + + virtual QList<QScreen*> subScreens() const { return QList<QScreen*>(); } + virtual QRegion region() const { return QRect(offset(), QSize(w, h)); } + int subScreenIndexAt(const QPoint &p) const; + + void setOffset(const QPoint &p); + QPoint offset() const; + + int physicalWidth() const { return physWidth; } // physical display size in mm + int physicalHeight() const { return physHeight; } // physical display size in mm + + QPixmapDataFactory* pixmapDataFactory() const; // Deprecated, will be removed in 4.6 + QGraphicsSystem* graphicsSystem() const; + +#ifdef QT_QWS_CLIENTBLIT + bool supportsBlitInClients() const; + void setSupportsBlitInClients(bool); +#endif + + ClassId classId() const; + +protected: + void setPixelFormat(QImage::Format format); + void setPixmapDataFactory(QPixmapDataFactory *factory); // Deprecated, will be removed in 4.6 + void setGraphicsSystem(QGraphicsSystem* system); + + QRgb screenclut[256]; + int screencols; + + uchar * data; + + // Table of allocated lumps, kept in sorted highest-to-lowest order + // The table itself is allocated at the bottom of offscreen memory + // i.e. it's similar to having a stack (the table) and a heap + // (the allocated blocks). Freed space is implicitly described + // by the gaps between the allocated lumps (this saves entries and + // means we don't need to worry about coalescing freed lumps) + + QPoolEntry * entries; + int * entryp; + unsigned int * lowest; + + int w; + int lstep; + int h; + int d; + PixelType pixeltype; + bool grayscale; + + int dw; + int dh; + + int size; // Screen size + int mapsize; // Total mapped memory + + int displayId; + + int physWidth; + int physHeight; + + friend class QWSServer; + friend class QWSServerPrivate; + static ClearCacheFunc clearCacheFunc; + +private: + void compose(int level, const QRegion &exposed, QRegion &blend, + QImage **blendbuffer, int changing_level); + void paintBackground(const QRegion &); + + friend class QWSOnScreenSurface; + static bool isWidgetPaintOnScreen(const QWidget *w); + +#if Q_BYTE_ORDER == Q_BIG_ENDIAN + void setFrameBufferLittleEndian(bool littleEndian); + bool frameBufferLittleEndian() const; + friend class QVNCScreen; + friend class QLinuxFbScreen; + friend class QVFbScreen; + friend class QProxyScreen; +#endif + friend void qt_solidFill_setup(QScreen*, const QColor&, const QRegion&); + friend void qt_blit_setup(QScreen *screen, const QImage &image, + const QPoint &topLeft, const QRegion ®ion); +#ifdef QT_QWS_DEPTH_GENERIC + friend void qt_set_generic_blit(QScreen *screen, int bpp, + int len_red, int len_green, int len_blue, + int len_alpha, int off_red, int off_green, + int off_blue, int off_alpha); +#endif + + QScreenPrivate *d_ptr; +}; + +// This lives in loadable modules + +#ifndef QT_LOADABLE_MODULES +extern "C" QScreen * qt_get_screen(int display_id, const char* spec); +#endif + +// This is in main lib, loads the right module, calls qt_get_screen +// In non-loadable cases just aliases to qt_get_screen + +const unsigned char * qt_probe_bus(); + +QT_END_NAMESPACE + +QT_END_HEADER + +#endif // QSCREEN_QWS_H diff --git a/src/gui/embedded/qscreendriverfactory_qws.cpp b/src/gui/embedded/qscreendriverfactory_qws.cpp new file mode 100644 index 0000000..8b17fba --- /dev/null +++ b/src/gui/embedded/qscreendriverfactory_qws.cpp @@ -0,0 +1,183 @@ +/**************************************************************************** +** +** 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 "qscreendriverfactory_qws.h" + +#include "qscreen_qws.h" +#include "qapplication.h" +#include "qscreenlinuxfb_qws.h" +#include "qscreentransformed_qws.h" +#include "qscreenvfb_qws.h" +#include "qscreenmulti_qws_p.h" +#include <stdlib.h> +#include "private/qfactoryloader_p.h" +#include "qscreendriverplugin_qws.h" + +#ifndef QT_NO_QWS_VNC +#include "qscreenvnc_qws.h" +#endif + +QT_BEGIN_NAMESPACE + +#if !defined(Q_OS_WIN32) || defined(QT_MAKEDLL) +#ifndef QT_NO_LIBRARY + +Q_GLOBAL_STATIC_WITH_ARGS(QFactoryLoader, loader, + (QScreenDriverFactoryInterface_iid, + QLatin1String("/gfxdrivers"), Qt::CaseInsensitive)) + +#endif //QT_NO_LIBRARY +#endif //QT_MAKEDLL + +/*! + \class QScreenDriverFactory + \ingroup qws + + \brief The QScreenDriverFactory class creates screen drivers in + Qt for Embedded Linux. + + Note that this class is only available in \l{Qt for Embedded Linux}. + + QScreenDriverFactory is used to detect and instantiate the + available screen drivers, allowing \l{Qt for Embedded Linux} to load the + preferred driver into the server application at runtime. The + create() function returns a QScreen object representing the screen + driver identified by a given key. The valid keys (i.e. the + supported drivers) can be retrieved using the keys() function. + + + \l{Qt for Embedded Linux} provides several built-in screen drivers. In + addition, custom screen drivers can be added using Qt's plugin + mechanism, i.e. by subclassing the QScreen class and creating a + screen driver plugin (QScreenDriverPlugin). See the + \l{Qt for Embedded Linux Display Management}{display management} + documentation for details. + + \sa QScreen, QScreenDriverPlugin +*/ + +/*! + Creates the screen driver specified by the given \a key, using the + display specified by the given \a displayId. + + Note that the keys are case-insensitive. + + \sa keys() +*/ +QScreen *QScreenDriverFactory::create(const QString& key, int displayId) +{ + QString driver = key.toLower(); +#ifndef QT_NO_QWS_QVFB + if (driver == QLatin1String("qvfb") || driver.isEmpty()) + return new QVFbScreen(displayId); +#endif +#ifndef QT_NO_QWS_LINUXFB + if (driver == QLatin1String("linuxfb") || driver.isEmpty()) + return new QLinuxFbScreen(displayId); +#endif +#ifndef QT_NO_QWS_TRANSFORMED + if (driver == QLatin1String("transformed")) + return new QTransformedScreen(displayId); +#endif +#ifndef QT_NO_QWS_VNC + if (driver == QLatin1String("vnc")) + return new QVNCScreen(displayId); +#endif +#ifndef QT_NO_QWS_MULTISCREEN + if (driver == QLatin1String("multi")) + return new QMultiScreen(displayId); +#endif + +#if !defined(Q_OS_WIN32) || defined(QT_MAKEDLL) +#ifndef QT_NO_LIBRARY + + if (QScreenDriverFactoryInterface *factory = qobject_cast<QScreenDriverFactoryInterface*>(loader()->instance(key))) + return factory->create(driver, displayId); + +#endif +#endif + return 0; +} + +/*! + Returns the list of valid keys, i.e. the available screen drivers. + + \sa create() +*/ +QStringList QScreenDriverFactory::keys() +{ + QStringList list; + +#ifndef QT_NO_QWS_QVFB + list << QLatin1String("QVFb"); +#endif +#ifndef QT_NO_QWS_LINUXFB + list << QLatin1String("LinuxFb"); +#endif +#ifndef QT_NO_QWS_TRANSFORMED + list << QLatin1String("Transformed"); +#endif +#ifndef QT_NO_QWS_VNC + list << QLatin1String("VNC"); +#endif +#ifndef QT_NO_QWS_MULTISCREEN + list << QLatin1String("Multi"); +#endif + +#if !defined(Q_OS_WIN32) || defined(QT_MAKEDLL) +#ifndef QT_NO_LIBRARY + QStringList plugins = loader()->keys(); + for (int i = 0; i < plugins.size(); ++i) { +# ifdef QT_NO_QWS_QVFB + // give QVFb top priority for autodetection + if (plugins.at(i) == QLatin1String("QVFb")) + list.prepend(plugins.at(i)); + else +# endif + if (!list.contains(plugins.at(i))) + list += plugins.at(i); + } +#endif //QT_NO_LIBRARY +#endif //QT_MAKEDLL + return list; +} + +QT_END_NAMESPACE diff --git a/src/gui/embedded/qscreendriverfactory_qws.h b/src/gui/embedded/qscreendriverfactory_qws.h new file mode 100644 index 0000000..eb9364c --- /dev/null +++ b/src/gui/embedded/qscreendriverfactory_qws.h @@ -0,0 +1,67 @@ +/**************************************************************************** +** +** 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$ +** +****************************************************************************/ + +#ifndef QSCREENDRIVERFACTORY_QWS_H +#define QSCREENDRIVERFACTORY_QWS_H + +#include <QtCore/qstringlist.h> + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +QT_MODULE(Gui) + +class QString; +class QScreen; + +class Q_GUI_EXPORT QScreenDriverFactory +{ +public: + static QStringList keys(); + static QScreen *create(const QString&, int); +}; + +QT_END_NAMESPACE + +QT_END_HEADER + +#endif // QSCREENDRIVERFACTORY_QWS_H diff --git a/src/gui/embedded/qscreendriverplugin_qws.cpp b/src/gui/embedded/qscreendriverplugin_qws.cpp new file mode 100644 index 0000000..5429bde --- /dev/null +++ b/src/gui/embedded/qscreendriverplugin_qws.cpp @@ -0,0 +1,123 @@ +/**************************************************************************** +** +** 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 "qscreendriverplugin_qws.h" + +QT_BEGIN_NAMESPACE + +#ifndef QT_NO_LIBRARY + +/*! + \class QScreenDriverPlugin + \ingroup plugins + \ingroup qws + + \brief The QScreenDriverPlugin class is an abstract base class for + screen driver plugins in Qt for Embedded Linux. + + Note that this class is only available in \l{Qt for Embedded Linux}. + + \l{Qt for Embedded Linux} provides ready-made drivers for several screen + protocols, see the \l{Qt for Embedded Linux Display Management}{display + management} documentation for details. Custom screen drivers can be + implemented by subclassing the QScreen class and creating a screen + driver plugin. + + A screen driver plugin can be created by subclassing + QScreenDriverPlugin and reimplementing the pure virtual keys() and + create() functions. By exporting the derived class using the + Q_EXPORT_PLUGIN2() macro, The default implementation of the + QScreenDriverFactory class will automatically detect the plugin + and load the driver into the server application at run-time. See + \l{How to Create Qt Plugins} for details. + + \sa QScreen, QScreenDriverFactory +*/ + +/*! + \fn QStringList QScreenDriverPlugin::keys() const + + Implement this function to return the list of valid keys, i.e. the + screen drivers supported by this plugin. + + \l{Qt for Embedded Linux} provides ready-made drivers for several screen + protocols, see the \l{Qt for Embedded Linux Display Management}{display + management} documentation for details. + + \sa create() +*/ + +/*! + Constructs a screen driver plugin with the given \a parent. + + Note that this constructor is invoked automatically by the + Q_EXPORT_PLUGIN2() macro, so there is no need for calling it + explicitly. +*/ +QScreenDriverPlugin::QScreenDriverPlugin(QObject *parent) + : QObject(parent) +{ +} + +/*! + Destroys this screen driver plugin. + + Note that Qt destroys a plugin automatically when it is no longer + used, so there is no need for calling the destructor explicitly. +*/ +QScreenDriverPlugin::~QScreenDriverPlugin() +{ +} + + +/*! + \fn QScreen* QScreenDriverPlugin::create(const QString &key, int displayId) + + Implement this function to create a driver matching the type + specified by the given \a key and \a displayId parameters. Note + that keys are case-insensitive. + + \sa keys() +*/ + +#endif // QT_NO_LIBRARY + +QT_END_NAMESPACE diff --git a/src/gui/embedded/qscreendriverplugin_qws.h b/src/gui/embedded/qscreendriverplugin_qws.h new file mode 100644 index 0000000..f7dd0fc --- /dev/null +++ b/src/gui/embedded/qscreendriverplugin_qws.h @@ -0,0 +1,84 @@ +/**************************************************************************** +** +** 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$ +** +****************************************************************************/ + +#ifndef QSCREENDRIVERPLUGIN_QWS_H +#define QSCREENDRIVERPLUGIN_QWS_H + +#include <QtCore/qplugin.h> +#include <QtCore/qfactoryinterface.h> + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +QT_MODULE(Gui) + +#ifndef QT_NO_LIBRARY + +class QScreen; + +struct Q_GUI_EXPORT QScreenDriverFactoryInterface : public QFactoryInterface +{ + virtual QScreen* create(const QString& driver, int displayId) = 0; +}; + +#define QScreenDriverFactoryInterface_iid "com.trolltech.Qt.QScreenDriverFactoryInterface" +Q_DECLARE_INTERFACE(QScreenDriverFactoryInterface, QScreenDriverFactoryInterface_iid) + +class Q_GUI_EXPORT QScreenDriverPlugin : public QObject, public QScreenDriverFactoryInterface +{ + Q_OBJECT + Q_INTERFACES(QScreenDriverFactoryInterface:QFactoryInterface) +public: + explicit QScreenDriverPlugin(QObject *parent = 0); + ~QScreenDriverPlugin(); + + virtual QStringList keys() const = 0; + virtual QScreen *create(const QString& driver, int displayId) = 0; +}; + +#endif // QT_NO_LIBRARY + +QT_END_NAMESPACE + +QT_END_HEADER + +#endif // QSCREENDRIVERPLUGIN_QWS_H diff --git a/src/gui/embedded/qscreenlinuxfb_qws.cpp b/src/gui/embedded/qscreenlinuxfb_qws.cpp new file mode 100644 index 0000000..fed7aad --- /dev/null +++ b/src/gui/embedded/qscreenlinuxfb_qws.cpp @@ -0,0 +1,1314 @@ +/**************************************************************************** +** +** 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 "qscreenlinuxfb_qws.h" + +#ifndef QT_NO_QWS_LINUXFB +//#include "qmemorymanager_qws.h" +#include "qwsdisplay_qws.h" +#include "qpixmap.h" +#include <private/qwssignalhandler_p.h> + +#include <unistd.h> +#include <stdlib.h> +#include <sys/ioctl.h> +#include <sys/types.h> +#include <sys/stat.h> +#include <sys/mman.h> +#include <sys/kd.h> +#include <fcntl.h> +#include <errno.h> +#include <stdio.h> +#include <limits.h> +#include <signal.h> + +#include "qwindowsystem_qws.h" + +#if !defined(Q_OS_DARWIN) && !defined(Q_OS_FREEBSD) +#include <linux/fb.h> + +#ifdef __i386__ +#include <asm/mtrr.h> +#endif +#endif + +QT_BEGIN_NAMESPACE + +extern int qws_client_id; + +//#define DEBUG_CACHE + +class QLinuxFbScreenPrivate : public QObject +{ +public: + QLinuxFbScreenPrivate(); + ~QLinuxFbScreenPrivate(); + + void openTty(); + void closeTty(); + + int fd; + int startupw; + int startuph; + int startupd; + bool blank; + + bool doGraphicsMode; +#ifdef QT_QWS_DEPTH_GENERIC + bool doGenericColors; +#endif + int ttyfd; + long oldKdMode; + QString ttyDevice; + QString displaySpec; +}; + +QLinuxFbScreenPrivate::QLinuxFbScreenPrivate() + : fd(-1), blank(true), doGraphicsMode(true), +#ifdef QT_QWS_DEPTH_GENERIC + doGenericColors(false), +#endif + ttyfd(-1), oldKdMode(KD_TEXT) +{ + QWSSignalHandler::instance()->addObject(this); +} + +QLinuxFbScreenPrivate::~QLinuxFbScreenPrivate() +{ + closeTty(); +} + +void QLinuxFbScreenPrivate::openTty() +{ + const char *const devs[] = {"/dev/tty0", "/dev/tty", "/dev/console", 0}; + + if (ttyDevice.isEmpty()) { + for (const char * const *dev = devs; *dev; ++dev) { + ttyfd = ::open(*dev, O_RDWR); + if (ttyfd != -1) + break; + } + } else { + ttyfd = ::open(ttyDevice.toAscii().constData(), O_RDWR); + } + + if (ttyfd == -1) + return; + + if (doGraphicsMode) { + ioctl(ttyfd, KDGETMODE, &oldKdMode); + if (oldKdMode != KD_GRAPHICS) { + int ret = ioctl(ttyfd, KDSETMODE, KD_GRAPHICS); + if (ret == -1) + doGraphicsMode = false; + } + } + + // No blankin' screen, no blinkin' cursor!, no cursor! + const char termctl[] = "\033[9;0]\033[?33l\033[?25l\033[?1c"; + ::write(ttyfd, termctl, sizeof(termctl)); +} + +void QLinuxFbScreenPrivate::closeTty() +{ + if (ttyfd == -1) + return; + + if (doGraphicsMode) + ioctl(ttyfd, KDSETMODE, oldKdMode); + + // Blankin' screen, blinkin' cursor! + const char termctl[] = "\033[9;15]\033[?33h\033[?25h\033[?0c"; + ::write(ttyfd, termctl, sizeof(termctl)); + + ::close(ttyfd); + ttyfd = -1; +} + +/*! + \internal + + \class QLinuxFbScreen + \ingroup qws + + \brief The QLinuxFbScreen class implements a screen driver for the + Linux framebuffer. + + Note that this class is only available in \l{Qt for Embedded Linux}. + Custom screen drivers can be added by subclassing the + QScreenDriverPlugin class, using the QScreenDriverFactory class to + dynamically load the driver into the application, but there should + only be one screen object per application. + + The QLinuxFbScreen class provides the cache() function allocating + off-screen graphics memory, and the complementary uncache() + function releasing the allocated memory. The latter function will + first sync the graphics card to ensure the memory isn't still + being used by a command in the graphics card FIFO queue. The + deleteEntry() function deletes the given memory block without such + synchronization. Given the screen instance and client id, the + memory can also be released using the clearCache() function, but + this should only be necessary if a client exits abnormally. + + In addition, when in paletted graphics modes, the set() function + provides the possibility of setting a specified color index to a + given RGB value. + + The QLinuxFbScreen class also acts as a factory for the + unaccelerated screen cursor and the unaccelerated raster-based + implementation of QPaintEngine (\c QRasterPaintEngine); + accelerated drivers for Linux should derive from this class. + + \sa QScreen, QScreenDriverPlugin, {Running Applications} +*/ + +/*! + \fn bool QLinuxFbScreen::useOffscreen() + \internal +*/ + +// Unaccelerated screen/driver setup. Can be overridden by accelerated +// drivers + +/*! + \fn QLinuxFbScreen::QLinuxFbScreen(int displayId) + + Constructs a QLinuxFbScreen object. The \a displayId argument + identifies the Qt for Embedded Linux server to connect to. +*/ + +QLinuxFbScreen::QLinuxFbScreen(int display_id) + : QScreen(display_id, LinuxFBClass), d_ptr(new QLinuxFbScreenPrivate) +{ + canaccel=false; + clearCacheFunc = &clearCache; +#ifdef QT_QWS_CLIENTBLIT + setSupportsBlitInClients(true); +#endif +} + +/*! + Destroys this QLinuxFbScreen object. +*/ + +QLinuxFbScreen::~QLinuxFbScreen() +{ +} + +/*! + \reimp + + This is called by \l{Qt for Embedded Linux} clients to map in the framebuffer. + It should be reimplemented by accelerated drivers to map in + graphics card registers; those drivers should then call this + function in order to set up offscreen memory management. The + device is specified in \a displaySpec; e.g. "/dev/fb". + + \sa disconnect() +*/ + +bool QLinuxFbScreen::connect(const QString &displaySpec) +{ + d_ptr->displaySpec = displaySpec; + + const QStringList args = displaySpec.split(QLatin1Char(':')); + + if (args.contains(QLatin1String("nographicsmodeswitch"))) + d_ptr->doGraphicsMode = false; + +#ifdef QT_QWS_DEPTH_GENERIC + if (args.contains(QLatin1String("genericcolors"))) + d_ptr->doGenericColors = true; +#endif + + QRegExp ttyRegExp(QLatin1String("tty=(.*)")); + if (args.indexOf(ttyRegExp) != -1) + d_ptr->ttyDevice = ttyRegExp.cap(1); + +#if Q_BYTE_ORDER == Q_BIG_ENDIAN +#ifndef QT_QWS_FRAMEBUFFER_LITTLE_ENDIAN + if (args.contains(QLatin1String("littleendian"))) +#endif + QScreen::setFrameBufferLittleEndian(true); +#endif + + // Check for explicitly specified device + const int len = 8; // "/dev/fbx" + int m = displaySpec.indexOf(QLatin1String("/dev/fb")); + + QString dev; + if (m > 0) + dev = displaySpec.mid(m, len); + else + dev = QLatin1String("/dev/fb0"); + + if (access(dev.toLatin1().constData(), R_OK|W_OK) == 0) + d_ptr->fd = open(dev.toLatin1().constData(), O_RDWR); + if (d_ptr->fd == -1) { + if (QApplication::type() == QApplication::GuiServer) { + perror("QScreenLinuxFb::connect"); + qCritical("Error opening framebuffer device %s", qPrintable(dev)); + return false; + } + if (access(dev.toLatin1().constData(), R_OK) == 0) + d_ptr->fd = open(dev.toLatin1().constData(), O_RDONLY); + } + + fb_fix_screeninfo finfo; + fb_var_screeninfo vinfo; + //####################### + // Shut up Valgrind + memset(&vinfo, 0, sizeof(vinfo)); + memset(&finfo, 0, sizeof(finfo)); + //####################### + + /* Get fixed screen information */ + if (d_ptr->fd != -1 && ioctl(d_ptr->fd, FBIOGET_FSCREENINFO, &finfo)) { + perror("QLinuxFbScreen::connect"); + qWarning("Error reading fixed information"); + return false; + } + + if (finfo.type == FB_TYPE_VGA_PLANES) { + qWarning("VGA16 video mode not supported"); + return false; + } + + /* Get variable screen information */ + if (d_ptr->fd != -1 && ioctl(d_ptr->fd, FBIOGET_VSCREENINFO, &vinfo)) { + perror("QLinuxFbScreen::connect"); + qWarning("Error reading variable information"); + return false; + } + + grayscale = vinfo.grayscale; + d = vinfo.bits_per_pixel; + if (d == 24) { + d = vinfo.red.length + vinfo.green.length + vinfo.blue.length; + if (d <= 0) + d = 24; // reset if color component lengths are not reported + } else if (d == 16) { + d = vinfo.red.length + vinfo.green.length + vinfo.blue.length; + if (d <= 0) + d = 16; + } + lstep = finfo.line_length; + + int xoff = vinfo.xoffset; + int yoff = vinfo.yoffset; + const char* qwssize; + if((qwssize=::getenv("QWS_SIZE")) && sscanf(qwssize,"%dx%d",&w,&h)==2) { + if (d_ptr->fd != -1) { + if ((uint)w > vinfo.xres) w = vinfo.xres; + if ((uint)h > vinfo.yres) h = vinfo.yres; + } + dw=w; + dh=h; + xoff += (vinfo.xres - w)/2; + yoff += (vinfo.yres - h)/2; + } else { + dw=w=vinfo.xres; + dh=h=vinfo.yres; + } + + if (w == 0 || h == 0) { + qWarning("QScreenLinuxFb::connect(): Unable to find screen geometry, " + "will use 320x240."); + dw = w = 320; + dh = h = 240; + } + + setPixelFormat(vinfo); + + // Handle display physical size spec. + QStringList displayArgs = displaySpec.split(QLatin1Char(':')); + QRegExp mmWidthRx(QLatin1String("mmWidth=?(\\d+)")); + int dimIdxW = displayArgs.indexOf(mmWidthRx); + QRegExp mmHeightRx(QLatin1String("mmHeight=?(\\d+)")); + int dimIdxH = displayArgs.indexOf(mmHeightRx); + if (dimIdxW >= 0) { + mmWidthRx.exactMatch(displayArgs.at(dimIdxW)); + physWidth = mmWidthRx.cap(1).toInt(); + if (dimIdxH < 0) + physHeight = dh*physWidth/dw; + } + if (dimIdxH >= 0) { + mmHeightRx.exactMatch(displayArgs.at(dimIdxH)); + physHeight = mmHeightRx.cap(1).toInt(); + if (dimIdxW < 0) + physWidth = dw*physHeight/dh; + } + if (dimIdxW < 0 && dimIdxH < 0) { + if (vinfo.width != 0 && vinfo.height != 0 + && vinfo.width != UINT_MAX && vinfo.height != UINT_MAX) { + physWidth = vinfo.width; + physHeight = vinfo.height; + } else { + const int dpi = 72; + physWidth = qRound(dw * 25.4 / dpi); + physHeight = qRound(dh * 25.4 / dpi); + } + } + + dataoffset = yoff * lstep + xoff * d / 8; + //qDebug("Using %dx%dx%d screen",w,h,d); + + /* Figure out the size of the screen in bytes */ + size = h * lstep; + + mapsize = finfo.smem_len; + + data = (unsigned char *)-1; + if (d_ptr->fd != -1) + data = (unsigned char *)mmap(0, mapsize, PROT_READ | PROT_WRITE, + MAP_SHARED, d_ptr->fd, 0); + + if ((long)data == -1) { + if (QApplication::type() == QApplication::GuiServer) { + perror("QLinuxFbScreen::connect"); + qWarning("Error: failed to map framebuffer device to memory."); + return false; + } + data = 0; + } else { + data += dataoffset; + } + + canaccel = useOffscreen(); + if(canaccel) + setupOffScreen(); + + // Now read in palette + if((vinfo.bits_per_pixel==8) || (vinfo.bits_per_pixel==4)) { + screencols= (vinfo.bits_per_pixel==8) ? 256 : 16; + int loopc; + fb_cmap startcmap; + startcmap.start=0; + startcmap.len=screencols; + startcmap.red=(unsigned short int *) + malloc(sizeof(unsigned short int)*screencols); + startcmap.green=(unsigned short int *) + malloc(sizeof(unsigned short int)*screencols); + startcmap.blue=(unsigned short int *) + malloc(sizeof(unsigned short int)*screencols); + startcmap.transp=(unsigned short int *) + malloc(sizeof(unsigned short int)*screencols); + if (d_ptr->fd == -1 || ioctl(d_ptr->fd, FBIOGETCMAP, &startcmap)) { + perror("QLinuxFbScreen::connect"); + qWarning("Error reading palette from framebuffer, using default palette"); + createPalette(startcmap, vinfo, finfo); + } + int bits_used = 0; + for(loopc=0;loopc<screencols;loopc++) { + screenclut[loopc]=qRgb(startcmap.red[loopc] >> 8, + startcmap.green[loopc] >> 8, + startcmap.blue[loopc] >> 8); + bits_used |= startcmap.red[loopc] + | startcmap.green[loopc] + | startcmap.blue[loopc]; + } + // WORKAROUND: Some framebuffer drivers only return 8 bit + // color values, so we need to not bit shift them.. + if ((bits_used & 0x00ff) && !(bits_used & 0xff00)) { + for(loopc=0;loopc<screencols;loopc++) { + screenclut[loopc] = qRgb(startcmap.red[loopc], + startcmap.green[loopc], + startcmap.blue[loopc]); + } + qWarning("8 bits cmap returned due to faulty FB driver, colors corrected"); + } + free(startcmap.red); + free(startcmap.green); + free(startcmap.blue); + free(startcmap.transp); + } else { + screencols=0; + } + + return true; +} + +/*! + \reimp + + This unmaps the framebuffer. + + \sa connect() +*/ + +void QLinuxFbScreen::disconnect() +{ + data -= dataoffset; + if (data) + munmap((char*)data,mapsize); + close(d_ptr->fd); +} + +// #define DEBUG_VINFO + +void QLinuxFbScreen::createPalette(fb_cmap &cmap, fb_var_screeninfo &vinfo, fb_fix_screeninfo &finfo) +{ + if((vinfo.bits_per_pixel==8) || (vinfo.bits_per_pixel==4)) { + screencols= (vinfo.bits_per_pixel==8) ? 256 : 16; + cmap.start=0; + cmap.len=screencols; + cmap.red=(unsigned short int *) + malloc(sizeof(unsigned short int)*screencols); + cmap.green=(unsigned short int *) + malloc(sizeof(unsigned short int)*screencols); + cmap.blue=(unsigned short int *) + malloc(sizeof(unsigned short int)*screencols); + cmap.transp=(unsigned short int *) + malloc(sizeof(unsigned short int)*screencols); + + if (screencols==16) { + if (finfo.type == FB_TYPE_PACKED_PIXELS) { + // We'll setup a grayscale cmap for 4bpp linear + int val = 0; + for (int idx = 0; idx < 16; ++idx, val += 17) { + cmap.red[idx] = (val<<8)|val; + cmap.green[idx] = (val<<8)|val; + cmap.blue[idx] = (val<<8)|val; + screenclut[idx]=qRgb(val, val, val); + } + } else { + // Default 16 colour palette + // Green is now trolltech green so certain images look nicer + // black d_gray l_gray white red green blue cyan magenta yellow + unsigned char reds[16] = { 0x00, 0x7F, 0xBF, 0xFF, 0xFF, 0xA2, 0x00, 0xFF, 0xFF, 0x00, 0x7F, 0x7F, 0x00, 0x00, 0x00, 0x82 }; + unsigned char greens[16] = { 0x00, 0x7F, 0xBF, 0xFF, 0x00, 0xC5, 0x00, 0xFF, 0x00, 0xFF, 0x00, 0x00, 0x00, 0x7F, 0x7F, 0x7F }; + unsigned char blues[16] = { 0x00, 0x7F, 0xBF, 0xFF, 0x00, 0x11, 0xFF, 0x00, 0xFF, 0xFF, 0x00, 0x7F, 0x7F, 0x7F, 0x00, 0x00 }; + + for (int idx = 0; idx < 16; ++idx) { + cmap.red[idx] = ((reds[idx]) << 8)|reds[idx]; + cmap.green[idx] = ((greens[idx]) << 8)|greens[idx]; + cmap.blue[idx] = ((blues[idx]) << 8)|blues[idx]; + cmap.transp[idx] = 0; + screenclut[idx]=qRgb(reds[idx], greens[idx], blues[idx]); + } + } + } else { + if (grayscale) { + // Build grayscale palette + int i; + for(i=0;i<screencols;++i) { + int bval = screencols == 256 ? i : (i << 4); + ushort val = (bval << 8) | bval; + cmap.red[i] = val; + cmap.green[i] = val; + cmap.blue[i] = val; + cmap.transp[i] = 0; + screenclut[i] = qRgb(bval,bval,bval); + } + } else { + // 6x6x6 216 color cube + int idx = 0; + for(int ir = 0x0; ir <= 0xff; ir+=0x33) { + for(int ig = 0x0; ig <= 0xff; ig+=0x33) { + for(int ib = 0x0; ib <= 0xff; ib+=0x33) { + cmap.red[idx] = (ir << 8)|ir; + cmap.green[idx] = (ig << 8)|ig; + cmap.blue[idx] = (ib << 8)|ib; + cmap.transp[idx] = 0; + screenclut[idx]=qRgb(ir, ig, ib); + ++idx; + } + } + } + // Fill in rest with 0 + for (int loopc=0; loopc<40; ++loopc) { + screenclut[idx]=0; + ++idx; + } + screencols=idx; + } + } + } else if(finfo.visual==FB_VISUAL_DIRECTCOLOR) { + cmap.start=0; + int rbits=0,gbits=0,bbits=0; + switch (vinfo.bits_per_pixel) { + case 8: + rbits=vinfo.red.length; + gbits=vinfo.green.length; + bbits=vinfo.blue.length; + if(rbits==0 && gbits==0 && bbits==0) { + // cyber2000 driver bug hack + rbits=3; + gbits=3; + bbits=2; + } + break; + case 15: + rbits=5; + gbits=5; + bbits=5; + break; + case 16: + rbits=5; + gbits=6; + bbits=5; + break; + case 18: + case 19: + rbits=6; + gbits=6; + bbits=6; + break; + case 24: case 32: + rbits=gbits=bbits=8; + break; + } + screencols=cmap.len=1<<qMax(rbits,qMax(gbits,bbits)); + cmap.red=(unsigned short int *) + malloc(sizeof(unsigned short int)*256); + cmap.green=(unsigned short int *) + malloc(sizeof(unsigned short int)*256); + cmap.blue=(unsigned short int *) + malloc(sizeof(unsigned short int)*256); + cmap.transp=(unsigned short int *) + malloc(sizeof(unsigned short int)*256); + for(unsigned int i = 0x0; i < cmap.len; i++) { + cmap.red[i] = i*65535/((1<<rbits)-1); + cmap.green[i] = i*65535/((1<<gbits)-1); + cmap.blue[i] = i*65535/((1<<bbits)-1); + cmap.transp[i] = 0; + } + } +} + +/*! + \reimp + + This is called by the \l{Qt for Embedded Linux} server at startup time. + It turns off console blinking, sets up the color palette, enables write + combining on the framebuffer and initialises the off-screen memory + manager. +*/ + +bool QLinuxFbScreen::initDevice() +{ + d_ptr->openTty(); + + // Grab current mode so we can reset it + fb_var_screeninfo vinfo; + fb_fix_screeninfo finfo; + //####################### + // Shut up Valgrind + memset(&vinfo, 0, sizeof(vinfo)); + memset(&finfo, 0, sizeof(finfo)); + //####################### + + if (ioctl(d_ptr->fd, FBIOGET_VSCREENINFO, &vinfo)) { + perror("QLinuxFbScreen::initDevice"); + qFatal("Error reading variable information in card init"); + return false; + } + +#ifdef DEBUG_VINFO + qDebug("Greyscale %d",vinfo.grayscale); + qDebug("Nonstd %d",vinfo.nonstd); + qDebug("Red %d %d %d",vinfo.red.offset,vinfo.red.length, + vinfo.red.msb_right); + qDebug("Green %d %d %d",vinfo.green.offset,vinfo.green.length, + vinfo.green.msb_right); + qDebug("Blue %d %d %d",vinfo.blue.offset,vinfo.blue.length, + vinfo.blue.msb_right); + qDebug("Transparent %d %d %d",vinfo.transp.offset,vinfo.transp.length, + vinfo.transp.msb_right); +#endif + + d_ptr->startupw=vinfo.xres; + d_ptr->startuph=vinfo.yres; + d_ptr->startupd=vinfo.bits_per_pixel; + grayscale = vinfo.grayscale; + + if (ioctl(d_ptr->fd, FBIOGET_FSCREENINFO, &finfo)) { + perror("QLinuxFbScreen::initDevice"); + qCritical("Error reading fixed information in card init"); + // It's not an /error/ as such, though definitely a bad sign + // so we return true + return true; + } + +#ifdef __i386__ + // Now init mtrr + if(!::getenv("QWS_NOMTRR")) { + int mfd=open("/proc/mtrr",O_WRONLY,0); + // MTRR entry goes away when file is closed - i.e. + // hopefully when QWS is killed + if(mfd != -1) { + mtrr_sentry sentry; + sentry.base=(unsigned long int)finfo.smem_start; + //qDebug("Physical framebuffer address %p",(void*)finfo.smem_start); + // Size needs to be in 4k chunks, but that's not always + // what we get thanks to graphics card registers. Write combining + // these is Not Good, so we write combine what we can + // (which is not much - 4 megs on an 8 meg card, it seems) + unsigned int size=finfo.smem_len; + size=size >> 22; + size=size << 22; + sentry.size=size; + sentry.type=MTRR_TYPE_WRCOMB; + if(ioctl(mfd,MTRRIOC_ADD_ENTRY,&sentry)==-1) { + //printf("Couldn't add mtrr entry for %lx %lx, %s\n", + //sentry.base,sentry.size,strerror(errno)); + } + } + } +#endif + if ((vinfo.bits_per_pixel==8) || (vinfo.bits_per_pixel==4) || (finfo.visual==FB_VISUAL_DIRECTCOLOR)) + { + fb_cmap cmap; + createPalette(cmap, vinfo, finfo); + if (ioctl(d_ptr->fd, FBIOPUTCMAP, &cmap)) { + perror("QLinuxFbScreen::initDevice"); + qWarning("Error writing palette to framebuffer"); + } + free(cmap.red); + free(cmap.green); + free(cmap.blue); + free(cmap.transp); + } + + if (canaccel) { + *entryp=0; + *lowest = mapsize; + insert_entry(*entryp, *lowest, *lowest); // dummy entry to mark start + } + + shared->fifocount = 0; + shared->buffer_offset = 0xffffffff; // 0 would be a sensible offset (screen) + shared->linestep = 0; + shared->cliptop = 0xffffffff; + shared->clipleft = 0xffffffff; + shared->clipright = 0xffffffff; + shared->clipbottom = 0xffffffff; + shared->rop = 0xffffffff; + +#ifdef QT_QWS_DEPTH_GENERIC + if (pixelFormat() == QImage::Format_Invalid && screencols == 0 + && d_ptr->doGenericColors) + { + qt_set_generic_blit(this, vinfo.bits_per_pixel, + vinfo.red.length, vinfo.green.length, + vinfo.blue.length, vinfo.transp.length, + vinfo.red.offset, vinfo.green.offset, + vinfo.blue.offset, vinfo.transp.offset); + } +#endif + +#ifndef QT_NO_QWS_CURSOR + QScreenCursor::initSoftwareCursor(); +#endif + blank(false); + + return true; +} + +/* + The offscreen memory manager's list of entries is stored at the bottom + of the offscreen memory area and consistes of a series of QPoolEntry's, + each of which keep track of a block of allocated memory. Unallocated memory + is implicitly indicated by the gap between blocks indicated by QPoolEntry's. + The memory manager looks through any unallocated memory before the end + of currently-allocated memory to see if a new block will fit in the gap; + if it doesn't it allocated it from the end of currently-allocated memory. + Memory is allocated from the top of the framebuffer downwards; if it hits + the list of entries then offscreen memory is full and further allocations + are made from main RAM (and hence unaccelerated). Allocated memory can + be seen as a sort of upside-down stack; lowest keeps track of the + bottom of the stack. +*/ + +void QLinuxFbScreen::delete_entry(int pos) +{ + if (pos > *entryp || pos < 0) { + qWarning("Attempt to delete odd pos! %d %d", pos, *entryp); + return; + } + +#ifdef DEBUG_CACHE + qDebug("Remove entry: %d", pos); +#endif + + QPoolEntry *qpe = &entries[pos]; + if (qpe->start <= *lowest) { + // Lowest goes up again + *lowest = entries[pos-1].start; +#ifdef DEBUG_CACHE + qDebug(" moved lowest to %d", *lowest); +#endif + } + + (*entryp)--; + if (pos == *entryp) + return; + + int size = (*entryp)-pos; + memmove(&entries[pos], &entries[pos+1], size*sizeof(QPoolEntry)); +} + +void QLinuxFbScreen::insert_entry(int pos, int start, int end) +{ + if (pos > *entryp) { + qWarning("Attempt to insert odd pos! %d %d",pos,*entryp); + return; + } + +#ifdef DEBUG_CACHE + qDebug("Insert entry: %d, %d -> %d", pos, start, end); +#endif + + if (start < (int)*lowest) { + *lowest = start; +#ifdef DEBUG_CACHE + qDebug(" moved lowest to %d", *lowest); +#endif + } + + if (pos == *entryp) { + entries[pos].start = start; + entries[pos].end = end; + entries[pos].clientId = qws_client_id; + (*entryp)++; + return; + } + + int size=(*entryp)-pos; + memmove(&entries[pos+1],&entries[pos],size*sizeof(QPoolEntry)); + entries[pos].start=start; + entries[pos].end=end; + entries[pos].clientId=qws_client_id; + (*entryp)++; +} + +/*! + \fn uchar * QLinuxFbScreen::cache(int amount) + + Requests the specified \a amount of offscreen graphics card memory + from the memory manager, and returns a pointer to the data within + the framebuffer (or 0 if there is no free memory). + + Note that the display is locked while memory is allocated in order to + preserve the memory pool's integrity. + + Use the QScreen::onCard() function to retrieve an offset (in + bytes) from the start of graphics card memory for the returned + pointer. + + \sa uncache(), clearCache(), deleteEntry() +*/ + +uchar * QLinuxFbScreen::cache(int amount) +{ + if (!canaccel || entryp == 0) + return 0; + + qt_fbdpy->grab(); + + int startp = cacheStart + (*entryp+1) * sizeof(QPoolEntry); + if (startp >= (int)*lowest) { + // We don't have room for another cache QPoolEntry. +#ifdef DEBUG_CACHE + qDebug("No room for pool entry in VRAM"); +#endif + qt_fbdpy->ungrab(); + return 0; + } + + int align = pixmapOffsetAlignment(); + + if (*entryp > 1) { + // Try to find a gap in the allocated blocks. + for (int loopc = 0; loopc < *entryp-1; loopc++) { + int freestart = entries[loopc+1].end; + int freeend = entries[loopc].start; + if (freestart != freeend) { + while (freestart % align) { + freestart++; + } + int len=freeend-freestart; + if (len >= amount) { + insert_entry(loopc+1, freestart, freestart+amount); + qt_fbdpy->ungrab(); + return data+freestart; + } + } + } + } + + // No free blocks in already-taken memory; get some more + // if we can + int newlowest = (*lowest)-amount; + if (newlowest % align) { + newlowest -= align; + while (newlowest % align) { + newlowest++; + } + } + if (startp >= newlowest) { + qt_fbdpy->ungrab(); +#ifdef DEBUG_CACHE + qDebug("No VRAM available for %d bytes", amount); +#endif + return 0; + } + insert_entry(*entryp, newlowest, *lowest); + qt_fbdpy->ungrab(); + + return data + newlowest; +} + +/*! + \fn void QLinuxFbScreen::uncache(uchar * memoryBlock) + + Deletes the specified \a memoryBlock allocated from the graphics + card memory. + + Note that the display is locked while memory is unallocated in + order to preserve the memory pool's integrity. + + This function will first sync the graphics card to ensure the + memory isn't still being used by a command in the graphics card + FIFO queue. It is possible to speed up a driver by overriding this + function to avoid syncing. For example, the driver might delay + deleting the memory until it detects that all commands dealing + with the memory are no longer in the queue. Note that it will then + be up to the driver to ensure that the specified \a memoryBlock no + longer is being used. + + \sa cache(), deleteEntry(), clearCache() + */ +void QLinuxFbScreen::uncache(uchar * c) +{ + // need to sync graphics card + + deleteEntry(c); +} + +/*! + \fn void QLinuxFbScreen::deleteEntry(uchar * memoryBlock) + + Deletes the specified \a memoryBlock allocated from the graphics + card memory. + + \sa uncache(), cache(), clearCache() +*/ +void QLinuxFbScreen::deleteEntry(uchar * c) +{ + qt_fbdpy->grab(); + unsigned long pos=(unsigned long)c; + pos-=((unsigned long)data); + unsigned int hold=(*entryp); + for(unsigned int loopc=1;loopc<hold;loopc++) { + if (entries[loopc].start==pos) { + if (entries[loopc].clientId == qws_client_id) + delete_entry(loopc); + else + qWarning("Attempt to delete client id %d cache entry", + entries[loopc].clientId); + qt_fbdpy->ungrab(); + return; + } + } + qt_fbdpy->ungrab(); + qWarning("Attempt to delete unknown offset %ld",pos); +} + +/*! + Removes all entries from the cache for the specified screen \a + instance and client identified by the given \a clientId. + + Calling this function should only be necessary if a client exits + abnormally. + + \sa cache(), uncache(), deleteEntry() +*/ +void QLinuxFbScreen::clearCache(QScreen *instance, int clientId) +{ + QLinuxFbScreen *screen = (QLinuxFbScreen *)instance; + if (!screen->canaccel || !screen->entryp) + return; + qt_fbdpy->grab(); + for (int loopc = 0; loopc < *(screen->entryp); loopc++) { + if (screen->entries[loopc].clientId == clientId) { + screen->delete_entry(loopc); + loopc--; + } + } + qt_fbdpy->ungrab(); +} + + +void QLinuxFbScreen::setupOffScreen() +{ + // Figure out position of offscreen memory + // Set up pool entries pointer table and 64-bit align it + int psize = size; + + // hw: this causes the limitation of cursors to 64x64 + // the cursor should rather use the normal pixmap mechanism + psize += 4096; // cursor data + psize += 8; // for alignment + psize &= ~0x7; // align + + unsigned long pos = (unsigned long)data; + pos += psize; + entryp = ((int *)pos); + lowest = ((unsigned int *)pos)+1; + pos += (sizeof(int))*4; + entries = (QPoolEntry *)pos; + + // beginning of offscreen memory available for pixmaps. + cacheStart = psize + 4*sizeof(int) + sizeof(QPoolEntry); +} + +/*! + \reimp + + This is called by the \l{Qt for Embedded Linux} server when it shuts + down, and should be inherited if you need to do any card-specific cleanup. + The default version hides the screen cursor and reenables the blinking + cursor and screen blanking. +*/ + +void QLinuxFbScreen::shutdownDevice() +{ + // Causing crashes. Not needed. + //setMode(startupw,startuph,startupd); +/* + if (startupd == 8) { + ioctl(fd,FBIOPUTCMAP,startcmap); + free(startcmap->red); + free(startcmap->green); + free(startcmap->blue); + free(startcmap->transp); + delete startcmap; + startcmap = 0; + } +*/ + d_ptr->closeTty(); +} + +/*! + \fn void QLinuxFbScreen::set(unsigned int index,unsigned int red,unsigned int green,unsigned int blue) + + Sets the specified color \a index to the specified RGB value, (\a + red, \a green, \a blue), when in paletted graphics modes. +*/ + +void QLinuxFbScreen::set(unsigned int i,unsigned int r,unsigned int g,unsigned int b) +{ + if (d_ptr->fd != -1) { + fb_cmap cmap; + cmap.start=i; + cmap.len=1; + cmap.red=(unsigned short int *) + malloc(sizeof(unsigned short int)*256); + cmap.green=(unsigned short int *) + malloc(sizeof(unsigned short int)*256); + cmap.blue=(unsigned short int *) + malloc(sizeof(unsigned short int)*256); + cmap.transp=(unsigned short int *) + malloc(sizeof(unsigned short int)*256); + cmap.red[0]=r << 8; + cmap.green[0]=g << 8; + cmap.blue[0]=b << 8; + cmap.transp[0]=0; + ioctl(d_ptr->fd, FBIOPUTCMAP, &cmap); + free(cmap.red); + free(cmap.green); + free(cmap.blue); + free(cmap.transp); + } + screenclut[i] = qRgb(r, g, b); +} + +/*! + \reimp + + Sets the framebuffer to a new resolution and bit depth. The width is + in \a nw, the height is in \a nh, and the depth is in \a nd. After + doing this any currently-existing paint engines will be invalid and the + screen should be completely redrawn. In a multiple-process + Embedded Qt situation you must signal all other applications to + call setMode() to the same mode and redraw. +*/ + +void QLinuxFbScreen::setMode(int nw,int nh,int nd) +{ + if (d_ptr->fd == -1) + return; + + fb_fix_screeninfo finfo; + fb_var_screeninfo vinfo; + //####################### + // Shut up Valgrind + memset(&vinfo, 0, sizeof(vinfo)); + memset(&finfo, 0, sizeof(finfo)); + //####################### + + if (ioctl(d_ptr->fd, FBIOGET_VSCREENINFO, &vinfo)) { + perror("QLinuxFbScreen::setMode"); + qFatal("Error reading variable information in mode change"); + } + + vinfo.xres=nw; + vinfo.yres=nh; + vinfo.bits_per_pixel=nd; + + if (ioctl(d_ptr->fd, FBIOPUT_VSCREENINFO, &vinfo)) { + perror("QLinuxFbScreen::setMode"); + qCritical("Error writing variable information in mode change"); + } + + if (ioctl(d_ptr->fd, FBIOGET_VSCREENINFO, &vinfo)) { + perror("QLinuxFbScreen::setMode"); + qFatal("Error reading changed variable information in mode change"); + } + + if (ioctl(d_ptr->fd, FBIOGET_FSCREENINFO, &finfo)) { + perror("QLinuxFbScreen::setMode"); + qFatal("Error reading fixed information"); + } + + disconnect(); + connect(d_ptr->displaySpec); + exposeRegion(region(), 0); +} + +// save the state of the graphics card +// This is needed so that e.g. we can restore the palette when switching +// between linux virtual consoles. + +/*! + \reimp + + This doesn't do anything; accelerated drivers may wish to reimplement + it to save graphics cards registers. It's called by the + \l{Qt for Embedded Linux} server when the virtual console is switched. +*/ + +void QLinuxFbScreen::save() +{ + // nothing to do. +} + + +// restore the state of the graphics card. +/*! + \reimp + + This is called when the virtual console is switched back to + \l{Qt for Embedded Linux} and restores the palette. +*/ +void QLinuxFbScreen::restore() +{ + if (d_ptr->fd == -1) + return; + + if ((d == 8) || (d == 4)) { + fb_cmap cmap; + cmap.start=0; + cmap.len=screencols; + cmap.red=(unsigned short int *) + malloc(sizeof(unsigned short int)*256); + cmap.green=(unsigned short int *) + malloc(sizeof(unsigned short int)*256); + cmap.blue=(unsigned short int *) + malloc(sizeof(unsigned short int)*256); + cmap.transp=(unsigned short int *) + malloc(sizeof(unsigned short int)*256); + for (int loopc = 0; loopc < screencols; loopc++) { + cmap.red[loopc] = qRed(screenclut[loopc]) << 8; + cmap.green[loopc] = qGreen(screenclut[loopc]) << 8; + cmap.blue[loopc] = qBlue(screenclut[loopc]) << 8; + cmap.transp[loopc] = 0; + } + ioctl(d_ptr->fd, FBIOPUTCMAP, &cmap); + free(cmap.red); + free(cmap.green); + free(cmap.blue); + free(cmap.transp); + } +} + +/*! + \fn int QLinuxFbScreen::sharedRamSize(void * end) + \internal +*/ + +// This works like the QScreenCursor code. end points to the end +// of our shared structure, we return the amount of memory we reserved +int QLinuxFbScreen::sharedRamSize(void * end) +{ + shared=(QLinuxFb_Shared *)end; + shared--; + return sizeof(QLinuxFb_Shared); +} + +/*! + \reimp +*/ +void QLinuxFbScreen::blank(bool on) +{ + if (d_ptr->blank == on) + return; + +#if defined(QT_QWS_IPAQ) + if (on) + system("apm -suspend"); +#else + if (d_ptr->fd == -1) + return; +// Some old kernel versions don't have this. These defines should go +// away eventually +#if defined(FBIOBLANK) +#if defined(VESA_POWERDOWN) && defined(VESA_NO_BLANKING) + ioctl(d_ptr->fd, FBIOBLANK, on ? VESA_POWERDOWN : VESA_NO_BLANKING); +#else + ioctl(d_ptr->fd, FBIOBLANK, on ? 1 : 0); +#endif +#endif +#endif + + d_ptr->blank = on; +} + +void QLinuxFbScreen::setPixelFormat(struct fb_var_screeninfo info) +{ + const fb_bitfield rgba[4] = { info.red, info.green, + info.blue, info.transp }; + + QImage::Format format = QImage::Format_Invalid; + + switch (d) { + case 32: { + const fb_bitfield argb8888[4] = {{16, 8, 0}, {8, 8, 0}, + {0, 8, 0}, {24, 8, 0}}; + const fb_bitfield abgr8888[4] = {{0, 8, 0}, {8, 8, 0}, + {16, 8, 0}, {24, 8, 0}}; + if (memcmp(rgba, argb8888, 4 * sizeof(fb_bitfield)) == 0) { + format = QImage::Format_ARGB32; + } else if (memcmp(rgba, argb8888, 3 * sizeof(fb_bitfield)) == 0) { + format = QImage::Format_RGB32; + } else if (memcmp(rgba, abgr8888, 3 * sizeof(fb_bitfield)) == 0) { + format = QImage::Format_RGB32; + pixeltype = QScreen::BGRPixel; + } + break; + } + case 24: { + const fb_bitfield rgb888[4] = {{16, 8, 0}, {8, 8, 0}, + {0, 8, 0}, {0, 0, 0}}; + const fb_bitfield bgr888[4] = {{0, 8, 0}, {8, 8, 0}, + {16, 8, 0}, {0, 0, 0}}; + if (memcmp(rgba, rgb888, 3 * sizeof(fb_bitfield)) == 0) { + format = QImage::Format_RGB888; + } else if (memcmp(rgba, bgr888, 3 * sizeof(fb_bitfield)) == 0) { + format = QImage::Format_RGB888; + pixeltype = QScreen::BGRPixel; + } + break; + } + case 18: { + const fb_bitfield rgb666[4] = {{12, 6, 0}, {6, 6, 0}, + {0, 6, 0}, {0, 0, 0}}; + if (memcmp(rgba, rgb666, 3 * sizeof(fb_bitfield)) == 0) + format = QImage::Format_RGB666; + break; + } + case 16: { + const fb_bitfield rgb565[4] = {{11, 5, 0}, {5, 6, 0}, + {0, 5, 0}, {0, 0, 0}}; + const fb_bitfield bgr565[4] = {{0, 5, 0}, {5, 6, 0}, + {11, 5, 0}, {0, 0, 0}}; + if (memcmp(rgba, rgb565, 3 * sizeof(fb_bitfield)) == 0) { + format = QImage::Format_RGB16; + } else if (memcmp(rgba, bgr565, 3 * sizeof(fb_bitfield)) == 0) { + format = QImage::Format_RGB16; + pixeltype = QScreen::BGRPixel; + } + break; + } + case 15: { + const fb_bitfield rgb1555[4] = {{10, 5, 0}, {5, 5, 0}, + {0, 5, 0}, {15, 1, 0}}; + const fb_bitfield bgr1555[4] = {{0, 5, 0}, {5, 5, 0}, + {10, 5, 0}, {15, 1, 0}}; + if (memcmp(rgba, rgb1555, 3 * sizeof(fb_bitfield)) == 0) { + format = QImage::Format_RGB555; + } else if (memcmp(rgba, bgr1555, 3 * sizeof(fb_bitfield)) == 0) { + format = QImage::Format_RGB555; + pixeltype = QScreen::BGRPixel; + } + break; + } + case 12: { + const fb_bitfield rgb444[4] = {{8, 4, 0}, {4, 4, 0}, + {0, 4, 0}, {0, 0, 0}}; + if (memcmp(rgba, rgb444, 3 * sizeof(fb_bitfield)) == 0) + format = QImage::Format_RGB444; + break; + } + case 8: + break; + case 1: + format = QImage::Format_Mono; //###: LSB??? + break; + default: + break; + } + + QScreen::setPixelFormat(format); +} + +bool QLinuxFbScreen::useOffscreen() +{ + if ((mapsize - size) < 16*1024) + return false; + + return true; +} + +QT_END_NAMESPACE + +#endif // QT_NO_QWS_LINUXFB diff --git a/src/gui/embedded/qscreenlinuxfb_qws.h b/src/gui/embedded/qscreenlinuxfb_qws.h new file mode 100644 index 0000000..781b205 --- /dev/null +++ b/src/gui/embedded/qscreenlinuxfb_qws.h @@ -0,0 +1,129 @@ +/**************************************************************************** +** +** 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$ +** +****************************************************************************/ + +#ifndef QSCREENLINUXFB_QWS_H +#define QSCREENLINUXFB_QWS_H + +#include <QtGui/qscreen_qws.h> + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +QT_MODULE(Gui) + +#ifndef QT_NO_QWS_LINUXFB + +class QLinuxFb_Shared +{ +public: + volatile int lastop; + volatile int optype; + volatile int fifocount; // Accel drivers only + volatile int fifomax; + volatile int forecol; // Foreground colour caching + volatile unsigned int buffer_offset; // Destination + volatile int linestep; + volatile int cliptop; // Clip rectangle + volatile int clipleft; + volatile int clipright; + volatile int clipbottom; + volatile unsigned int rop; + +}; + +struct fb_cmap; +struct fb_var_screeninfo; +struct fb_fix_screeninfo; +class QLinuxFbScreenPrivate; + +class Q_GUI_EXPORT QLinuxFbScreen : public QScreen +{ +public: + explicit QLinuxFbScreen(int display_id); + virtual ~QLinuxFbScreen(); + + virtual bool initDevice(); + virtual bool connect(const QString &displaySpec); + + virtual bool useOffscreen(); + + virtual void disconnect(); + virtual void shutdownDevice(); + virtual void setMode(int,int,int); + virtual void save(); + virtual void restore(); + virtual void blank(bool on); + virtual void set(unsigned int,unsigned int,unsigned int,unsigned int); + virtual uchar * cache(int); + virtual void uncache(uchar *); + virtual int sharedRamSize(void *); + + QLinuxFb_Shared * shared; + +protected: + + void deleteEntry(uchar *); + + bool canaccel; + int dataoffset; + int cacheStart; + + static void clearCache(QScreen *instance, int); + +private: + + void delete_entry(int); + void insert_entry(int,int,int); + void setupOffScreen(); + void createPalette(fb_cmap &cmap, fb_var_screeninfo &vinfo, fb_fix_screeninfo &finfo); + void setPixelFormat(struct fb_var_screeninfo); + + QLinuxFbScreenPrivate *d_ptr; +}; + +#endif // QT_NO_QWS_LINUXFB + +QT_END_NAMESPACE + +QT_END_HEADER + +#endif // QSCREENLINUXFB_QWS_H diff --git a/src/gui/embedded/qscreenmulti_qws.cpp b/src/gui/embedded/qscreenmulti_qws.cpp new file mode 100644 index 0000000..1914b44 --- /dev/null +++ b/src/gui/embedded/qscreenmulti_qws.cpp @@ -0,0 +1,482 @@ +/**************************************************************************** +** +** 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 "qscreenmulti_qws_p.h" + +#ifndef QT_NO_QWS_MULTISCREEN + +#include <qlist.h> +#include <qstringlist.h> +#include <qwidget.h> + +QT_BEGIN_NAMESPACE + +#ifndef QT_NO_QWS_CURSOR + +class QMultiScreenCursor : public QScreenCursor +{ +public: + QMultiScreenCursor() : currentCursor(qt_screencursor) { enable = false; } + ~QMultiScreenCursor() { qt_screencursor = 0; } + + void set(const QImage &image, int hotx, int hoty); + void move(int x, int y); + void show(); + void hide(); + + void addCursor(QScreenCursor *cursor); + +private: + void setCurrentCursor(QScreenCursor *newCursor); + + QScreenCursor *currentCursor; + QList<QScreenCursor*> cursors; +}; + +void QMultiScreenCursor::set(const QImage &image, int hotx, int hoty) +{ + QScreenCursor::set(image, hotx, hoty); + if (currentCursor) + currentCursor->set(image, hotx, hoty); +} + +void QMultiScreenCursor::setCurrentCursor(QScreenCursor *newCursor) +{ + *((QScreenCursor*)this) = *newCursor; + currentCursor = newCursor; +} + +// XXX: this is a mess! +void QMultiScreenCursor::move(int x, int y) +{ + const int oldIndex = qt_screen->subScreenIndexAt(pos); + QScreenCursor::move(x, y); // updates pos + const int newIndex = qt_screen->subScreenIndexAt(pos); + + if (!currentCursor && oldIndex != -1) + setCurrentCursor(cursors.at(oldIndex)); + QScreenCursor *oldCursor = currentCursor; + + if (oldIndex != -1) { + const QScreen *oldScreen = qt_screen->subScreens().at(oldIndex); + if (newIndex == -1 || oldScreen->region().contains(pos)) { + oldCursor->move(x, y); + return; + } + } + + if (newIndex != -1) { + QScreenCursor *newCursor = cursors.at(newIndex); + newCursor->set(cursor, hotspot.x(), hotspot.y()); + + if (oldCursor) { + if (oldCursor->isVisible()) + newCursor->show(); + oldCursor->hide(); + } + + newCursor->move(x, y); + + setCurrentCursor(newCursor); + } +} + +void QMultiScreenCursor::show() +{ + if (currentCursor) + currentCursor->show(); +} + +void QMultiScreenCursor::hide() +{ + if (currentCursor) + currentCursor->hide(); +} + +void QMultiScreenCursor::addCursor(QScreenCursor *cursor) +{ + cursors.append(cursor); +} + +#endif + +class QMultiScreenPrivate +{ +public: + QMultiScreenPrivate() +#ifndef QT_NO_QWS_CURSOR + : cursor(0) +#endif + {} + ~QMultiScreenPrivate() + { +#ifndef QT_NO_QWS_CURSOR + delete cursor; +#endif + } + + QList<QScreen*> screens; + QRegion region; +#ifndef QT_NO_QWS_CURSOR + QMultiScreenCursor *cursor; +#endif +}; + +QMultiScreen::QMultiScreen(int displayId) + : QScreen(displayId, MultiClass), d_ptr(new QMultiScreenPrivate) +{ +} + +QMultiScreen::~QMultiScreen() +{ + delete d_ptr; +} + +bool QMultiScreen::initDevice() +{ + bool ok = true; + +#ifndef QT_NO_QWS_CURSOR + d_ptr->cursor = new QMultiScreenCursor; +#endif + + const int n = d_ptr->screens.count(); + for (int i = 0; i < n; ++i) { + QScreen *s = d_ptr->screens.at(i); + ok = s->initDevice() && ok; +#ifndef QT_NO_QWS_CURSOR + d_ptr->cursor->addCursor(qt_screencursor); // XXX +#endif + } + +#ifndef QT_NO_QWS_CURSOR + // XXX + qt_screencursor = d_ptr->cursor; +#endif + + return ok; +} + +static int getDisplayId(const QString &spec) +{ + QRegExp regexp(QLatin1String(":(\\d+)\\b")); + if (regexp.lastIndexIn(spec) != -1) { + const QString capture = regexp.cap(1); + return capture.toInt(); + } + return 0; +} + +static QPoint filterDisplayOffset(QString &spec) +{ + QRegExp regexp(QLatin1String(":offset=(\\d+),(\\d+)\\b")); + if (regexp.indexIn(spec) == -1) + return QPoint(); + + const int x = regexp.cap(1).toInt(); + const int y = regexp.cap(2).toInt(); + spec.remove(regexp.pos(0), regexp.matchedLength()); + return QPoint(x, y); +} + +bool QMultiScreen::connect(const QString &displaySpec) +{ + QString dSpec = displaySpec; + if (dSpec.startsWith(QLatin1String("Multi:"), Qt::CaseInsensitive)) + dSpec = dSpec.mid(QString(QLatin1String("Multi:")).size()); + + const QString displayIdSpec = QString(QLatin1String(" :%1")).arg(displayId); + if (dSpec.endsWith(displayIdSpec)) + dSpec = dSpec.left(dSpec.size() - displayIdSpec.size()); + + QStringList specs = dSpec.split(QLatin1Char(' '), QString::SkipEmptyParts); + foreach (QString spec, specs) { + const int id = getDisplayId(spec); + const QPoint offset = filterDisplayOffset(spec); + QScreen *s = qt_get_screen(id, spec.toLatin1().constData()); + s->setOffset(offset); + addSubScreen(s); + } + + QScreen *firstScreen = d_ptr->screens.at(0); + Q_ASSERT(firstScreen); + + // XXX + QScreen::d = firstScreen->depth(); + + QScreen::lstep = 0; + QScreen::data = 0; + QScreen::size = 0; + + QScreen::w = d_ptr->region.boundingRect().width(); + QScreen::h = d_ptr->region.boundingRect().height(); + + QScreen::dw = QScreen::w; + QScreen::dh = QScreen::h; + + // XXX - Extend the physical size based on the first screen + // to encompass all screens, so that code that uses the multi + // screen to calculate dpi values will get the right numbers. + QScreen::physWidth = firstScreen->physicalWidth() * w / firstScreen->width(); + QScreen::physHeight = firstScreen->physicalHeight() * h / firstScreen->height(); + + // XXXXX + qt_screen = this; + + return true; +} + +void QMultiScreen::disconnect() +{ + const int n = d_ptr->screens.size(); + for (int i = 0; i < n; ++i) + d_ptr->screens.at(i)->disconnect(); +} + +void QMultiScreen::shutdownDevice() +{ + const int n = d_ptr->screens.size(); + for (int i = 0; i < n; ++i) + d_ptr->screens.at(i)->shutdownDevice(); +} + +void QMultiScreen::setMode(int, int, int) +{ + return; +} + +bool QMultiScreen::supportsDepth(int) const +{ + return false; +} + +void QMultiScreen::save() +{ + const int n = d_ptr->screens.size(); + for (int i = 0; i < n; ++i) + d_ptr->screens.at(i)->save(); +} + +void QMultiScreen::restore() +{ + const int n = d_ptr->screens.size(); + for (int i = 0; i < n; ++i) + d_ptr->screens.at(i)->restore(); +} + +void QMultiScreen::blank(bool on) +{ + const int n = d_ptr->screens.size(); + for (int i = 0; i < n; ++i) + d_ptr->screens.at(i)->blank(on); +} + +bool QMultiScreen::onCard(const unsigned char *ptr) const +{ + const int n = d_ptr->screens.size(); + for (int i = 0; i < n; ++i) + if (d_ptr->screens.at(i)->onCard(ptr)) + return true; + return false; +} + +bool QMultiScreen::onCard(const unsigned char *ptr, ulong &offset) const +{ + const int n = d_ptr->screens.size(); + for (int i = 0; i < n; ++i) + if (d_ptr->screens.at(i)->onCard(ptr, offset)) + return true; + return false; +} + +bool QMultiScreen::isInterlaced() const +{ + const int n = d_ptr->screens.size(); + for (int i = 0; i < n; ++i) + if (d_ptr->screens.at(i)->isInterlaced()) + return true; + + return false; +} + +int QMultiScreen::memoryNeeded(const QString &string) +{ + int total = 0; + const int n = d_ptr->screens.size(); + for (int i = 0; i < n; ++i) + total += d_ptr->screens.at(i)->memoryNeeded(string); + return total; +} + +int QMultiScreen::sharedRamSize(void *arg) +{ + int total = 0; + const int n = d_ptr->screens.size(); + for (int i = 0; i < n; ++i) + total += d_ptr->screens.at(i)->sharedRamSize(arg); + return total; +} + +void QMultiScreen::haltUpdates() +{ + const int n = d_ptr->screens.size(); + for (int i = 0; i < n; ++i) + d_ptr->screens.at(i)->haltUpdates(); +} + +void QMultiScreen::resumeUpdates() +{ + const int n = d_ptr->screens.size(); + for (int i = 0; i < n; ++i) + d_ptr->screens.at(i)->resumeUpdates(); +} + +void QMultiScreen::exposeRegion(QRegion region, int changing) +{ + const int n = d_ptr->screens.size(); + for (int i = 0; i < n; ++i) { + QScreen *screen = d_ptr->screens.at(i); + const QRegion r = region & screen->region(); + if (r.isEmpty()) + continue; + screen->exposeRegion(r, changing); + } +} + +void QMultiScreen::solidFill(const QColor &color, const QRegion ®ion) +{ + const int n = d_ptr->screens.size(); + for (int i = 0; i < n; ++i) { + QScreen *screen = d_ptr->screens.at(i); + const QRegion r = region & screen->region(); + if (r.isEmpty()) + continue; + screen->solidFill(color, r); + } +} + +void QMultiScreen::blit(const QImage &img, const QPoint &topLeft, + const QRegion ®ion) +{ + const int n = d_ptr->screens.size(); + for (int i = 0; i < n; ++i) { + QScreen *screen = d_ptr->screens.at(i); + const QRegion r = region & screen->region(); + if (r.isEmpty()) + continue; + screen->blit(img, topLeft, r); + } +} + +void QMultiScreen::blit(QWSWindow *bs, const QRegion &clip) +{ + const int n = d_ptr->screens.size(); + for (int i = 0; i < n; ++i) { + QScreen *screen = d_ptr->screens.at(i); + const QRegion r = clip & screen->region(); + if (r.isEmpty()) + continue; + screen->blit(bs, r); + } +} + +void QMultiScreen::setDirty(const QRect &rect) +{ + const int n = d_ptr->screens.size(); + for (int i = 0; i < n; ++i) { + QScreen *screen = d_ptr->screens.at(i); + const QRegion r = screen->region() & rect; + if (r.isEmpty()) + continue; + screen->setDirty(r.boundingRect()); + } +} + + +QWSWindowSurface* QMultiScreen::createSurface(const QString &key) const +{ + QWSWindowSurface* surf = 0; + const int n = d_ptr->screens.size(); + for (int i = 0; i < n; ++i) { + QScreen *screen = d_ptr->screens.at(i); + surf = screen->createSurface(key); + if (surf) + break; + } + return surf; +} + + +QWSWindowSurface* QMultiScreen::createSurface(QWidget *widget) const +{ + const QPoint midpoint = (widget->frameGeometry().topLeft() + + widget->frameGeometry().bottomRight()) / 2; + int index = subScreenIndexAt(midpoint); + if (index == -1) + index = 0; // XXX + return d_ptr->screens.at(index)->createSurface(widget); +} + +QList<QScreen*> QMultiScreen::subScreens() const +{ + return d_ptr->screens; +} + +QRegion QMultiScreen::region() const +{ + return d_ptr->region; +} + +void QMultiScreen::addSubScreen(QScreen *screen) +{ + d_ptr->screens.append(screen); + d_ptr->region += screen->region(); +} + +void QMultiScreen::removeSubScreen(QScreen *screen) +{ + d_ptr->screens.removeAll(screen); + d_ptr->region -= screen->region(); +} + +QT_END_NAMESPACE + +#endif // QT_NO_QWS_MULTISCREEN diff --git a/src/gui/embedded/qscreenmulti_qws_p.h b/src/gui/embedded/qscreenmulti_qws_p.h new file mode 100644 index 0000000..e01b167 --- /dev/null +++ b/src/gui/embedded/qscreenmulti_qws_p.h @@ -0,0 +1,114 @@ +/**************************************************************************** +** +** 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$ +** +****************************************************************************/ + +#ifndef QMULTISCREEN_QWS_P_H +#define QMULTISCREEN_QWS_P_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists for the convenience +// of the QLibrary class. This header file may change from +// version to version without notice, or even be removed. +// +// We mean it. +// + +#include <qscreen_qws.h> + +QT_BEGIN_NAMESPACE + +#ifndef QT_NO_QWS_MULTISCREEN + +class QMultiScreenPrivate; + +class QMultiScreen : public QScreen +{ +public: + QMultiScreen(int displayId); + ~QMultiScreen(); + bool initDevice(); + bool connect(const QString &displaySpec); + void disconnect(); + void shutdownDevice(); + void setMode(int,int,int); + bool supportsDepth(int) const; + + void save(); + void restore(); + void blank(bool on); + + bool onCard(const unsigned char *) const; + bool onCard(const unsigned char *, ulong& out_offset) const; + + bool isInterlaced() const; + + int memoryNeeded(const QString&); + int sharedRamSize(void *); + + void haltUpdates(); + void resumeUpdates(); + + void exposeRegion(QRegion r, int changing); + + void blit(const QImage &img, const QPoint &topLeft, const QRegion ®ion); + void solidFill(const QColor &color, const QRegion ®ion); + void blit(QWSWindow *bs, const QRegion &clip); + void setDirty(const QRect&); + + QWSWindowSurface* createSurface(QWidget *widget) const; + QWSWindowSurface* createSurface(const QString &key) const; + + QList<QScreen*> subScreens() const; + QRegion region() const; + +private: + void addSubScreen(QScreen *screen); + void removeSubScreen(QScreen *screen); + + QMultiScreenPrivate *d_ptr; +}; + + +QT_END_NAMESPACE +#endif // QT_NO_QWS_MULTISCREEN +#endif // QMULTISCREEN_QWS_P_H diff --git a/src/gui/embedded/qscreenproxy_qws.cpp b/src/gui/embedded/qscreenproxy_qws.cpp new file mode 100644 index 0000000..5b8f6f0 --- /dev/null +++ b/src/gui/embedded/qscreenproxy_qws.cpp @@ -0,0 +1,631 @@ +/**************************************************************************** +** +** 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 <qscreenproxy_qws.h> + +#ifndef QT_NO_QWS_PROXYSCREEN + +#include <qregexp.h> + +#ifndef QT_NO_QWS_CURSOR + +/*! + \class QProxyScreenCursor + \since 4.5 + \ingroup qws + \brief The QProxyScreenCursor class provides a generic interface to + QScreenCursor implementations. +*/ + +/*! + Constructs a proxy screen cursor. +*/ +QProxyScreenCursor::QProxyScreenCursor() + : QScreenCursor(), realCursor(0), d_ptr(0) +{ +} + +/*! + Destroys the proxy screen cursor. +*/ +QProxyScreenCursor::~QProxyScreenCursor() +{ +} + +/*! + Sets the real screen cursor to be used for the proxy screen cursor to + the \a cursor specified. + + \sa screenCursor() +*/ +void QProxyScreenCursor::setScreenCursor(QScreenCursor *cursor) +{ + realCursor = cursor; + configure(); +} + +/*! + Returns the real screen cursor used by the proxy screen cursor. + + \sa setScreenCursor() +*/ +QScreenCursor* QProxyScreenCursor::screenCursor() const +{ + return realCursor; +} + +/*! + \reimp +*/ +void QProxyScreenCursor::set(const QImage &image, int hotx, int hoty) +{ + if (realCursor) { + hotspot = QPoint(hotx, hoty); + cursor = image; + size = image.size(); + realCursor->set(image, hotx, hoty); + } else { + QScreenCursor::set(image, hotx, hoty); + } +} + +/*! + \reimp +*/ +void QProxyScreenCursor::move(int x, int y) +{ + if (realCursor) { + pos = QPoint(x, y); + realCursor->move(x, y); + } else { + QScreenCursor::move(x, y); + } +} + +/*! + \reimp +*/ +void QProxyScreenCursor::show() +{ + if (realCursor) { + realCursor->show(); + enable = true; + } else { + QScreenCursor::show(); + } +} + +/*! + \reimp +*/ +void QProxyScreenCursor::hide() +{ + if (realCursor) { + realCursor->hide(); + enable = false; + } else { + QScreenCursor::hide(); + } +} + +/*! + \internal +*/ +void QProxyScreenCursor::configure() +{ + if (!realCursor) + return; + + cursor = realCursor->cursor; + size = realCursor->size; + pos = realCursor->pos; + hotspot = realCursor->hotspot; + enable = realCursor->enable; + hwaccel = realCursor->hwaccel; + supportsAlpha = realCursor->supportsAlpha; +} + +#endif // QT_NO_QWS_CURSOR + +/*! + \class QProxyScreen + \ingroup qws + \brief The QProxyScreen class provides a generic interface to QScreen implementations. +*/ + +/*! + \fn QProxyScreen::QProxyScreen(int displayId, ClassId classId) + + Constructs a proxy screen with the given \a displayId and \a classId. +*/ +QProxyScreen::QProxyScreen(int displayId, QScreen::ClassId classId) + : QScreen(displayId, classId), realScreen(0), d_ptr(0) +{ +} + +/*! + Destroys the proxy screen. +*/ +QProxyScreen::~QProxyScreen() +{ +} + +/*! + Sets the real \a screen to be used by the proxy screen. + + \sa screen() +*/ +void QProxyScreen::setScreen(QScreen *screen) +{ + realScreen = screen; + configure(); +} + +/*! + Returns the real screen used by the proxy screen. + + \sa setScreen() +*/ +QScreen* QProxyScreen::screen() const +{ + return realScreen; +} + + +/*! + \internal +*/ +void QProxyScreen::configure() +{ + if (!realScreen) + return; + + d = realScreen->depth(); + w = realScreen->width(); + h = realScreen->height(); + dw = realScreen->deviceWidth(); + dh = realScreen->deviceHeight(); + lstep = realScreen->linestep(); + data = realScreen->base(); + lstep = realScreen->linestep(); + size = realScreen->screenSize(); + physWidth = realScreen->physicalWidth(); + physHeight = realScreen->physicalHeight(); + pixeltype = realScreen->pixelType(); +#if Q_BYTE_ORDER == Q_BIG_ENDIAN + setFrameBufferLittleEndian(realScreen->frameBufferLittleEndian()); +#endif + + setOffset(realScreen->offset()); + setPixelFormat(realScreen->pixelFormat()); + +#ifdef QT_QWS_CLIENTBLIT + setSupportsBlitInClients(realScreen->supportsBlitInClients()); +#endif +} + +/*! + \internal + Returns the display ID that corresponds to the given \a spec. +*/ +static int getDisplayId(const QString &spec) +{ + QRegExp regexp(QLatin1String(":(\\d+)\\b")); + if (regexp.lastIndexIn(spec) != -1) { + const QString capture = regexp.cap(1); + return capture.toInt(); + } + return 0; +} + +/*! + \reimp +*/ +bool QProxyScreen::connect(const QString &displaySpec) +{ + const int id = getDisplayId(displaySpec); + realScreen = qt_get_screen(id, displaySpec.toLatin1().constData()); + configure(); + + return true; +} + +/*! + \reimp +*/ +void QProxyScreen::exposeRegion(QRegion r, int changing) +{ + if (!realScreen) { + QScreen::exposeRegion(r, changing); + return; + } + + realScreen->exposeRegion(r, changing); + + const QVector<QRect> rects = r.rects(); + for (int i = 0; i < rects.size(); ++i) + setDirty(rects.at(i)); +} + +/*! + \reimp +*/ +void QProxyScreen::blit(const QImage &image, const QPoint &topLeft, + const QRegion ®ion) +{ + if (!realScreen) { + QScreen::blit(image, topLeft, region); + return; + } + + realScreen->blit(image, topLeft, region); +} + +/*! + \reimp +*/ +void QProxyScreen::solidFill(const QColor &color, const QRegion ®ion) +{ + if (!realScreen) { + QScreen::solidFill(color, region); + return; + } + realScreen->solidFill(color, region); +} + +/*! + \reimp +*/ +QSize QProxyScreen::mapToDevice(const QSize &s) const +{ + if (!realScreen) + return QScreen::mapToDevice(s); + + return realScreen->mapToDevice(s); +} + +/*! + \reimp +*/ +QSize QProxyScreen::mapFromDevice(const QSize &s) const +{ + if (!realScreen) + return QScreen::mapFromDevice(s); + + return realScreen->mapFromDevice(s); +} + +/*! + \reimp +*/ +QPoint QProxyScreen::mapToDevice(const QPoint &p, const QSize &s) const +{ + if (!realScreen) + return QScreen::mapToDevice(p, s); + + return realScreen->mapToDevice(p, s); +} + +/*! + \reimp +*/ +QPoint QProxyScreen::mapFromDevice(const QPoint &p, const QSize &s) const +{ + if (!realScreen) + return QScreen::mapFromDevice(p, s); + + return realScreen->mapFromDevice(p, s); +} + +/*! + \reimp +*/ +QRect QProxyScreen::mapToDevice(const QRect &r, const QSize &s) const +{ + if (!realScreen) + return QScreen::mapToDevice(r, s); + + return realScreen->mapToDevice(r, s); +} + +/*! + \reimp +*/ +QRect QProxyScreen::mapFromDevice(const QRect &r, const QSize &s) const +{ + if (!realScreen) + return QScreen::mapFromDevice(r, s); + + return realScreen->mapFromDevice(r, s); +} + +/*! + \reimp +*/ +QRegion QProxyScreen::mapToDevice(const QRegion &r, const QSize &s) const +{ + if (!realScreen) + return QScreen::mapToDevice(r, s); + + return realScreen->mapToDevice(r, s); +} + +/*! + \reimp +*/ +QRegion QProxyScreen::mapFromDevice(const QRegion &r, const QSize &s) const +{ + if (!realScreen) + return QScreen::mapFromDevice(r, s); + + return realScreen->mapFromDevice(r, s); +} + +/*! + \reimp +*/ +void QProxyScreen::disconnect() +{ + if (realScreen) { + realScreen->disconnect(); + delete realScreen; + realScreen = 0; + } +} + +/*! +*/ +bool QProxyScreen::initDevice() +{ + if (realScreen) + return realScreen->initDevice(); + + return false; +} + +/*! + \reimp +*/ +void QProxyScreen::shutdownDevice() +{ + if (realScreen) + realScreen->shutdownDevice(); +} + +/*! + \reimp +*/ +void QProxyScreen::setMode(int w,int h, int d) +{ + if (realScreen) { + realScreen->setMode(w, h, d); + } else { + QScreen::dw = QScreen::w = w; + QScreen::dh = QScreen::h = h; + QScreen::d = d; + } + configure(); + exposeRegion(region(), 0); +} + +/*! + \reimp +*/ +bool QProxyScreen::supportsDepth(int depth) const +{ + if (realScreen) + return realScreen->supportsDepth(depth); + return false; +} + +/*! + \reimp +*/ +void QProxyScreen::save() +{ + if (realScreen) + realScreen->save(); + QScreen::save(); +} + +/*! + \reimp +*/ +void QProxyScreen::restore() +{ + if (realScreen) + realScreen->restore(); + QScreen::restore(); +} + +/*! + \reimp +*/ +void QProxyScreen::blank(bool on) +{ + if (realScreen) + realScreen->blank(on); +} + +/*! + \reimp +*/ +bool QProxyScreen::onCard(const unsigned char *ptr) const +{ + if (realScreen) + return realScreen->onCard(ptr); + return false; +} + +/*! + \reimp +*/ +bool QProxyScreen::onCard(const unsigned char *ptr, ulong &offset) const +{ + if (realScreen) + return realScreen->onCard(ptr, offset); + return false; +} + +/*! + \reimp +*/ +bool QProxyScreen::isInterlaced() const +{ + if (realScreen) + return realScreen->isInterlaced(); + return false; +} + +/*! + \reimp +*/ +bool QProxyScreen::isTransformed() const +{ + if (realScreen) + return realScreen->isTransformed(); + return QScreen::isTransformed(); +} + +/*! + \reimp +*/ +int QProxyScreen::transformOrientation() const +{ + if (realScreen) + return realScreen->transformOrientation(); + return QScreen::transformOrientation(); +} + +/*! +\reimp +*/ +int QProxyScreen::memoryNeeded(const QString &str) +{ + if (realScreen) + return realScreen->memoryNeeded(str); + else + return QScreen::memoryNeeded(str); +} + +/*! +\reimp +*/ +int QProxyScreen::sharedRamSize(void *ptr) +{ + if (realScreen) + return realScreen->sharedRamSize(ptr); + else + return QScreen::sharedRamSize(ptr); +} + +/*! +\reimp +*/ +void QProxyScreen::haltUpdates() +{ + if (realScreen) + realScreen->haltUpdates(); +} + +/*! +\reimp +*/ +void QProxyScreen::resumeUpdates() +{ + if (realScreen) + realScreen->resumeUpdates(); +} + +/*! + \reimp +*/ +void QProxyScreen::setDirty(const QRect &rect) +{ + if (realScreen) + realScreen->setDirty(rect); +} + +/*! + \reimp +*/ +QWSWindowSurface* QProxyScreen::createSurface(QWidget *widget) const +{ + if (realScreen) + return realScreen->createSurface(widget); + + return QScreen::createSurface(widget); +} + +/*! + \reimp +*/ +QWSWindowSurface* QProxyScreen::createSurface(const QString &key) const +{ + if (realScreen) + return realScreen->createSurface(key); + + return QScreen::createSurface(key); +} + +/*! + \reimp +*/ +QList<QScreen*> QProxyScreen::subScreens() const +{ + if (realScreen) + return realScreen->subScreens(); + + return QScreen::subScreens(); +} + +/*! + \reimp +*/ +QRegion QProxyScreen::region() const +{ + if (realScreen) + return realScreen->region(); + else + return QScreen::region(); +} + +#endif // QT_NO_QWS_PROXYSCREEN diff --git a/src/gui/embedded/qscreenproxy_qws.h b/src/gui/embedded/qscreenproxy_qws.h new file mode 100644 index 0000000..6373c2a --- /dev/null +++ b/src/gui/embedded/qscreenproxy_qws.h @@ -0,0 +1,153 @@ +/**************************************************************************** +** +** 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$ +** +****************************************************************************/ + +#ifndef QPROXYSCREEN_QWS_H +#define QPROXYSCREEN_QWS_H + +#include <QtGui/qscreen_qws.h> + +#ifndef QT_NO_QWS_PROXYSCREEN + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +QT_MODULE(Gui) + +class QProxyScreenPrivate; + +#ifndef QT_NO_QWS_CURSOR + +class QProxyScreenCursorPrivate; + +class Q_GUI_EXPORT QProxyScreenCursor : public QScreenCursor +{ +public: + QProxyScreenCursor(); + ~QProxyScreenCursor(); + + void setScreenCursor(QScreenCursor *cursor); + QScreenCursor* screenCursor() const; + + void set(const QImage &image, int hotx, int hoty); + void move(int x, int y); + void show(); + void hide(); + +private: + void configure(); + + QScreenCursor *realCursor; + QProxyScreenCursorPrivate *d_ptr; +}; + +#endif // QT_NO_QWS_CURSOR + +class Q_GUI_EXPORT QProxyScreen : public QScreen +{ +public: + QProxyScreen(int display_id, ClassId = ProxyClass); + ~QProxyScreen(); + + void setScreen(QScreen *screen); + QScreen *screen() const; + + QSize mapToDevice(const QSize &s) const; + QSize mapFromDevice(const QSize &s) const; + + QPoint mapToDevice(const QPoint &, const QSize &) const; + QPoint mapFromDevice(const QPoint &, const QSize &) const; + + QRect mapToDevice(const QRect &, const QSize &) const; + QRect mapFromDevice(const QRect &, const QSize &) const; + + QRegion mapToDevice(const QRegion &, const QSize &) const; + QRegion mapFromDevice(const QRegion &, const QSize &) const; + + bool connect(const QString &displaySpec); + bool initDevice(); + void shutdownDevice(); + void disconnect(); + + void setMode(int width, int height, int depth); + bool supportsDepth(int) const; + + void save(); + void restore(); + void blank(bool on); + + bool onCard(const unsigned char *) const; + bool onCard(const unsigned char *, ulong& out_offset) const; + + bool isInterlaced() const; + bool isTransformed() const; + int transformOrientation() const; + + int memoryNeeded(const QString&); + int sharedRamSize(void *); + + void haltUpdates(); + void resumeUpdates(); + + void exposeRegion(QRegion r, int changing); + void blit(const QImage &img, const QPoint &topLeft, const QRegion ®ion); + void solidFill(const QColor &color, const QRegion ®ion); + void setDirty(const QRect&); + + QWSWindowSurface* createSurface(QWidget *widget) const; + QWSWindowSurface* createSurface(const QString &key) const; + + QList<QScreen*> subScreens() const; + QRegion region() const; + +private: + void configure(); + + QScreen *realScreen; + QProxyScreenPrivate *d_ptr; +}; + +QT_END_NAMESPACE + +QT_END_HEADER + +#endif // QT_NO_QWS_PROXYSCREEN +#endif // QPROXYSCREEN_QWS_H diff --git a/src/gui/embedded/qscreentransformed_qws.cpp b/src/gui/embedded/qscreentransformed_qws.cpp new file mode 100644 index 0000000..f988789 --- /dev/null +++ b/src/gui/embedded/qscreentransformed_qws.cpp @@ -0,0 +1,734 @@ +/**************************************************************************** +** +** 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 "qscreentransformed_qws.h" + +#ifndef QT_NO_QWS_TRANSFORMED +#include <qscreendriverfactory_qws.h> +#include <qvector.h> +#include <private/qpainter_p.h> +#include <private/qmemrotate_p.h> +#include <qmatrix.h> + +#include <unistd.h> +#include <sys/ioctl.h> +#include <sys/types.h> +#include <sys/stat.h> +#include <sys/mman.h> +#include <fcntl.h> +#include <errno.h> + +#include <qwindowsystem_qws.h> +#include <qwsdisplay_qws.h> + +QT_BEGIN_NAMESPACE + +//#define QT_REGION_DEBUG + +#ifdef QT_REGION_DEBUG +#include <QDebug> +#endif + +class QTransformedScreenPrivate +{ +public: + QTransformedScreenPrivate(QTransformedScreen *parent); + + void configure(); + + QTransformedScreen::Transformation transformation; +#ifdef QT_QWS_DEPTH_GENERIC + bool doGenericColors; +#endif + QTransformedScreen *q; +}; + +QTransformedScreenPrivate::QTransformedScreenPrivate(QTransformedScreen *parent) + : transformation(QTransformedScreen::None), +#ifdef QT_QWS_DEPTH_GENERIC + doGenericColors(false), +#endif + q(parent) +{ +} + +extern "C" +#ifndef QT_BUILD_GUI_LIB +Q_DECL_EXPORT +#endif +void qws_setScreenTransformation(QScreen *that, int t) +{ + QTransformedScreen *tscreen = static_cast<QTransformedScreen*>(that); + tscreen->setTransformation((QTransformedScreen::Transformation)t); +} + +// --------------------------------------------------------------------------- +// Transformed Screen +// --------------------------------------------------------------------------- + +/*! + \internal + + \class QTransformedScreen + \ingroup qws + + \brief The QTransformedScreen class implements a screen driver for + a transformed screen. + + Note that this class is only available in \l{Qt for Embedded Linux}. + Custom screen drivers can be added by subclassing the + QScreenDriverPlugin class, using the QScreenDriverFactory class to + dynamically load the driver into the application, but there should + only be one screen object per application. + + Use the QScreen::isTransformed() function to determine if a screen + is transformed. The QTransformedScreen class itself provides means + of rotating the screen with its setTransformation() function; the + transformation() function returns the currently set rotation in + terms of the \l Transformation enum (which describes the various + available rotation settings). Alternatively, QTransformedScreen + provides an implementation of the QScreen::transformOrientation() + function, returning the current rotation as an integer value. + + \sa QScreen, QScreenDriverPlugin, {Running Applications} +*/ + +/*! + \enum QTransformedScreen::Transformation + + This enum describes the various rotations a transformed screen can + have. + + \value None No rotation + \value Rot90 90 degrees rotation + \value Rot180 180 degrees rotation + \value Rot270 270 degrees rotation +*/ + +/*! + \fn bool QTransformedScreen::isTransformed() const + \reimp +*/ + +/*! + Constructs a QTransformedScreen object. The \a displayId argument + identifies the Qt for Embedded Linux server to connect to. +*/ +QTransformedScreen::QTransformedScreen(int displayId) + : QProxyScreen(displayId, QScreen::TransformedClass) +{ + d_ptr = new QTransformedScreenPrivate(this); + d_ptr->transformation = None; + +#ifdef QT_REGION_DEBUG + qDebug() << "QTransformedScreen::QTransformedScreen"; +#endif +} + +void QTransformedScreenPrivate::configure() +{ + // ###: works because setTransformation recalculates unconditionally + q->setTransformation(transformation); +} + +/*! + Destroys the QTransformedScreen object. +*/ +QTransformedScreen::~QTransformedScreen() +{ + delete d_ptr; +} + +static int getDisplayId(const QString &spec) +{ + QRegExp regexp(QLatin1String(":(\\d+)\\b")); + if (regexp.lastIndexIn(spec) != -1) { + const QString capture = regexp.cap(1); + return capture.toInt(); + } + return 0; +} + +static QTransformedScreen::Transformation filterTransformation(QString &spec) +{ + QRegExp regexp(QLatin1String("\\bRot(\\d+):?\\b"), Qt::CaseInsensitive); + if (regexp.indexIn(spec) == -1) + return QTransformedScreen::None; + + const int degrees = regexp.cap(1).toInt(); + spec.remove(regexp.pos(0), regexp.matchedLength()); + + return static_cast<QTransformedScreen::Transformation>(degrees / 90); +} + +/*! + \reimp +*/ +bool QTransformedScreen::connect(const QString &displaySpec) +{ + QString dspec = displaySpec.trimmed(); + if (dspec.startsWith(QLatin1String("Transformed:"), Qt::CaseInsensitive)) + dspec = dspec.mid(QString(QLatin1String("Transformed:")).size()); + else if (!dspec.compare(QLatin1String("Transformed"), Qt::CaseInsensitive)) + dspec = QString(); + + const QString displayIdSpec = QString(QLatin1String(" :%1")).arg(displayId); + if (dspec.endsWith(displayIdSpec)) + dspec = dspec.left(dspec.size() - displayIdSpec.size()); + + d_ptr->transformation = filterTransformation(dspec); + + QString driver = dspec; + int colon = driver.indexOf(QLatin1Char(':')); + if (colon >= 0) + driver.truncate(colon); + + if (!QScreenDriverFactory::keys().contains(driver, Qt::CaseInsensitive)) + if (!dspec.isEmpty()) + dspec.prepend(QLatin1String(":")); + + const int id = getDisplayId(dspec); + QScreen *s = qt_get_screen(id, dspec.toLatin1().constData()); + setScreen(s); + +#ifdef QT_QWS_DEPTH_GENERIC + d_ptr->doGenericColors = dspec.contains(QLatin1String("genericcolors")); +#endif + + d_ptr->configure(); + + // XXX + qt_screen = this; + + return true; +} + +/*! + Returns the currently set rotation. + + \sa setTransformation(), QScreen::transformOrientation() +*/ +QTransformedScreen::Transformation QTransformedScreen::transformation() const +{ + return d_ptr->transformation; +} + +/*! + \reimp +*/ +int QTransformedScreen::transformOrientation() const +{ + return (int)d_ptr->transformation; +} + +/*! + \reimp +*/ +void QTransformedScreen::exposeRegion(QRegion region, int changing) +{ + if (!data || d_ptr->transformation == None) { + QProxyScreen::exposeRegion(region, changing); + return; + } + QScreen::exposeRegion(region, changing); +} + +/*! + Rotates this screen object according to the specified \a transformation. + + \sa transformation() +*/ +void QTransformedScreen::setTransformation(Transformation transformation) +{ + d_ptr->transformation = transformation; + QSize size = mapFromDevice(QSize(dw, dh)); + w = size.width(); + h = size.height(); + + const QScreen *s = screen(); + size = mapFromDevice(QSize(s->physicalWidth(), s->physicalHeight())); + physWidth = size.width(); + physHeight = size.height(); + +#ifdef QT_REGION_DEBUG + qDebug() << "QTransformedScreen::setTransformation" << transformation + << "size" << w << h << "dev size" << dw << dh; +#endif + +} + +static inline QRect correctNormalized(const QRect &r) { + const int x1 = qMin(r.left(), r.right()); + const int x2 = qMax(r.left(), r.right()); + const int y1 = qMin(r.top(), r.bottom()); + const int y2 = qMax(r.top(), r.bottom()); + + return QRect( QPoint(x1,y1), QPoint(x2,y2) ); +} + +template <class DST, class SRC> +static inline void blit90(QScreen *screen, const QImage &image, + const QRect &rect, const QPoint &topLeft) +{ + const SRC *src = (const SRC*)(image.scanLine(rect.top())) + rect.left(); + DST *dest = (DST*)(screen->base() + topLeft.y() * screen->linestep()) + + topLeft.x(); + qt_memrotate90(src, rect.width(), rect.height(), image.bytesPerLine(), + dest, screen->linestep()); +} + +template <class DST, class SRC> +static inline void blit180(QScreen *screen, const QImage &image, + const QRect &rect, const QPoint &topLeft) +{ + const SRC *src = (const SRC*)(image.scanLine(rect.top())) + rect.left(); + DST *dest = (DST*)(screen->base() + topLeft.y() * screen->linestep()) + + topLeft.x(); + qt_memrotate180(src, rect.width(), rect.height(), image.bytesPerLine(), + dest, screen->linestep()); +} + +template <class DST, class SRC> +static inline void blit270(QScreen *screen, const QImage &image, + const QRect &rect, const QPoint &topLeft) +{ + const SRC *src = (const SRC *)(image.scanLine(rect.top())) + rect.left(); + DST *dest = (DST*)(screen->base() + topLeft.y() * screen->linestep()) + + topLeft.x(); + qt_memrotate270(src, rect.width(), rect.height(), image.bytesPerLine(), + dest, screen->linestep()); +} + +typedef void (*BlitFunc)(QScreen *, const QImage &, const QRect &, const QPoint &); + +#define SET_BLIT_FUNC(dst, src, rotation, func) \ +do { \ + switch (rotation) { \ + case Rot90: \ + func = blit90<dst, src>; \ + break; \ + case Rot180: \ + func = blit180<dst, src>; \ + break; \ + case Rot270: \ + func = blit270<dst, src>; \ + break; \ + default: \ + break; \ + } \ +} while (0) + +/*! + \reimp +*/ +void QTransformedScreen::blit(const QImage &image, const QPoint &topLeft, + const QRegion ®ion) +{ + const Transformation trans = d_ptr->transformation; + if (trans == None) { + QProxyScreen::blit(image, topLeft, region); + return; + } + + const QVector<QRect> rects = region.rects(); + const QRect bound = QRect(0, 0, QScreen::w, QScreen::h) + & QRect(topLeft, image.size()); + + BlitFunc func = 0; +#ifdef QT_QWS_DEPTH_GENERIC + if (d_ptr->doGenericColors && depth() == 16) { + if (image.depth() == 16) + SET_BLIT_FUNC(qrgb_generic16, quint16, trans, func); + else + SET_BLIT_FUNC(qrgb_generic16, quint32, trans, func); + } else +#endif + switch (depth()) { +#ifdef QT_QWS_DEPTH_32 + case 32: +#ifdef QT_QWS_DEPTH_16 + if (image.depth() == 16) + SET_BLIT_FUNC(quint32, quint16, trans, func); + else +#endif + SET_BLIT_FUNC(quint32, quint32, trans, func); + break; +#endif +#if defined(QT_QWS_DEPTH_24) || defined(QT_QWS_DEPTH18) + case 24: + case 18: + SET_BLIT_FUNC(quint24, quint24, trans, func); + break; +#endif +#if defined(QT_QWS_DEPTH_16) || defined(QT_QWS_DEPTH_15) || defined(QT_QWS_DEPTH_12) + case 16: + case 15: + case 12: + if (image.depth() == 16) + SET_BLIT_FUNC(quint16, quint16, trans, func); + else + SET_BLIT_FUNC(quint16, quint32, trans, func); + break; +#endif +#ifdef QT_QWS_DEPTH_8 + case 8: + if (image.depth() == 16) + SET_BLIT_FUNC(quint8, quint16, trans, func); + else + SET_BLIT_FUNC(quint8, quint32, trans, func); + break; +#endif + default: + return; + } + if (!func) + return; + + QWSDisplay::grab(); + for (int i = 0; i < rects.size(); ++i) { + const QRect r = rects.at(i) & bound; + + QPoint dst; + switch (trans) { + case Rot90: + dst = mapToDevice(r.topRight(), QSize(w, h)); + break; + case Rot180: + dst = mapToDevice(r.bottomRight(), QSize(w, h)); + break; + case Rot270: + dst = mapToDevice(r.bottomLeft(), QSize(w, h)); + break; + default: + break; + } + func(this, image, r.translated(-topLeft), dst); + } + QWSDisplay::ungrab(); + +} + +/*! + \reimp +*/ +void QTransformedScreen::solidFill(const QColor &color, const QRegion ®ion) +{ + const QRegion tr = mapToDevice(region, QSize(w,h)); + + Q_ASSERT(tr.boundingRect() == mapToDevice(region.boundingRect(), QSize(w,h))); + +#ifdef QT_REGION_DEBUG + qDebug() << "QTransformedScreen::solidFill region" << region << "transformed" << tr; +#endif + QProxyScreen::solidFill(color, tr); +} + +/*! + \reimp +*/ +QSize QTransformedScreen::mapToDevice(const QSize &s) const +{ + switch (d_ptr->transformation) { + case None: + case Rot180: + break; + case Rot90: + case Rot270: + return QSize(s.height(), s.width()); + break; + } + return s; +} + +/*! + \reimp +*/ +QSize QTransformedScreen::mapFromDevice(const QSize &s) const +{ + switch (d_ptr->transformation) { + case None: + case Rot180: + break; + case Rot90: + case Rot270: + return QSize(s.height(), s.width()); + break; + } + return s; +} + +/*! + \reimp +*/ +QPoint QTransformedScreen::mapToDevice(const QPoint &p, const QSize &s) const +{ + QPoint rp(p); + + switch (d_ptr->transformation) { + case None: + break; + case Rot90: + rp.setX(p.y()); + rp.setY(s.width() - p.x() - 1); + break; + case Rot180: + rp.setX(s.width() - p.x() - 1); + rp.setY(s.height() - p.y() - 1); + break; + case Rot270: + rp.setX(s.height() - p.y() - 1); + rp.setY(p.x()); + break; + } + + return rp; +} + +/*! + \reimp +*/ +QPoint QTransformedScreen::mapFromDevice(const QPoint &p, const QSize &s) const +{ + QPoint rp(p); + + switch (d_ptr->transformation) { + case None: + break; + case Rot90: + rp.setX(s.height() - p.y() - 1); + rp.setY(p.x()); + break; + case Rot180: + rp.setX(s.width() - p.x() - 1); + rp.setY(s.height() - p.y() - 1); + break; + case Rot270: + rp.setX(p.y()); + rp.setY(s.width() - p.x() - 1); + break; + } + + return rp; +} + +/*! + \reimp +*/ +QRect QTransformedScreen::mapToDevice(const QRect &r, const QSize &s) const +{ + if (r.isNull()) + return QRect(); + + QRect tr; + switch (d_ptr->transformation) { + case None: + tr = r; + break; + case Rot90: + tr.setCoords(r.y(), s.width() - r.x() - 1, + r.bottom(), s.width() - r.right() - 1); + break; + case Rot180: + tr.setCoords(s.width() - r.x() - 1, s.height() - r.y() - 1, + s.width() - r.right() - 1, s.height() - r.bottom() - 1); + break; + case Rot270: + tr.setCoords(s.height() - r.y() - 1, r.x(), + s.height() - r.bottom() - 1, r.right()); + break; + } + + return correctNormalized(tr); +} + +/*! + \reimp +*/ +QRect QTransformedScreen::mapFromDevice(const QRect &r, const QSize &s) const +{ + if (r.isNull()) + return QRect(); + + QRect tr; + switch (d_ptr->transformation) { + case None: + tr = r; + break; + case Rot90: + tr.setCoords(s.height() - r.y() - 1, r.x(), + s.height() - r.bottom() - 1, r.right()); + break; + case Rot180: + tr.setCoords(s.width() - r.x() - 1, s.height() - r.y() - 1, + s.width() - r.right() - 1, s.height() - r.bottom() - 1); + break; + case Rot270: + tr.setCoords(r.y(), s.width() - r.x() - 1, + r.bottom(), s.width() - r.right() - 1); + break; + } + + return correctNormalized(tr); +} + +/*! + \reimp +*/ +QRegion QTransformedScreen::mapToDevice(const QRegion &rgn, const QSize &s) const +{ + if (d_ptr->transformation == None) + return QProxyScreen::mapToDevice(rgn, s); + +#ifdef QT_REGION_DEBUG + qDebug() << "mapToDevice size" << s << "rgn: " << rgn; +#endif + QRect tr; + QRegion trgn; + QVector<QRect> a = rgn.rects(); + const QRect *r = a.data(); + + int w = s.width(); + int h = s.height(); + int size = a.size(); + + switch (d_ptr->transformation) { + case None: + break; + case Rot90: + for (int i = 0; i < size; i++, r++) { + tr.setCoords(r->y(), w - r->x() - 1, + r->bottom(), w - r->right() - 1); + trgn |= correctNormalized(tr); + } + break; + case Rot180: + for (int i = 0; i < size; i++, r++) { + tr.setCoords(w - r->x() - 1, h - r->y() - 1, + w - r->right() - 1, h - r->bottom() - 1); + trgn |= correctNormalized(tr); + } + break; + case Rot270: + for (int i = 0; i < size; i++, r++) { + tr.setCoords(h - r->y() - 1, r->x(), + h - r->bottom() - 1, r->right()); + trgn |= correctNormalized(tr); + } + break; + } +#ifdef QT_REGION_DEBUG + qDebug() << "mapToDevice trgn: " << trgn; +#endif + return trgn; +} + +/*! + \reimp +*/ +QRegion QTransformedScreen::mapFromDevice(const QRegion &rgn, const QSize &s) const +{ + if (d_ptr->transformation == None) + return QProxyScreen::mapFromDevice(rgn, s); + +#ifdef QT_REGION_DEBUG + qDebug() << "fromDevice: realRegion count: " << rgn.rects().size() << " isEmpty? " << rgn.isEmpty() << " bounds:" << rgn.boundingRect(); +#endif + QRect tr; + QRegion trgn; + QVector<QRect> a = rgn.rects(); + const QRect *r = a.data(); + + int w = s.width(); + int h = s.height(); + int size = a.size(); + + switch (d_ptr->transformation) { + case None: + break; + case Rot90: + for (int i = 0; i < size; i++, r++) { + tr.setCoords(h - r->y() - 1, r->x(), + h - r->bottom() - 1, r->right()); + trgn |= correctNormalized(tr); + } + break; + case Rot180: + for (int i = 0; i < size; i++, r++) { + tr.setCoords(w - r->x() - 1, h - r->y() - 1, + w - r->right() - 1, h - r->bottom() - 1); + trgn |= correctNormalized(tr); + } + break; + case Rot270: + for (int i = 0; i < size; i++, r++) { + tr.setCoords(r->y(), w - r->x() - 1, + r->bottom(), w - r->right() - 1); + trgn |= correctNormalized(tr); + } + break; + } +#ifdef QT_REGION_DEBUG + qDebug() << "fromDevice: transRegion count: " << trgn.rects().size() << " isEmpty? " << trgn.isEmpty() << " bounds:" << trgn.boundingRect(); +#endif + return trgn; +} + +/*! + \reimp +*/ +void QTransformedScreen::setDirty(const QRect& rect) +{ + const QRect r = mapToDevice(rect, QSize(width(), height())); + QProxyScreen::setDirty(r); +} + +/*! + \reimp +*/ +QRegion QTransformedScreen::region() const +{ + QRegion deviceRegion = QProxyScreen::region(); + return mapFromDevice(deviceRegion, QSize(deviceWidth(), deviceHeight())); +} + +QT_END_NAMESPACE + +#endif // QT_NO_QWS_TRANSFORMED diff --git a/src/gui/embedded/qscreentransformed_qws.h b/src/gui/embedded/qscreentransformed_qws.h new file mode 100644 index 0000000..e2d5a33 --- /dev/null +++ b/src/gui/embedded/qscreentransformed_qws.h @@ -0,0 +1,103 @@ +/**************************************************************************** +** +** 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$ +** +****************************************************************************/ + +#ifndef QSCREENTRANSFORMED_QWS_H +#define QSCREENTRANSFORMED_QWS_H + +#include <QtGui/qscreenproxy_qws.h> + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +QT_MODULE(Gui) + +#ifndef QT_NO_QWS_TRANSFORMED + +class QTransformedScreenPrivate; + +class Q_AUTOTEST_EXPORT QTransformedScreen : public QProxyScreen +{ +public: + explicit QTransformedScreen(int display_id); + ~QTransformedScreen(); + + enum Transformation { None, Rot90, Rot180, Rot270 }; + + void setTransformation(Transformation t); + Transformation transformation() const; + int transformOrientation() const; + + QSize mapToDevice(const QSize &s) const; + QSize mapFromDevice(const QSize &s) const; + + QPoint mapToDevice(const QPoint &, const QSize &) const; + QPoint mapFromDevice(const QPoint &, const QSize &) const; + + QRect mapToDevice(const QRect &, const QSize &) const; + QRect mapFromDevice(const QRect &, const QSize &) const; + + QRegion mapToDevice(const QRegion &, const QSize &) const; + QRegion mapFromDevice(const QRegion &, const QSize &) const; + + bool connect(const QString &displaySpec); + + bool isTransformed() const { return transformation() != None; } + + void exposeRegion(QRegion region, int changing); + void blit(const QImage &img, const QPoint &topLeft, const QRegion ®ion); + void solidFill(const QColor &color, const QRegion ®ion); + void setDirty(const QRect&); + + QRegion region() const; + +private: + friend class QTransformedScreenPrivate; + QTransformedScreenPrivate *d_ptr; +}; + +#endif // QT_NO_QWS_TRANSFORMED + +QT_END_NAMESPACE + +QT_END_HEADER + +#endif // QSCREENTRANSFORMED_QWS_H diff --git a/src/gui/embedded/qscreenvfb_qws.cpp b/src/gui/embedded/qscreenvfb_qws.cpp new file mode 100644 index 0000000..abbe73b --- /dev/null +++ b/src/gui/embedded/qscreenvfb_qws.cpp @@ -0,0 +1,444 @@ +/**************************************************************************** +** +** 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$ +** +****************************************************************************/ + +#ifndef QT_NO_QWS_QVFB + +#define QTOPIA_QVFB_BRIGHTNESS + +#include <stdlib.h> +#include <sys/types.h> +#include <sys/ipc.h> +#include <sys/shm.h> +#include <sys/stat.h> +#include <unistd.h> +#include <fcntl.h> +#include <errno.h> + +#include <qvfbhdr.h> +#include <qscreenvfb_qws.h> +#include <qkbdvfb_qws.h> +#include <qmousevfb_qws.h> +#include <qwindowsystem_qws.h> +#include <qsocketnotifier.h> +#include <qapplication.h> +#include <qscreen_qws.h> +#include <qmousedriverfactory_qws.h> +#include <qkbddriverfactory_qws.h> +#include <qdebug.h> + +QT_BEGIN_NAMESPACE + +class QVFbScreenPrivate +{ +public: + QVFbScreenPrivate(); + ~QVFbScreenPrivate(); + + bool success; + unsigned char *shmrgn; + int brightness; + bool blank; + QVFbHeader *hdr; + QWSMouseHandler *mouse; +#ifndef QT_NO_QWS_KEYBOARD + QWSKeyboardHandler *keyboard; +#endif +}; + +QVFbScreenPrivate::QVFbScreenPrivate() + : mouse(0) + +{ +#ifndef QT_NO_QWS_KEYBOARD + keyboard = 0; +#endif + brightness = 255; + blank = false; +} + +QVFbScreenPrivate::~QVFbScreenPrivate() +{ + delete mouse; +#ifndef QT_NO_QWS_KEYBOARD + delete keyboard; +#endif +} + +/*! + \internal + + \class QVFbScreen + \ingroup qws + + \brief The QVFbScreen class implements a screen driver for the + virtual framebuffer. + + Note that this class is only available in \l{Qt for Embedded Linux}. + Custom screen drivers can be added by subclassing the + QScreenDriverPlugin class, using the QScreenDriverFactory class to + dynamically load the driver into the application, but there should + only be one screen object per application. + + The Qt for Embedded Linux platform provides a \l{The Virtual + Framebuffer}{virtual framebuffer} for development and debugging; + the virtual framebuffer allows Qt for Embedded Linux applications to be + developed on a desktop machine, without switching between consoles + and X11. + + \sa QScreen, QScreenDriverPlugin, {Running Applications} +*/ + +/*! + \fn bool QVFbScreen::connect(const QString & displaySpec) + \reimp +*/ + +/*! + \fn void QVFbScreen::disconnect() + \reimp +*/ + +/*! + \fn bool QVFbScreen::initDevice() + \reimp +*/ + +/*! + \fn void QVFbScreen::restore() + \reimp +*/ + +/*! + \fn void QVFbScreen::save() + \reimp +*/ + +/*! + \fn void QVFbScreen::setDirty(const QRect & r) + \reimp +*/ + +/*! + \fn void QVFbScreen::setMode(int nw, int nh, int nd) + \reimp +*/ + +/*! + \fn void QVFbScreen::shutdownDevice() + \reimp +*/ + +/*! + \fn QVFbScreen::QVFbScreen(int displayId) + + Constructs a QVNCScreen object. The \a displayId argument + identifies the Qt for Embedded Linux server to connect to. +*/ +QVFbScreen::QVFbScreen(int display_id) + : QScreen(display_id, VFbClass), d_ptr(new QVFbScreenPrivate) +{ + d_ptr->shmrgn = 0; + d_ptr->hdr = 0; + data = 0; +} + +/*! + Destroys this QVFbScreen object. +*/ +QVFbScreen::~QVFbScreen() +{ + delete d_ptr; +} + +static QVFbScreen *connected = 0; + +bool QVFbScreen::connect(const QString &displaySpec) +{ + QStringList displayArgs = displaySpec.split(QLatin1Char(':')); + if (displayArgs.contains(QLatin1String("Gray"))) + grayscale = true; + + key_t key = ftok(QByteArray(QT_VFB_MOUSE_PIPE).replace("%1", QByteArray::number(displayId)), 'b'); + + if (key == -1) + return false; + +#if Q_BYTE_ORDER == Q_BIG_ENDIAN +#ifndef QT_QWS_FRAMEBUFFER_LITTLE_ENDIAN + if (displayArgs.contains(QLatin1String("littleendian"))) +#endif + QScreen::setFrameBufferLittleEndian(true); +#endif + + int shmId = shmget(key, 0, 0); + if (shmId != -1) + d_ptr->shmrgn = (unsigned char *)shmat(shmId, 0, 0); + else + return false; + + if ((long)d_ptr->shmrgn == -1 || d_ptr->shmrgn == 0) { + qDebug("No shmrgn %ld", (long)d_ptr->shmrgn); + return false; + } + + d_ptr->hdr = (QVFbHeader *)d_ptr->shmrgn; + data = d_ptr->shmrgn + d_ptr->hdr->dataoffset; + + dw = w = d_ptr->hdr->width; + dh = h = d_ptr->hdr->height; + d = d_ptr->hdr->depth; + + switch (d) { + case 1: + setPixelFormat(QImage::Format_Mono); + break; + case 8: + setPixelFormat(QImage::Format_Indexed8); + break; + case 12: + setPixelFormat(QImage::Format_RGB444); + break; + case 15: + setPixelFormat(QImage::Format_RGB555); + break; + case 16: + setPixelFormat(QImage::Format_RGB16); + break; + case 18: + setPixelFormat(QImage::Format_RGB666); + break; + case 24: + setPixelFormat(QImage::Format_RGB888); + break; + case 32: + setPixelFormat(QImage::Format_ARGB32_Premultiplied); + break; + } + + lstep = d_ptr->hdr->linestep; + + // Handle display physical size spec. + int dimIdxW = -1; + int dimIdxH = -1; + for (int i = 0; i < displayArgs.size(); ++i) { + if (displayArgs.at(i).startsWith(QLatin1String("mmWidth"))) { + dimIdxW = i; + break; + } + } + for (int i = 0; i < displayArgs.size(); ++i) { + if (displayArgs.at(i).startsWith(QLatin1String("mmHeight"))) { + dimIdxH = i; + break; + } + } + if (dimIdxW >= 0) { + bool ok; + int pos = 7; + if (displayArgs.at(dimIdxW).at(pos) == QLatin1Char('=')) + ++pos; + int pw = displayArgs.at(dimIdxW).mid(pos).toInt(&ok); + if (ok) { + physWidth = pw; + if (dimIdxH < 0) + physHeight = dh*physWidth/dw; + } + } + if (dimIdxH >= 0) { + bool ok; + int pos = 8; + if (displayArgs.at(dimIdxH).at(pos) == QLatin1Char('=')) + ++pos; + int ph = displayArgs.at(dimIdxH).mid(pos).toInt(&ok); + if (ok) { + physHeight = ph; + if (dimIdxW < 0) + physWidth = dw*physHeight/dh; + } + } + if (dimIdxW < 0 && dimIdxH < 0) { + const int dpi = 72; + physWidth = qRound(dw * 25.4 / dpi); + physHeight = qRound(dh * 25.4 / dpi); + } + + qDebug("Connected to VFB server %s: %d x %d x %d %dx%dmm (%dx%ddpi)", displaySpec.toLatin1().data(), + w, h, d, physWidth, physHeight, qRound(dw*25.4/physWidth), qRound(dh*25.4/physHeight) ); + + size = lstep * h; + mapsize = size; + screencols = d_ptr->hdr->numcols; + memcpy(screenclut, d_ptr->hdr->clut, sizeof(QRgb) * screencols); + + connected = this; + + return true; +} + +void QVFbScreen::disconnect() +{ + connected = 0; + if ((long)d_ptr->shmrgn != -1 && d_ptr->shmrgn) { + if (qApp->type() == QApplication::GuiServer && d_ptr->hdr->dataoffset >= (int)sizeof(QVFbHeader)) { + d_ptr->hdr->serverVersion = 0; + } + shmdt((char*)d_ptr->shmrgn); + } +} + +bool QVFbScreen::initDevice() +{ +#ifndef QT_NO_QWS_MOUSE_QVFB + const QString mouseDev = QString(QLatin1String(QT_VFB_MOUSE_PIPE)) + .arg(displayId); + d_ptr->mouse = new QVFbMouseHandler(QLatin1String("QVFbMouse"), mouseDev); + qwsServer->setDefaultMouse("None"); + if (d_ptr->mouse) + d_ptr->mouse->setScreen(this); +#endif + +#if !defined(QT_NO_QWS_KBD_QVFB) && !defined(QT_NO_QWS_KEYBOARD) + const QString keyboardDev = QString(QLatin1String(QT_VFB_KEYBOARD_PIPE)) + .arg(displayId); + d_ptr->keyboard = new QVFbKeyboardHandler(keyboardDev); + qwsServer->setDefaultKeyboard("None"); +#endif + + if (d_ptr->hdr->dataoffset >= (int)sizeof(QVFbHeader)) + d_ptr->hdr->serverVersion = QT_VERSION; + + if(d==8) { + screencols=256; + if (grayscale) { + // Build grayscale palette + for(int loopc=0;loopc<256;loopc++) { + screenclut[loopc]=qRgb(loopc,loopc,loopc); + } + } else { + // 6x6x6 216 color cube + int idx = 0; + for(int ir = 0x0; ir <= 0xff; ir+=0x33) { + for(int ig = 0x0; ig <= 0xff; ig+=0x33) { + for(int ib = 0x0; ib <= 0xff; ib+=0x33) { + screenclut[idx]=qRgb(ir, ig, ib); + idx++; + } + } + } + screencols=idx; + } + memcpy(d_ptr->hdr->clut, screenclut, sizeof(QRgb) * screencols); + d_ptr->hdr->numcols = screencols; + } else if (d == 4) { + int val = 0; + for (int idx = 0; idx < 16; idx++, val += 17) { + screenclut[idx] = qRgb(val, val, val); + } + screencols = 16; + memcpy(d_ptr->hdr->clut, screenclut, sizeof(QRgb) * screencols); + d_ptr->hdr->numcols = screencols; + } else if (d == 1) { + screencols = 2; + screenclut[1] = qRgb(0xff, 0xff, 0xff); + screenclut[0] = qRgb(0, 0, 0); + memcpy(d_ptr->hdr->clut, screenclut, sizeof(QRgb) * screencols); + d_ptr->hdr->numcols = screencols; + } + +#ifndef QT_NO_QWS_CURSOR + QScreenCursor::initSoftwareCursor(); +#endif + return true; +} + +void QVFbScreen::shutdownDevice() +{ +} + +void QVFbScreen::setMode(int ,int ,int) +{ +} + +// save the state of the graphics card +// This is needed so that e.g. we can restore the palette when switching +// between linux virtual consoles. +void QVFbScreen::save() +{ + // nothing to do. +} + +// restore the state of the graphics card. +void QVFbScreen::restore() +{ +} +void QVFbScreen::setDirty(const QRect& rect) +{ + const QRect r = rect.translated(-offset()); + d_ptr->hdr->dirty = true; + d_ptr->hdr->update = d_ptr->hdr->update.united(r); +} + +void QVFbScreen::setBrightness(int b) +{ + if (connected) { + connected->d_ptr->brightness = b; + + QVFbHeader *hdr = connected->d_ptr->hdr; + if (hdr->viewerVersion < 0x040400) // brightness not supported + return; + + const int br = connected->d_ptr->blank ? 0 : b; + if (hdr->brightness != br) { + hdr->brightness = br; + connected->setDirty(connected->region().boundingRect()); + } + } +} + +void QVFbScreen::blank(bool on) +{ + d_ptr->blank = on; + setBrightness(connected->d_ptr->brightness); +} + +#endif // QT_NO_QWS_QVFB + +QT_END_NAMESPACE diff --git a/src/gui/embedded/qscreenvfb_qws.h b/src/gui/embedded/qscreenvfb_qws.h new file mode 100644 index 0000000..1f8edf2 --- /dev/null +++ b/src/gui/embedded/qscreenvfb_qws.h @@ -0,0 +1,86 @@ +/**************************************************************************** +** +** 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$ +** +****************************************************************************/ + +#ifndef QSCREENVFB_QWS_H +#define QSCREENVFB_QWS_H + +#include <QtGui/qscreen_qws.h> +#include <QtGui/qvfbhdr.h> + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +QT_MODULE(Gui) + +#ifndef QT_NO_QWS_QVFB + +class QVFbScreenPrivate; + +class Q_GUI_EXPORT QVFbScreen : public QScreen +{ +public: + explicit QVFbScreen(int display_id); + virtual ~QVFbScreen(); + virtual bool initDevice(); + virtual bool connect(const QString &displaySpec); + virtual void disconnect(); + virtual void shutdownDevice(); + virtual void save(); + virtual void restore(); + virtual void setMode(int nw,int nh,int nd); + virtual void setDirty(const QRect& r); + virtual void blank(bool); +#ifdef QTOPIA_QVFB_BRIGHTNESS + static void setBrightness(int b); +#endif + +private: + QVFbScreenPrivate *d_ptr; +}; + +#endif // QT_NO_QWS_QVFB + +QT_END_NAMESPACE + +QT_END_HEADER + +#endif // QSCREENVFB_QWS_H diff --git a/src/gui/embedded/qsoundqss_qws.cpp b/src/gui/embedded/qsoundqss_qws.cpp new file mode 100644 index 0000000..c72ea91 --- /dev/null +++ b/src/gui/embedded/qsoundqss_qws.cpp @@ -0,0 +1,1498 @@ +/**************************************************************************** +** +** 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 "qsoundqss_qws.h" + +#ifndef QT_NO_SOUND +#include <qbytearray.h> +#include <qlist.h> +#include <qsocketnotifier.h> +#include <qfile.h> +#include <qfileinfo.h> +#include <qstringlist.h> +#include <qevent.h> +#include <qalgorithms.h> +#include <qtimer.h> +#include <qpointer.h> + +#include <unistd.h> +#include <stdlib.h> +#include <fcntl.h> +#include <errno.h> +#include <time.h> +#include <sys/types.h> +#include <sys/stat.h> +#include <sys/ioctl.h> +#include <sys/soundcard.h> + +#include <qdebug.h> + +QT_BEGIN_NAMESPACE + +extern int errno; + +#define QT_QWS_SOUND_16BIT 1 // or 0, or undefined for always 0 +#define QT_QWS_SOUND_STEREO 1 // or 0, or undefined for always 0 + +// Zaurus SL5000D doesn't seem to return any error if setting to 44000 and it fails, +// however 44100 works, 44100 is more common that 44000. +static int sound_speed = 44100; +#ifndef QT_NO_QWS_SOUNDSERVER +extern int qws_display_id; +#define SOUND_PIPE "/tmp/.qt_soundserver-%1" +#endif + +static char *zeroMem = 0; + +struct QRiffChunk { + char id[4]; + quint32 size; + char data[4/*size*/]; +}; + +#if defined(QT_QWS_IPAQ) +static const int sound_fragment_size = 12; +#else +static const int sound_fragment_size = 12; +#endif +static const int sound_buffer_size = 1 << sound_fragment_size; +// nb. there will be an sound startup delay of +// 2^sound_fragment_size / sound_speed seconds. +// (eg. sound_fragment_size==12, sound_speed==44000 means 0.093s delay) + +#ifdef QT_QWS_SOUND_STEREO +static int sound_stereo=QT_QWS_SOUND_STEREO; +#else +static const int sound_stereo=0; +#endif +#ifdef QT_QWS_SOUND_16BIT +static bool sound_16bit=QT_QWS_SOUND_16BIT; +#else +static const bool sound_16bit=false; +#endif + +#ifndef QT_NO_QWS_SOUNDSERVER +class QWSSoundServerClient : public QObject { + Q_OBJECT + +public: + QWSSoundServerClient(QWS_SOCK_BASE *s, QObject* parent); + ~QWSSoundServerClient(); + +public slots: + void sendSoundCompleted(int, int); + void sendDeviceReady(int, int); + void sendDeviceError(int, int, int); + +signals: + void play(int, int, const QString&); + void play(int, int, const QString&, int, int); + void playRaw(int, int, const QString&, int, int, int, int); + + void pause(int, int); + void stop(int, int); + void resume(int, int); + void setVolume(int, int, int, int); + void setMute(int, int, bool); + + void stopAll(int); + + void playPriorityOnly(bool); + + void setSilent( bool ); + +private slots: + void tryReadCommand(); + +private: + void sendClientMessage(QString msg); + int mCurrentID; + int left, right; + bool priExist; + static int lastId; + static int nextId() { return ++lastId; } + QPointer<QWS_SOCK_BASE> socket; +}; + +int QWSSoundServerClient::lastId = 0; + +QWSSoundServerClient::QWSSoundServerClient(QWS_SOCK_BASE *s, QObject* parent) : + QObject( parent ) +{ + socket = s; + priExist = false; + mCurrentID = nextId(); + connect(socket,SIGNAL(readyRead()), + this,SLOT(tryReadCommand())); + connect(socket, SIGNAL(disconnected()), this, SLOT(deleteLater())); +} + +QWSSoundServerClient::~QWSSoundServerClient() +{ + if (priExist) + playPriorityOnly(false); + emit stopAll(mCurrentID); + if (socket) + socket->deleteLater(); +} + +static QString getStringTok(QString &in) +{ + int pos = in.indexOf(QLatin1Char(' ')); + QString ret; + if (pos > 0) { + ret = in.left(pos); + in = in.mid(pos+1); + } else { + ret = in; + in = QString::null; + } + return ret; +} + +static int getNumTok(QString &in) +{ + return getStringTok(in).toInt(); +} + +void QWSSoundServerClient::tryReadCommand() +{ + while ( socket->canReadLine() ) { + QString l = QString::fromAscii(socket->readLine()); + l.truncate(l.length()-1); // chomp + QString functionName = getStringTok(l); + int soundid = getNumTok(l); + if (functionName == QLatin1String("PLAY")) { + emit play(mCurrentID, soundid, l); + } else if (functionName == QLatin1String("PLAYEXTEND")) { + int volume = getNumTok(l); + int flags = getNumTok(l); + emit play(mCurrentID, soundid, l, volume, flags); + } else if (functionName == QLatin1String("PLAYRAW")) { + int chs = getNumTok(l); + int freq = getNumTok(l); + int bitspersample = getNumTok(l); + int flags = getNumTok(l); + emit playRaw(mCurrentID, soundid, l, freq, chs, bitspersample, flags); + } else if (functionName == QLatin1String("PAUSE")) { + emit pause(mCurrentID, soundid); + } else if (functionName == QLatin1String("STOP")) { + emit stop(mCurrentID, soundid); + } else if (functionName == QLatin1String("RESUME")) { + emit resume(mCurrentID, soundid); + } else if (functionName == QLatin1String("SETVOLUME")) { + int left = getNumTok(l); + int right = getNumTok(l); + emit setVolume(mCurrentID, soundid, left, right); + } else if (functionName == QLatin1String("MUTE")) { + emit setMute(mCurrentID, soundid, true); + } else if (functionName == QLatin1String("UNMUTE")) { + emit setMute(mCurrentID, soundid, false); + } else if (functionName == QLatin1String("PRIORITYONLY")) { + bool sPri = soundid != 0; + if (sPri != priExist) { + priExist = sPri; + emit playPriorityOnly(sPri); + } + } else if(functionName == QLatin1String("SILENT")) { + emit setSilent( soundid != 0 ); + } + } +} + +void QWSSoundServerClient::sendClientMessage(QString msg) +{ +#ifndef QT_NO_TEXTCODEC + QByteArray u = msg.toUtf8(); +#else + QByteArray u = msg.toLatin1(); +#endif + socket->write(u.data(), u.length()); + socket->flush(); +} + +void QWSSoundServerClient::sendSoundCompleted(int gid, int sid) +{ + if (gid == mCurrentID) + sendClientMessage(QLatin1String("SOUNDCOMPLETED ") + + QString::number(sid) + QLatin1Char('\n')); +} + +void QWSSoundServerClient::sendDeviceReady(int gid, int sid) +{ + if (gid == mCurrentID) + sendClientMessage(QLatin1String("DEVICEREADY ") + + QString::number(sid) + QLatin1Char('\n')); +} + +void QWSSoundServerClient::sendDeviceError(int gid, int sid, int err) +{ + if (gid == mCurrentID) + sendClientMessage(QLatin1String("DEVICEERROR ") + + QString::number(sid) + QLatin1Char(' ') + + QString::number(err) + QLatin1Char('\n')); +} +#endif + +static const int maxVolume = 100; +static const int runinLength = 2*sound_buffer_size; +class QWSSoundServerProvider { +public: + QWSSoundServerProvider(int w, int s) + : mWid(w), mSid(s), mMuted(false) + { + leftVolume = maxVolume>>1; + rightVolume = maxVolume>>1; + isPriority = false; + samples_due = 0; + max1 = max2 = out = 0;//= sound_buffer_size; + data = data1; + max = &max1; + sampleRunin = 0; + dev = -1; + } + + virtual ~QWSSoundServerProvider() { + } + + int groupId() const { return mWid; } + int soundId() const { return mSid; } + + void startSampleRunin() { + // inteded to provide even audio return from mute/pause/dead samples. + //sampleRunin = runinLength; // or more? + } + + + void setVolume(int lv, int rv) { + leftVolume = qMin(maxVolume, qMax(0, lv)); + rightVolume = qMin(maxVolume, qMax(0, rv)); + } + + void setMute(bool m) { mMuted = m; } + bool muted() { return mMuted; } + + void setPriority(bool p) { + if (p != isPriority) { + isPriority = p; // currently meaningless. + } + } + + + static void setPlayPriorityOnly(bool p) + { + if (p) + priorityExists++; + else + priorityExists--; + + if (priorityExists < 0) + qDebug("QSS: got more priority offs than ons"); + } + + // return -1 for file broken, give up. + // else return sampels ready for playing. + // argument is max samples server is looking for, + // in terms of current device status. + virtual int readySamples(int) = 0; + + int getSample(int off, int bps) { + return (bps == 1) ? (data[out+off] - 128) * 128 : ((short*)data)[(out/2)+off]; + } + + int add(int* mixl, int* mixr, int count) + { + int bytesPerSample = chunkdata.wBitsPerSample >> 3; + + if ( mMuted ) { + sampleRunin -= qMin(sampleRunin,count); + while (count && (dev != -1)) { + if (out >= *max) { + // switch buffers + out = 0; + if (data == data1 && max2 != 0) { + data = data2; + max = &max2; + max1 = 0; + } else if (data == data2 && max1 != 0) { + data = data1; + max = &max1; + max2 = 0; + } else { + qDebug("QSS Read Error: both buffers empty"); + return 0; + } + } + samples_due += sound_speed; + while (count && samples_due >= chunkdata.samplesPerSec) { + samples_due -= chunkdata.samplesPerSec; + count--; + } + out += bytesPerSample * chunkdata.channels; + } + return count; + } + + // This shouldn't be the case + if ( !mixl || !mixr ) + return 0; + + int lVolNum = leftVolume, lVolDen = maxVolume; + int rVolNum = rightVolume, rVolDen = maxVolume; + if (priorityExists > 0 && !isPriority) { + lVolNum = 0; // later, make this gradually fade in and out. + lVolDen = 5; + rVolNum = 0; + rVolDen = 5; + } + + while (count && (dev != -1)) { + if (out >= *max) { + // switch buffers + out = 0; + if (data == data1 && max2 != 0) { + data = data2; + max = &max2; + max1 = 0; + } else if (data == data2 && max1 != 0) { + data = data1; + max = &max1; + max2 = 0; + } else { + qDebug("QSS Read Error: both buffers empty"); + return 0; + } + } + samples_due += sound_speed; + if (count && samples_due >= chunkdata.samplesPerSec) { + int l = getSample(0,bytesPerSample)*lVolNum/lVolDen; + int r = (chunkdata.channels == 2) ? getSample(1,bytesPerSample)*rVolNum/rVolDen : l; + if (!sound_stereo && chunkdata.channels == 2) + l += r; + if (sampleRunin) { + while (sampleRunin && count && samples_due >= chunkdata.samplesPerSec) { + mixl++; + if (sound_stereo) + mixr++; + samples_due -= chunkdata.samplesPerSec; + sampleRunin--; + count--; + } + } + while (count && samples_due >= chunkdata.samplesPerSec) { + *mixl++ += l; + if (sound_stereo) + *mixr++ += r; + samples_due -= chunkdata.samplesPerSec; + count--; + } + } + + // optimize out manipulation of sample if downsampling and we skip it + out += bytesPerSample * chunkdata.channels; + } + + return count; + } + + virtual bool finished() const = 0; + + bool equal(int wid, int sid) + { + return (wid == mWid && sid == mSid); + } + +protected: + + char * prepareBuffer( int &size) + { + // keep reading as long as there is 50 % or more room in off buffer. + if (data == data1 && (max2<<1 < sound_buffer_size)) { + size=sound_buffer_size - max2; + return (char *)data2; + } else if (data == data2 && (max1<<1 < sound_buffer_size)) { + size=sound_buffer_size - max1; + return (char *)data1; + } else { + size = 0; + return 0; + } + } + + void updateBuffer(int read) + { + // always reads to off buffer. + if (read >= 0) { + if (data == data2) { + max1 = read; + } else { + max2 = read; + } + } + } + + int devSamples() + { + int possible = (((max1+max2-out) / ((chunkdata.wBitsPerSample>>3)*chunkdata.channels)) + *sound_speed)/chunkdata.samplesPerSec; + + return possible; + } + + + struct { + qint16 formatTag; + qint16 channels; + qint32 samplesPerSec; + qint32 avgBytesPerSec; + qint16 blockAlign; + qint16 wBitsPerSample; + } chunkdata; + int dev; + int samples_due; +private: + int mWid; + int mSid; + int leftVolume; + int rightVolume; + bool isPriority; + static int priorityExists; + int *max; + uchar *data; + uchar data1[sound_buffer_size+4]; // +4 to handle badly aligned input data + uchar data2[sound_buffer_size+4]; // +4 to handle badly aligned input data + int out, max1, max2; + int sampleRunin; + bool mMuted; +}; + +int QWSSoundServerProvider::priorityExists = 0; + +class QWSSoundServerBucket : public QWSSoundServerProvider { +public: + QWSSoundServerBucket(int d, int wid, int sid) + : QWSSoundServerProvider(wid, sid) + { + dev = d; + wavedata_remaining = -1; + mFinishedRead = false; + mInsufficientSamples = false; + } + ~QWSSoundServerBucket() + { + //dev->close(); + ::close(dev); + } + bool finished() const + { + //return !max; + return mInsufficientSamples && mFinishedRead ; + } + int readySamples(int) + { + int size; + char *dest = prepareBuffer(size); + // may want to change this to something like + // if (data == data1 && max2<<1 < sound_buffer_size + // || + // data == data2 && max1<<1 < sound_buffer_size) + // so will keep filling off buffer while there is +50% space left + if (size > 0 && dest != 0) { + while ( wavedata_remaining < 0 ) { + //max = 0; + wavedata_remaining = -1; + // Keep reading chunks... + const int n = sizeof(chunk)-sizeof(chunk.data); + int nr = ::read(dev, (void*)&chunk,n); + if ( nr != n ) { + // XXX check error? or don't we care? + wavedata_remaining = 0; + mFinishedRead = true; + } else if ( qstrncmp(chunk.id,"data",4) == 0 ) { + wavedata_remaining = chunk.size; + + //out = max = sound_buffer_size; + + } else if ( qstrncmp(chunk.id,"RIFF",4) == 0 ) { + char d[4]; + if ( read(dev, d, 4) != 4 ) { + // XXX check error? or don't we care? + //qDebug("couldn't read riff"); + mInsufficientSamples = true; + mFinishedRead = true; + return 0; + } else if ( qstrncmp(d,"WAVE",4) != 0 ) { + // skip + if ( chunk.size > 1000000000 || lseek(dev,chunk.size-4, SEEK_CUR) == -1 ) { + //qDebug("oversized wav chunk"); + mFinishedRead = true; + } + } + } else if ( qstrncmp(chunk.id,"fmt ",4) == 0 ) { + if ( ::read(dev,(char*)&chunkdata,sizeof(chunkdata)) != sizeof(chunkdata) ) { + // XXX check error? or don't we care? + //qDebug("couldn't ready chunkdata"); + mFinishedRead = true; + } +#define WAVE_FORMAT_PCM 1 + else if ( chunkdata.formatTag != WAVE_FORMAT_PCM ) { + //qDebug("WAV file: UNSUPPORTED FORMAT %d",chunkdata.formatTag); + mFinishedRead = true; + } + } else { + // ignored chunk + if ( chunk.size > 1000000000 || lseek(dev, chunk.size, SEEK_CUR) == -1) { + //qDebug("chunk size too big"); + mFinishedRead = true; + } + } + } + // this looks wrong. + if (wavedata_remaining <= 0) { + mFinishedRead = true; + } + + } + // may want to change this to something like + // if (data == data1 && max2<<1 < sound_buffer_size + // || + // data == data2 && max1<<1 < sound_buffer_size) + // so will keep filling off buffer while there is +50% space left + + if (wavedata_remaining) { + if (size > 0 && dest != 0) { + int read = ::read(dev, dest, qMin(size, wavedata_remaining)); + // XXX check error? or don't we care? + wavedata_remaining -= read; + updateBuffer(read); + if (read <= 0) // data unexpectidly ended + mFinishedRead = true; + } + } + int possible = devSamples(); + if (possible == 0) + mInsufficientSamples = true; + return possible; + } + +protected: + QRiffChunk chunk; + int wavedata_remaining; + bool mFinishedRead; + bool mInsufficientSamples; +}; + +class QWSSoundServerStream : public QWSSoundServerProvider { +public: + QWSSoundServerStream(int d,int c, int f, int b, + int wid, int sid) + : QWSSoundServerProvider(wid, sid) + { + chunkdata.channels = c; + chunkdata.samplesPerSec = f; + chunkdata.wBitsPerSample = b; + dev = d; + //fcntl( dev, F_SETFL, O_NONBLOCK ); + lasttime = 0; + } + + ~QWSSoundServerStream() + { + if (dev != -1) { + ::close(dev); + dev = -1; + } + } + + bool finished() const + { + return (dev == -1); + } + + + int readySamples(int) + { + int size; + char *dest = prepareBuffer(size); + if (size > 0 && dest != 0 && dev != -1) { + + int read = ::read(dev, dest, size); + if (read < 0) { + switch(errno) { + case EAGAIN: + case EINTR: + // means read may yet succeed on the next attempt + break; + default: + // unexpected error, fail. + ::close(dev); + dev = -1; + } + } else if (read == 0) { + // 0 means writer has closed dev and/or + // file is at end. + ::close(dev); + dev = -1; + } else { + updateBuffer(read); + } + } + int possible = devSamples(); + if (possible == 0) + startSampleRunin(); + return possible; + } + +protected: + time_t lasttime; +}; + +#ifndef QT_NO_QWS_SOUNDSERVER +QWSSoundServerSocket::QWSSoundServerSocket(QObject *parent) : + QWSServerSocket(QString::fromLatin1(SOUND_PIPE).arg(qws_display_id), parent) +{ + connect(this, SIGNAL(newConnection()), this, SLOT(newConnection())); +} + + +#ifdef QT3_SUPPORT +QWSSoundServerSocket::QWSSoundServerSocket(QObject *parent, const char *name) : + QWSServerSocket(QString::fromLatin1(SOUND_PIPE).arg(qws_display_id), parent) +{ + if (name) + setObjectName(QString::fromAscii(name)); + connect(this, SIGNAL(newConnection()), this, SLOT(newConnection())); +} +#endif + +void QWSSoundServerSocket::newConnection() +{ + while (QWS_SOCK_BASE *sock = nextPendingConnection()) { + QWSSoundServerClient* client = new QWSSoundServerClient(sock,this); + + connect(client, SIGNAL(play(int,int,QString)), + this, SIGNAL(playFile(int,int,QString))); + connect(client, SIGNAL(play(int,int,QString,int,int)), + this, SIGNAL(playFile(int,int,QString,int,int))); + connect(client, SIGNAL(playRaw(int,int,QString,int,int,int,int)), + this, SIGNAL(playRawFile(int,int,QString,int,int,int,int))); + + connect(client, SIGNAL(pause(int,int)), + this, SIGNAL(pauseFile(int,int))); + connect(client, SIGNAL(stop(int,int)), + this, SIGNAL(stopFile(int,int))); + connect(client, SIGNAL(playPriorityOnly(bool)), + this, SIGNAL(playPriorityOnly(bool))); + connect(client, SIGNAL(stopAll(int)), + this, SIGNAL(stopAll(int))); + connect(client, SIGNAL(resume(int,int)), + this, SIGNAL(resumeFile(int,int))); + + connect(client, SIGNAL(setSilent(bool)), + this, SIGNAL(setSilent(bool))); + + connect(client, SIGNAL(setMute(int,int,bool)), + this, SIGNAL(setMute(int,int,bool))); + connect(client, SIGNAL(setVolume(int,int,int,int)), + this, SIGNAL(setVolume(int,int,int,int))); + + connect(this, SIGNAL(soundFileCompleted(int,int)), + client, SLOT(sendSoundCompleted(int,int))); + connect(this, SIGNAL(deviceReady(int,int)), + client, SLOT(sendDeviceReady(int,int))); + connect(this, SIGNAL(deviceError(int,int,int)), + client, SLOT(sendDeviceError(int,int,int))); + } +} + +#endif + +class QWSSoundServerPrivate : public QObject { + Q_OBJECT + +public: + QWSSoundServerPrivate(QObject* parent=0, const char* name=0) : + QObject(parent) + { + timerId = 0; + if (name) + setObjectName(QString::fromAscii(name)); +#ifndef QT_NO_QWS_SOUNDSERVER + server = new QWSSoundServerSocket(this); + + connect(server, SIGNAL(playFile(int,int,QString)), + this, SLOT(playFile(int,int,QString))); + connect(server, SIGNAL(playFile(int,int,QString,int,int)), + this, SLOT(playFile(int,int,QString,int,int))); + connect(server, SIGNAL(playRawFile(int,int,QString,int,int,int,int)), + this, SLOT(playRawFile(int,int,QString,int,int,int,int))); + + connect(server, SIGNAL(pauseFile(int,int)), + this, SLOT(pauseFile(int,int))); + connect(server, SIGNAL(stopFile(int,int)), + this, SLOT(stopFile(int,int))); + connect(server, SIGNAL(stopAll(int)), + this, SLOT(stopAll(int))); + connect(server, SIGNAL(playPriorityOnly(bool)), + this, SLOT(playPriorityOnly(bool))); + connect(server, SIGNAL(resumeFile(int,int)), + this, SLOT(resumeFile(int,int))); + + connect( server, SIGNAL(setSilent(bool)), + this, SLOT(setSilent(bool))); + + connect(server, SIGNAL(setMute(int,int,bool)), + this, SLOT(setMute(int,int,bool))); + connect(server, SIGNAL(setVolume(int,int,int,int)), + this, SLOT(setVolume(int,int,int,int))); + + connect(this, SIGNAL(soundFileCompleted(int,int)), + server, SIGNAL(soundFileCompleted(int,int))); + connect(this, SIGNAL(deviceReady(int,int)), + server, SIGNAL(deviceReady(int,int))); + connect(this, SIGNAL(deviceError(int,int,int)), + server, SIGNAL(deviceError(int,int,int))); + +#endif + silent = false; + fd = -1; + unwritten = 0; + can_GETOSPACE = true; + } + + ~QWSSoundServerPrivate() + { + qDeleteAll(active); + qDeleteAll(inactive); + } + +signals: + void soundFileCompleted(int, int); + void deviceReady(int, int); + void deviceError(int, int, int); + +public slots: + void playRawFile(int wid, int sid, const QString &filename, int freq, int channels, int bitspersample, int flags); + void playFile(int wid, int sid, const QString& filename); + void playFile(int wid, int sid, const QString& filename, int v, int flags); + void checkPresetVolumes(int wid, int sid, QWSSoundServerProvider *p); + void pauseFile(int wid, int sid); + void resumeFile(int wid, int sid); + void stopFile(int wid, int sid); + void stopAll(int wid); + void setVolume(int wid, int sid, int lv, int rv); + void setMute(int wid, int sid, bool m); + void playPriorityOnly(bool p); + void sendCompletedSignals(); + void feedDevice(int fd); + void setSilent( bool enabled ); + +protected: + void timerEvent(QTimerEvent* event); + +private: + int openFile(int wid, int sid, const QString& filename); + bool openDevice(); + void closeDevice() + { + if (fd >= 0) { + ::close(fd); + fd = -1; + } + } + + QList<QWSSoundServerProvider*> active; + QList<QWSSoundServerProvider*> inactive; + struct PresetVolume { + int wid; + int sid; + int left; + int right; + bool mute; + }; + QList<PresetVolume> volumes; + struct CompletedInfo { + CompletedInfo( ) : groupId( 0 ), soundId( 0 ) { } + CompletedInfo( int _groupId, int _soundId ) : groupId( _groupId ), soundId( _soundId ) { } + int groupId; + int soundId; + }; + QList<CompletedInfo> completed; + + bool silent; + + int fd; + int unwritten; + int timerId; + char* cursor; + short data[sound_buffer_size*2]; + bool can_GETOSPACE; +#ifndef QT_NO_QWS_SOUNDSERVER + QWSSoundServerSocket *server; +#endif +}; + +void QWSSoundServerPrivate::setSilent( bool enabled ) +{ + // Close output device + closeDevice(); + if( !unwritten && !active.count() ) { + sendCompletedSignals(); + } + // Stop processing audio + killTimer( timerId ); + silent = enabled; + // If audio remaining, open output device and continue processing + if( unwritten || active.count() ) { + openDevice(); + } +} + +void QWSSoundServerPrivate::timerEvent(QTimerEvent* event) +{ + // qDebug("QSS timer event"); + if( event->timerId() == timerId ) { + if (fd >= 0) + feedDevice(fd); + if (fd < 0) { + killTimer(timerId); + timerId = 0; + } + } +} + +void QWSSoundServerPrivate::playRawFile(int wid, int sid, const QString &filename, + int freq, int channels, int bitspersample, int flags) +{ +#ifdef QT_NO_QWS_SOUNDSERVER + Q_UNUSED(flags); +#endif + int f = openFile(wid, sid, filename); + if ( f ) { + QWSSoundServerStream *b = new QWSSoundServerStream(f, channels, freq, bitspersample, wid, sid); + // check preset volumes. + checkPresetVolumes(wid, sid, b); +#ifndef QT_NO_QWS_SOUNDSERVER + b->setPriority((flags & QWSSoundClient::Priority) == QWSSoundClient::Priority); +#endif + active.append(b); + emit deviceReady(wid, sid); + } +} + +void QWSSoundServerPrivate::playFile(int wid, int sid, const QString& filename) +{ + int f = openFile(wid, sid, filename); + if ( f ) { + QWSSoundServerProvider *b = new QWSSoundServerBucket(f, wid, sid); + checkPresetVolumes(wid, sid, b); + active.append( b ); + emit deviceReady(wid, sid); + } +} + +void QWSSoundServerPrivate::playFile(int wid, int sid, const QString& filename, + int v, int flags) +{ +#ifdef QT_NO_QWS_SOUNDSERVER + Q_UNUSED(flags); +#endif + int f = openFile(wid, sid, filename); + if ( f ) { + QWSSoundServerProvider *b = new QWSSoundServerBucket(f, wid, sid); + checkPresetVolumes(wid, sid, b); + b->setVolume(v, v); +#ifndef QT_NO_QWS_SOUNDSERVER + b->setPriority((flags & QWSSoundClient::Priority) == QWSSoundClient::Priority); +#endif + active.append(b); + emit deviceReady(wid, sid); + } +} + +void QWSSoundServerPrivate::checkPresetVolumes(int wid, int sid, QWSSoundServerProvider *p) +{ + QList<PresetVolume>::Iterator it = volumes.begin(); + while (it != volumes.end()) { + PresetVolume v = *it; + if (v.wid == wid && v.sid == sid) { + p->setVolume(v.left, v.right); + p->setMute(v.mute); + it = volumes.erase(it); + return; + } else { + ++it; + } + } +} + +void QWSSoundServerPrivate::pauseFile(int wid, int sid) +{ + QWSSoundServerProvider *bucket; + for (int i = 0; i < active.size(); ++i ) { + bucket = active.at(i); + if (bucket->equal(wid, sid)) { + // found bucket.... + active.removeAt(i); + inactive.append(bucket); + return; + } + } +} + +void QWSSoundServerPrivate::resumeFile(int wid, int sid) +{ + QWSSoundServerProvider *bucket; + for (int i = 0; i < inactive.size(); ++i ) { + bucket = inactive.at(i); + if (bucket->equal(wid, sid)) { + // found bucket.... + inactive.removeAt(i); + active.append(bucket); + return; + } + } +} + +void QWSSoundServerPrivate::stopFile(int wid, int sid) +{ + QWSSoundServerProvider *bucket; + for (int i = 0; i < active.size(); ++i ) { + bucket = active.at(i); + if (bucket->equal(wid, sid)) { + active.removeAt(i); + delete bucket; + return; + } + } + for (int i = 0; i < inactive.size(); ++i ) { + bucket = inactive.at(i); + if (bucket->equal(wid, sid)) { + inactive.removeAt(i); + delete bucket; + return; + } + } +} + +void QWSSoundServerPrivate::stopAll(int wid) +{ + QWSSoundServerProvider *bucket; + QList<QWSSoundServerProvider*>::Iterator it = active.begin(); + while (it != active.end()) { + bucket = *it; + if (bucket->groupId() == wid) { + it = active.erase(it); + delete bucket; + } else { + ++it; + } + } + it = inactive.begin(); + while (it != inactive.end()) { + bucket = *it; + if (bucket->groupId() == wid) { + it = inactive.erase(it); + delete bucket; + } else { + ++it; + } + } +} + +void QWSSoundServerPrivate::setVolume(int wid, int sid, int lv, int rv) +{ + QWSSoundServerProvider *bucket; + for( int i = 0; i < active.size(); ++i ) { + bucket = active.at(i); + if (bucket->equal(wid, sid)) { + bucket->setVolume(lv,rv); + return; + } + } + // If gotten here, then it means wid/sid wasn't set up yet. + // first find and remove current preset volumes, then add this one. + QList<PresetVolume>::Iterator it = volumes.begin(); + while (it != volumes.end()) { + PresetVolume v = *it; + if (v.wid == wid && v.sid == sid) + it = volumes.erase(it); + else + ++it; + } + // and then add this volume + PresetVolume nv; + nv.wid = wid; + nv.sid = sid; + nv.left = lv; + nv.right = rv; + nv.mute = false; + volumes.append(nv); +} + +void QWSSoundServerPrivate::setMute(int wid, int sid, bool m) +{ + QWSSoundServerProvider *bucket; + for( int i = 0; i < active.size(); ++i ) { + bucket = active.at(i); + if (bucket->equal(wid, sid)) { + bucket->setMute(m); + return; + } + } + // if gotten here then setting is being applied before item + // is created. + QList<PresetVolume>::Iterator it = volumes.begin(); + while (it != volumes.end()) { + PresetVolume v = *it; + if (v.wid == wid && v.sid == sid) { + (*it).mute = m; + return; + } + } + if (m) { + PresetVolume nv; + nv.wid = wid; + nv.sid = sid; + nv.left = maxVolume>>1; + nv.right = maxVolume>>1; + nv.mute = true; + volumes.append(nv); + } +} + +void QWSSoundServerPrivate::playPriorityOnly(bool p) +{ + QWSSoundServerProvider::setPlayPriorityOnly(p); +} + +void QWSSoundServerPrivate::sendCompletedSignals() +{ + while( !completed.isEmpty() ) { + emit soundFileCompleted( (*completed.begin()).groupId, + (*completed.begin()).soundId ); + completed.erase( completed.begin() ); + } +} + + +int QWSSoundServerPrivate::openFile(int wid, int sid, const QString& filename) +{ + stopFile(wid, sid); // close and re-open. + int f = ::open(QFile::encodeName(filename), O_RDONLY|O_NONBLOCK); + if (f == -1) { + // XXX check ferror, check reason. + qDebug("Failed opening \"%s\"",filename.toLatin1().data()); +#ifndef QT_NO_QWS_SOUNDSERVER + emit deviceError(wid, sid, (int)QWSSoundClient::ErrOpeningFile ); +#endif + } else if ( openDevice() ) { + return f; + } +#ifndef QT_NO_QWS_SOUNDSERVER + emit deviceError(wid, sid, (int)QWSSoundClient::ErrOpeningAudioDevice ); +#endif + return 0; +} + +bool QWSSoundServerPrivate::openDevice() +{ + if (fd < 0) { + if( silent ) { + fd = ::open( "/dev/null", O_WRONLY ); + // Emulate write to audio device + int delay = 1000*(sound_buffer_size>>(sound_stereo+sound_16bit))/sound_speed/2; + timerId = startTimer(delay); + + return true; + } + // + // Don't block open right away. + // + bool openOkay = false; + if ((fd = ::open("/dev/dsp", O_WRONLY|O_NONBLOCK)) != -1) { + int flags = fcntl(fd, F_GETFL); + flags &= ~O_NONBLOCK; + openOkay = (fcntl(fd, F_SETFL, flags) == 0); + } + if (!openOkay) { + qDebug("Failed opening audio device"); + return false; + } + + // Setup soundcard at 16 bit mono + int v; + //v=0x00010000+sound_fragment_size; + // um the media player did this instead. + v=0x10000 * 4 + sound_fragment_size; + if (ioctl(fd, SNDCTL_DSP_SETFRAGMENT, &v)) + qWarning("Could not set fragments to %08x",v); +#ifdef QT_QWS_SOUND_16BIT + v=AFMT_S16_LE; if (ioctl(fd, SNDCTL_DSP_SETFMT, &v)) + qWarning("Could not set format %d",v); + if (AFMT_S16_LE != v) + qDebug("Want format %d got %d", AFMT_S16_LE, v); +#else + v=AFMT_U8; if (ioctl(fd, SNDCTL_DSP_SETFMT, &v)) + qWarning("Could not set format %d",v); + if (AFMT_U8 != v) + qDebug("Want format %d got %d", AFMT_U8, v); +#endif + v=sound_stereo; if (ioctl(fd, SNDCTL_DSP_STEREO, &v)) + qWarning("Could not set stereo %d",v); + if (sound_stereo != v) + qDebug("Want stereo %d got %d", sound_stereo, v); +#ifdef QT_QWS_SOUND_STEREO + sound_stereo=v; +#endif + v=sound_speed; if (ioctl(fd, SNDCTL_DSP_SPEED, &sound_speed)) + qWarning("Could not set speed %d",v); + if (v != sound_speed) + qDebug("Want speed %d got %d", v, sound_speed); + + int delay = 1000*(sound_buffer_size>>(sound_stereo+sound_16bit)) + /sound_speed/2; + // qDebug("QSS delay: %d", delay); + timerId = startTimer(delay); + + // + // Check system volume + // + int mixerHandle = ::open( "/dev/mixer", O_RDWR|O_NONBLOCK ); + if ( mixerHandle >= 0 ) { + int volume; + ioctl( mixerHandle, MIXER_READ(0), &volume ); + close( mixerHandle ); + if ( volume < 1<<(sound_stereo+sound_16bit) ) + qDebug("Want sound at %d got %d", + 1<<(sound_stereo+sound_16bit), volume); + } else + qDebug( "get volume of audio device failed" ); + + } + return true; +} + +void QWSSoundServerPrivate::feedDevice(int fd) +{ + if ( !unwritten && active.size() == 0 ) { + closeDevice(); + sendCompletedSignals(); + return; + } else { + sendCompletedSignals(); + } + + QWSSoundServerProvider* bucket; + + // find out how much audio is possible + int available = sound_buffer_size; + QList<QWSSoundServerProvider*> running; + for (int i = 0; i < active.size(); ++i) { + bucket = active.at(i); + int ready = bucket->readySamples(available); + if (ready > 0) { + available = qMin(available, ready); + running.append(bucket); + } + } + + audio_buf_info info; + if (can_GETOSPACE && ioctl(fd,SNDCTL_DSP_GETOSPACE,&info)) { + can_GETOSPACE = false; + fcntl(fd, F_SETFL, O_NONBLOCK); + } + if (!can_GETOSPACE) + info.fragments = 4; // #### configurable? + if (info.fragments > 0) { + if (!unwritten) { + int left[sound_buffer_size]; + memset(left,0,available*sizeof(int)); + int right[sound_buffer_size]; + if ( sound_stereo ) + memset(right,0,available*sizeof(int)); + + if (running.size() > 0) { + // should do volume mod here in regards to each bucket to avoid flattened/bad peaks. + for (int i = 0; i < running.size(); ++i ) { + bucket = running.at(i); + int unused = bucket->add(left,right,available); + if (unused > 0) { + // this error is quite serious, as + // it will really screw up mixing. + qDebug("provider lied about samples ready"); + } + } + if ( sound_16bit ) { + short *d = (short*)data; + for (int i=0; i<available; i++) { + *d++ = (short)qMax(qMin(left[i],32767),-32768); + if ( sound_stereo ) + *d++ = (short)qMax(qMin(right[i],32767),-32768); + } + } else { + signed char *d = (signed char *)data; + for (int i=0; i<available; i++) { + *d++ = (signed char)qMax(qMin(left[i]/256,127),-128)+128; + if ( sound_stereo ) + *d++ = (signed char)qMax(qMin(right[i]/256,127),-128)+128; + } + } + unwritten = available*(sound_16bit+1)*(sound_stereo+1); + cursor = (char*)data; + } + } + // sound open, but nothing written. Should clear the buffer. + + int w; + if (unwritten) { + w = ::write(fd,cursor,unwritten); + + if (w < 0) { + if (can_GETOSPACE) + return; + w = 0; + } + + cursor += w; + unwritten -= w; + } else { + // write some zeros to clear the buffer? + if (!zeroMem) + zeroMem = (char *)calloc(sound_buffer_size, sizeof(char)); + w = ::write(fd, zeroMem, sound_buffer_size); + if (w < 0) + w = 0; + } + } + + QList<QWSSoundServerProvider*>::Iterator it = active.begin(); + while (it != active.end()) { + bucket = *it; + if (bucket->finished()) { + completed.append(CompletedInfo(bucket->groupId(), bucket->soundId())); + it = active.erase(it); + delete bucket; + } else { + ++it; + } + } +} + + +QWSSoundServer::QWSSoundServer(QObject* parent) : + QObject(parent) +{ + d = new QWSSoundServerPrivate(this); + + connect( d, SIGNAL(soundFileCompleted(int,int)), + this, SLOT(translateSoundCompleted(int,int)) ); +} + +void QWSSoundServer::playFile( int sid, const QString& filename ) +{ + //wid == 0, as it is the server initiating rather than a client + // if wid was passable, would accidently collide with server + // sockect's wids. + d->playFile(0, sid, filename); +} + +void QWSSoundServer::pauseFile( int sid ) +{ + d->pauseFile(0, sid); +} + +void QWSSoundServer::stopFile( int sid ) +{ + d->stopFile(0, sid); +} + +void QWSSoundServer::resumeFile( int sid ) +{ + d->resumeFile(0, sid); +} + +QWSSoundServer::~QWSSoundServer() +{ + d->stopAll(0); +} + +void QWSSoundServer::translateSoundCompleted( int, int sid ) +{ + emit soundCompleted( sid ); +} + +#ifndef QT_NO_QWS_SOUNDSERVER +QWSSoundClient::QWSSoundClient(QObject* parent) : + QWSSocket(parent) +{ + connectToLocalFile(QString::fromLatin1(SOUND_PIPE).arg(qws_display_id)); + QObject::connect(this,SIGNAL(readyRead()), + this,SLOT(tryReadCommand())); + if( state() == QWS_SOCK_BASE::ConnectedState ) QTimer::singleShot(1, this, SIGNAL(connected())); + else QTimer::singleShot(1, this, SLOT(emitConnectionRefused())); +} + +QWSSoundClient::~QWSSoundClient( ) +{ + flush(); +} + +void QWSSoundClient::reconnect() +{ + connectToLocalFile(QString::fromLatin1(SOUND_PIPE).arg(qws_display_id)); + if( state() == QWS_SOCK_BASE::ConnectedState ) emit connected(); + else emit error( QTcpSocket::ConnectionRefusedError ); +} + +void QWSSoundClient::sendServerMessage(QString msg) +{ +#ifndef QT_NO_TEXTCODEC + QByteArray u = msg.toUtf8(); +#else + QByteArray u = msg.toLatin1(); +#endif + write(u.data(), u.length()); + flush(); +} + +void QWSSoundClient::play( int id, const QString& filename ) +{ + QFileInfo fi(filename); + sendServerMessage(QLatin1String("PLAY ") + + QString::number(id) + QLatin1Char(' ') + + fi.absoluteFilePath() + QLatin1Char('\n')); +} + +void QWSSoundClient::play( int id, const QString& filename, int volume, int flags) +{ + QFileInfo fi(filename); + sendServerMessage(QLatin1String("PLAYEXTEND ") + + QString::number(id) + QLatin1Char(' ') + + QString::number(volume) + QLatin1Char(' ') + + QString::number(flags) + QLatin1Char(' ') + + fi.absoluteFilePath() + QLatin1Char('\n')); +} + +void QWSSoundClient::pause( int id ) +{ + sendServerMessage(QLatin1String("PAUSE ") + + QString::number(id) + QLatin1Char('\n')); +} + +void QWSSoundClient::stop( int id ) +{ + sendServerMessage(QLatin1String("STOP ") + + QString::number(id) + QLatin1Char('\n')); +} + +void QWSSoundClient::resume( int id ) +{ + sendServerMessage(QLatin1String("RESUME ") + + QString::number(id) + QLatin1Char('\n')); +} + +void QWSSoundClient::playRaw( int id, const QString& filename, + int freq, int chs, int bitspersample, int flags) +{ + QFileInfo fi(filename); + sendServerMessage(QLatin1String("PLAYRAW ") + + QString::number(id) + QLatin1Char(' ') + + QString::number(chs) + QLatin1Char(' ') + + QString::number(freq) + QLatin1Char(' ') + + QString::number(bitspersample) + QLatin1Char(' ') + + QString::number(flags) + QLatin1Char(' ') + + fi.absoluteFilePath() + QLatin1Char('\n')); +} + +void QWSSoundClient::setMute( int id, bool m ) +{ + sendServerMessage(QLatin1String(m ? "MUTE " : "UNMUTE ") + + QString::number(id) + QLatin1Char('\n')); +} + +void QWSSoundClient::setVolume( int id, int leftVol, int rightVol ) +{ + sendServerMessage(QLatin1String("SETVOLUME ") + + QString::number(id) + QLatin1Char(' ') + + QString::number(leftVol) + QLatin1Char(' ') + + QString::number(rightVol) + QLatin1Char('\n')); +} + +void QWSSoundClient::playPriorityOnly( bool pri ) +{ + sendServerMessage(QLatin1String("PRIORITYONLY ") + + QString::number(pri ? 1 : 0) + QLatin1Char('\n')); +} + +void QWSSoundClient::setSilent( bool enable ) +{ + sendServerMessage(QLatin1String("SILENT ") + + QString::number( enable ? 1 : 0 ) + QLatin1Char('\n')); +} + +void QWSSoundClient::tryReadCommand() +{ + while ( canReadLine() ) { + QString l = QString::fromAscii(readLine()); + l.truncate(l.length()-1); // chomp + QStringList token = l.split(QLatin1Char(' ')); + if (token[0] == QLatin1String("SOUNDCOMPLETED")) { + emit soundCompleted(token[1].toInt()); + } else if (token[0] == QLatin1String("DEVICEREADY")) { + emit deviceReady(token[1].toInt()); + } else if (token[0] == QLatin1String("DEVICEERROR")) { + emit deviceError(token[1].toInt(),(DeviceErrors)token[2].toInt()); + } + } +} + +void QWSSoundClient::emitConnectionRefused() +{ + emit error( QTcpSocket::ConnectionRefusedError ); +} +#endif + +QT_END_NAMESPACE + +#include "qsoundqss_qws.moc" + +#endif // QT_NO_SOUND diff --git a/src/gui/embedded/qsoundqss_qws.h b/src/gui/embedded/qsoundqss_qws.h new file mode 100644 index 0000000..072a694 --- /dev/null +++ b/src/gui/embedded/qsoundqss_qws.h @@ -0,0 +1,177 @@ +/**************************************************************************** +** +** 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$ +** +****************************************************************************/ + +#ifndef QSOUNDQSS_QWS_H +#define QSOUNDQSS_QWS_H + +#include <QtCore/qglobal.h> + +#ifndef QT_NO_SOUND + +#include <QtNetwork/qtcpserver.h> +#include <QtNetwork/qtcpsocket.h> +#include <QtGui/qwssocket_qws.h> + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +QT_MODULE(Gui) + +#if defined(QT_NO_NETWORK) || defined(QT_NO_DNS) +#define QT_NO_QWS_SOUNDSERVER +#endif + +#ifndef Q_OS_MAC + +class QWSSoundServerPrivate; + +class Q_GUI_EXPORT QWSSoundServer : public QObject { + Q_OBJECT +public: + explicit QWSSoundServer(QObject *parent=0); + ~QWSSoundServer(); + void playFile( int id, const QString& filename ); + void stopFile( int id ); + void pauseFile( int id ); + void resumeFile( int id ); + +Q_SIGNALS: + void soundCompleted( int ); + +private Q_SLOTS: + void translateSoundCompleted( int, int ); + +private: + QWSSoundServerPrivate* d; +}; + +#ifndef QT_NO_QWS_SOUNDSERVER +class Q_GUI_EXPORT QWSSoundClient : public QWSSocket { + Q_OBJECT +public: + + enum SoundFlags { + Priority = 0x01, + Streaming = 0x02 // currently ignored, but but could set up so both Raw and non raw can be done streaming or not. + }; + enum DeviceErrors { + ErrOpeningAudioDevice = 0x01, + ErrOpeningFile = 0x02, + ErrReadingFile = 0x04 + }; + explicit QWSSoundClient(QObject* parent=0); + ~QWSSoundClient( ); + void reconnect(); + void play( int id, const QString& filename ); + void play( int id, const QString& filename, int volume, int flags = 0 ); + void playRaw( int id, const QString&, int, int, int, int flags = 0 ); + + void pause( int id ); + void stop( int id ); + void resume( int id ); + void setVolume( int id, int left, int right ); + void setMute( int id, bool m ); + + // to be used by server only, to protect phone conversation/rings. + void playPriorityOnly(bool); + + // If silent, tell sound server to release audio device + // Otherwise, allow sound server to regain audio device + void setSilent(bool); + +Q_SIGNALS: + void soundCompleted(int); + void deviceReady(int id); + void deviceError(int id, QWSSoundClient::DeviceErrors); + +private Q_SLOTS: + void tryReadCommand(); + void emitConnectionRefused(); + +private: + void sendServerMessage(QString msg); +}; + +class QWSSoundServerSocket : public QWSServerSocket { + Q_OBJECT + +public: + explicit QWSSoundServerSocket(QObject *parent=0); +public Q_SLOTS: + void newConnection(); + +#ifdef QT3_SUPPORT +public: + QT3_SUPPORT_CONSTRUCTOR QWSSoundServerSocket(QObject *parent, const char *name); +#endif + +Q_SIGNALS: + void playFile(int, int, const QString&); + void playFile(int, int, const QString&, int, int); + void playRawFile(int, int, const QString&, int, int, int, int); + void pauseFile(int, int); + void stopFile(int, int); + void resumeFile(int, int); + void setVolume(int, int, int, int); + void setMute(int, int, bool); + + void stopAll(int); + + void playPriorityOnly(bool); + + void setSilent(bool); + + void soundFileCompleted(int, int); + void deviceReady(int, int); + void deviceError(int, int, int); +}; +#endif + +#endif // Q_OS_MAC + +QT_END_NAMESPACE + +QT_END_HEADER + +#endif // QT_NO_SOUND + +#endif // QSOUNDQSS_QWS_H diff --git a/src/gui/embedded/qtransportauth_qws.cpp b/src/gui/embedded/qtransportauth_qws.cpp new file mode 100644 index 0000000..97ba5b8 --- /dev/null +++ b/src/gui/embedded/qtransportauth_qws.cpp @@ -0,0 +1,1562 @@ +/**************************************************************************** +** +** 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 "qtransportauth_qws.h" +#include "qtransportauth_qws_p.h" + +#ifndef QT_NO_SXE + +#include "../../3rdparty/md5/md5.h" +#include "../../3rdparty/md5/md5.cpp" +#include "qwsutils_qws.h" +#include "qwssocket_qws.h" +#include "qwscommand_qws_p.h" +#include "qwindowsystem_qws.h" +#include "qbuffer.h" +#include "qthread.h" +#include "qabstractsocket.h" +#include "qlibraryinfo.h" +#include "qfile.h" +#include "qdebug.h" + +#include <syslog.h> +#include <unistd.h> +#include <fcntl.h> +#include <sys/stat.h> +#include <sys/types.h> +#include <sys/socket.h> +#include <sys/file.h> +#include <stdio.h> +#include <stdlib.h> +#include <errno.h> +#include <time.h> + +#include <QtCore/qcache.h> + +#define BUF_SIZE 512 + +QT_BEGIN_NAMESPACE + +/*! + \internal + memset for security purposes, guaranteed not to be optimized away + http://www.faqs.org/docs/Linux-HOWTO/Secure-Programs-HOWTO.html +*/ +Q_GUI_EXPORT void *guaranteed_memset(void *v,int c,size_t n) +{ + volatile char *p = (char *)v; while (n--) *p++=c; return v; +} + +/*! + \class QTransportAuth + \internal + + \brief Authenticate a message transport. + + For performance reasons, message authentication is tied to an individual + message transport instance. For example in connection oriented transports + the authentication cookie can be cached against the connection avoiding + the overhead of authentication on every message. + + For each process there is one instance of the QTransportAuth object. + For server processes it can determine the \link secure-exe-environ.html SXE + Program Identity \endlink and provide access to policy data to determine if + the message should be forwarded for action. If not actioned, the message + may be treated as being from a flawed or malicious process. + + Retrieve the instance with the getInstance() method. The constructor is + disabled and instances of QTransportAuth should never be constructed by + calling classes. + + To make the Authentication easier to use a proxied QIODevice is provided + which uses an internal QBuffer. + + In the server code first get a pointer to a QTransportAuth::Data object + using the connectTransport() method: + + \snippet doc/src/snippets/code/src_gui_embedded_qtransportauth_qws.cpp 0 + + Here it is asserted that the transport is trusted. See the assumptions + listed in the \link secure-exe-environ.html SXE documentation \endlink + + Then proxy in the authentication device: + + \snippet doc/src/snippets/code/src_gui_embedded_qtransportauth_qws.cpp 1 + + In the client code it is similar. Use the connectTransport() method + just the same then proxy in the authentication device instead of the + socket in write calls: + + \snippet doc/src/snippets/code/src_gui_embedded_qtransportauth_qws.cpp 2 +*/ + +static int hmac_md5( + unsigned char* text, /* pointer to data stream */ + int text_length, /* length of data stream */ + const unsigned char* key, /* pointer to authentication key */ + int key_length, /* length of authentication key */ + unsigned char * digest /* caller digest to be filled in */ + ); + + + +#define KEY_CACHE_SIZE 30 + +const char * const errorStrings[] = { + "pending identity verification", + "message too small to carry auth data", + "cache miss on connection oriented transport", + "no magic bytes on message", + "key not found for prog id", + "authorization key match failed", + "key out of date" +}; + +const char *QTransportAuth::errorString( const Data &d ) +{ + if (( d.status & ErrMask ) == Success ) + return "success"; + int e = d.status & ErrMask; + if ( e > OutOfDate ) + return "unknown"; + return errorStrings[e]; +} + +SxeRegistryLocker::SxeRegistryLocker( QObject *reg ) + : m_success( false ) + , m_reg( 0 ) +{ + if ( reg ) + if ( !QMetaObject::invokeMethod( reg, "lockManifest", Q_RETURN_ARG(bool, m_success) )) + m_success = false; + m_reg = reg; +} + +SxeRegistryLocker::~SxeRegistryLocker() +{ + if ( m_success ) + QMetaObject::invokeMethod( m_reg, "unlockManifest" ); +} + + +QTransportAuthPrivate::QTransportAuthPrivate() + : keyInitialised(false) + , m_packageRegistry( 0 ) +{ +} + +QTransportAuthPrivate::~QTransportAuthPrivate() +{ +} + +/*! + \internal + Construct a new QTransportAuth +*/ +QTransportAuth::QTransportAuth() : QObject(*new QTransportAuthPrivate) +{ + // qDebug( "creating transport auth" ); +} + +/*! + \internal + Destructor +*/ +QTransportAuth::~QTransportAuth() +{ + // qDebug( "deleting transport auth" ); +} + +/*! + Set the process key for this currently running Qt Extended process to + the \a authdata. \a authdata should be sizeof(struct AuthCookie) + in length and contain the key and program id. Use this method + when setting or changing the SXE identity of the current program. +*/ +void QTransportAuth::setProcessKey( const char *authdata ) +{ + Q_D(QTransportAuth); + ::memcpy(&d->authKey, authdata, sizeof(struct AuthCookie)); + QFile proc_key( QLatin1String("/proc/self/lids_key") ); + // where proc key exists use that instead + if ( proc_key.open( QIODevice::ReadOnly )) + { + qint64 kb = proc_key.read( (char*)&d->authKey.key, QSXE_KEY_LEN ); +#ifdef QTRANSPORTAUTH_DEBUG + qDebug( "Using %li bytes of /proc/%i/lids_key\n", (long int)kb, getpid() ); +#else + Q_UNUSED( kb ); +#endif + } + d->keyInitialised = true; +} + + +/*! + Apply \a key as the process key for the currently running application. + + \a prog is current ignored + + Deprecated function +*/ +void QTransportAuth::setProcessKey( const char *key, const char *prog ) +{ + Q_UNUSED(prog); + setProcessKey( key ); +#ifdef QTRANSPORTAUTH_DEBUG + char displaybuf[QSXE_KEY_LEN*2+1]; + hexstring( displaybuf, (const unsigned char *)key, QSXE_KEY_LEN ); + qDebug() << "key" << displaybuf << "set"; +#endif +} + +/*! + Register \a pr as a policy handler object. The object pointed to + by \a pr should have a slot as follows + \snippet doc/src/snippets/code/src_gui_embedded_qtransportauth_qws.cpp 3 + All requests received by this server will then generate a call to + this slot, and may be processed for policy compliance. +*/ +void QTransportAuth::registerPolicyReceiver( QObject *pr ) +{ + // not every policy receiver needs setup - no error if this fails + QMetaObject::invokeMethod( pr, "setupPolicyCheck" ); + + connect( this, SIGNAL(policyCheck(QTransportAuth::Data&,QString)), + pr, SLOT(policyCheck(QTransportAuth::Data&,QString)), Qt::DirectConnection ); +} + +/*! + Unregister the \a pr from being a policy handler. No more policyCheck signals + are received by this object. +*/ +void QTransportAuth::unregisterPolicyReceiver( QObject *pr ) +{ + disconnect( pr ); + // not every policy receiver needs tear down - no error if this fails + QMetaObject::invokeMethod( pr, "teardownPolicyCheck" ); +} + +/*! + Record a new transport connection with \a properties and \a descriptor. + + The calling code is responsible for destroying the returned data when the + tranport connection is closed. +*/ +QTransportAuth::Data *QTransportAuth::connectTransport( unsigned char properties, int descriptor ) +{ + Data *data = new Data(properties, descriptor); + data->status = Pending; + return data; +} + +/*! + Is the transport trusted. This is true iff data written into the + transport medium cannot be intercepted or modified by another process. + This is for example true for Unix Domain Sockets, but not for shared + memory or UDP sockets. + + There is of course an underlying assumption that the kernel implementing + the transport is sound, ie it cannot be compromised by writing to + /dev/kmem or loading untrusted modules +*/ +inline bool QTransportAuth::Data::trusted() const +{ + return (bool)(properties & Trusted); +} + +/*! + Assert that the transport is trusted. + + For example with respect to shared memory, if it is ensured that no untrusted + root processes are running, and that unix permissions have been set such that + any untrusted non-root processes do not have access rights, then a shared + memory transport could be asserted to be trusted. + + \sa trusted() +*/ +inline void QTransportAuth::Data::setTrusted( bool t ) +{ + properties = t ? properties | Trusted : properties & ~Trusted; +} + +/*! + Is the transport connection oriented. This is true iff once a connection + has been accepted, and state established, then further messages over the + transport are guaranteed to have come from the original connecting entity. + This is for example true for Unix Domain Sockets, but not + for shared memory or UDP sockets. + + By extension if the transport is not trusted() then it should not be + assumed to be connection oriented, since spoofed connection information + could be created. For example if we assume the TCP/IP transport is + trusted, it can be treated as connection oriented; but this is only the + case if intervening routers are trusted. + + Connection oriented transports have authorization cached against the + connection, and thus authorization is only done at connect time. +*/ +inline bool QTransportAuth::Data::connection() const +{ + return (bool)(properties & Connection); +} + +/*! + Assert that the transport is connection oriented. + + \sa connection() +*/ +inline void QTransportAuth::Data::setConnection( bool t ) +{ + properties = t ? properties | Connection : properties & ~Connection; +} + +/*! + Return a pointer to the instance of this process's QTransportAuth object +*/ +QTransportAuth *QTransportAuth::getInstance() +{ + static QTransportAuth theInstance; + + return &theInstance; +} + +/*! + Set the full path to the key file + + Since this is normally relative to Qtopia::qpeDir() this needs to be + set within the Qt Extended framework. + + The keyfile should be protected by file permissions or by MAC rules + such that it can only be read/written by the "qpe" server process +*/ +void QTransportAuth::setKeyFilePath( const QString &path ) +{ + Q_D(QTransportAuth); + d->m_keyFilePath = path; +} + +QString QTransportAuth::keyFilePath() const +{ + Q_D(const QTransportAuth); + return d->m_keyFilePath; +} + +void QTransportAuth::setLogFilePath( const QString &path ) +{ + Q_D(QTransportAuth); + d->m_logFilePath = path; +} + +QString QTransportAuth::logFilePath() const +{ + Q_D(const QTransportAuth); + return d->m_logFilePath; +} + +void QTransportAuth::setPackageRegistry( QObject *registry ) +{ + Q_D(QTransportAuth); + d->m_packageRegistry = registry; +} + +bool QTransportAuth::isDiscoveryMode() const +{ +#if defined(SXE_DISCOVERY) + static bool checked = false; + static bool yesItIs = false; + + if ( checked ) return yesItIs; + + yesItIs = ( getenv( "SXE_DISCOVERY_MODE" ) != 0 ); + if ( yesItIs ) + { + qWarning("SXE Discovery mode on, ALLOWING ALL requests and logging to %s", + qPrintable(logFilePath())); + QFile::remove( logFilePath() ); + } + checked = true; + return yesItIs; +#else + return false; +#endif +} + +/*! + \internal + Return the authorizer device mapped to this client. Note that this + could probably all be void* instead of QWSClient* for generality. + Until the need for that rears its head its QWSClient* to save the casts. + + #### OK the need has arrived, but the public API is frozen. +*/ +QIODevice *QTransportAuth::passThroughByClient( QWSClient *client ) const +{ + Q_D(const QTransportAuth); + + if ( client == 0 ) return 0; + if ( d->buffersByClient.contains( client )) + { + return d->buffersByClient[client]; + } + // qWarning( "buffer not found for client %p", client ); + return 0; +} + +/*! + \internal + Return a QIODevice pointer (to an internal QBuffer) which can be used + to receive data after authorisation on transport \a d. + + The return QIODevice will act as a pass-through. + + The data will be consumed from \a iod and forwarded on to the returned + QIODevice which can be connected to readyRead() signal handlers in + place of the original QIODevice \a iod. + + This will be called in the server process to handle incoming + authenticated requests. + + The returned QIODevice will take ownership of \a data which will be deleted + when the QIODevice is delected. + + \sa setTargetDevice() +*/ +QAuthDevice *QTransportAuth::recvBuf( QTransportAuth::Data *data, QIODevice *iod ) +{ + return new QAuthDevice( iod, data, QAuthDevice::Receive ); +} + +/*! + Return a QIODevice pointer (to an internal QBuffer) which can be used + to write data onto, for authorisation on transport \a d. + + The return QIODevice will act as a pass-through. + + The data written to the return QIODevice will be forwarded on to the + returned QIODevice. In the case of a QTcpSocket, this will cause it + to send out the data with the authentication information on it. + + This will be called in the client process to generate outgoing + authenticated requests. + + The returned QIODevice will take ownership of \a data which will be deleted + when the QIODevice is delected. + + \sa setTargetDevice() +*/ +QAuthDevice *QTransportAuth::authBuf( QTransportAuth::Data *data, QIODevice *iod ) +{ + return new QAuthDevice( iod, data, QAuthDevice::Send ); +} + +const unsigned char *QTransportAuth::getClientKey( unsigned char progId ) +{ + Q_D(QTransportAuth); + return d->getClientKey( progId ); +} + +void QTransportAuth::invalidateClientKeyCache() +{ + Q_D(QTransportAuth); + d->invalidateClientKeyCache(); +} + +QMutex *QTransportAuth::getKeyFileMutex() +{ + Q_D(QTransportAuth); + return &d->keyfileMutex; +} + +/* + \internal + Respond to the destroyed(QObject*) signal of the QAuthDevice's + client object and remove it from the buffersByClient lookup hash. +*/ +void QTransportAuth::bufferDestroyed( QObject *cli ) +{ + Q_D(QTransportAuth); + if ( cli == NULL ) return; + + if ( d->buffersByClient.contains( cli )) + { + d->buffersByClient.remove( cli ); + // qDebug( "@@@@@@@ client %p removed @@@@@@@@@", cli ); + } + // qDebug( " client count %d", d->buffersByClient.count() ); +} + +bool QTransportAuth::authorizeRequest( QTransportAuth::Data &d, const QString &request ) +{ + bool isAuthorized = true; + + if ( !request.isEmpty() && request != QLatin1String("Unknown") ) + { + d.status &= QTransportAuth::ErrMask; // clear the status + emit policyCheck( d, request ); + isAuthorized = (( d.status & QTransportAuth::StatusMask ) == QTransportAuth::Allow ); + } +#if defined(SXE_DISCOVERY) + if (isDiscoveryMode()) { +#ifndef QT_NO_TEXTSTREAM + if (!logFilePath().isEmpty()) { + QFile log( logFilePath() ); + if (!log.open(QIODevice::WriteOnly | QIODevice::Append)) { + qWarning("Could not write to log in discovery mode: %s", + qPrintable(logFilePath())); + } else { + QTextStream ts( &log ); + ts << d.progId << '\t' << ( isAuthorized ? "Allow" : "Deny" ) << '\t' << request << endl; + } + } +#endif + isAuthorized = true; + } +#endif + if ( !isAuthorized ) + { + qWarning( "%s - denied: for Program Id %u [PID %d]" + , qPrintable(request), d.progId, d.processId ); + + char linkTarget[BUF_SIZE]=""; + char exeLink[BUF_SIZE]=""; + char cmdlinePath[BUF_SIZE]=""; + char cmdline[BUF_SIZE]=""; + + //get executable from /proc/pid/exe + snprintf( exeLink, BUF_SIZE, "/proc/%d/exe", d.processId ); + if ( -1 == ::readlink( exeLink, linkTarget, BUF_SIZE - 1 ) ) + { + qWarning( "SXE:- Error encountered in retrieving executable link target from /proc/%u/exe : %s", + d.processId, strerror(errno) ); + snprintf( linkTarget, BUF_SIZE, "%s", linkTarget ); + } + + //get cmdline from proc/pid/cmdline + snprintf( cmdlinePath, BUF_SIZE, "/proc/%d/cmdline", d.processId ); + int cmdlineFd = open( cmdlinePath, O_RDONLY ); + if ( cmdlineFd == -1 ) + { + qWarning( "SXE:- Error encountered in opening /proc/%u/cmdline: %s", + d.processId, strerror(errno) ); + snprintf( cmdline, BUF_SIZE, "%s", "Unknown" ); + } + else + { + if ( -1 == ::read(cmdlineFd, cmdline, BUF_SIZE - 1 ) ) + { + qWarning( "SXE:- Error encountered in reading /proc/%u/cmdline : %s", + d.processId, strerror(errno) ); + snprintf( cmdline, BUF_SIZE, "%s", "Unknown" ); + } + close( cmdlineFd ); + } + + syslog( LOG_ERR | LOG_LOCAL6, "%s // PID:%u // ProgId:%u // Exe:%s // Request:%s // Cmdline:%s", + "<SXE Breach>", d.processId, d.progId, linkTarget, qPrintable(request), cmdline); + } + + return isAuthorized; +} + +inline bool __fileOpen( QFile *f ) +{ +#ifdef QTRANSPORTAUTH_DEBUG + if ( f->open( QIODevice::ReadOnly )) + { + qDebug( "Opened file: %s\n", qPrintable( f->fileName() )); + return true; + } + else + { + qWarning( "Could not open file: %s\n", qPrintable( f->fileName() )); + return false; + } +#else + return ( f->open( QIODevice::ReadOnly )); +#endif +} + +/*! + \internal + Find client keys for the \a progId. If it is cached should be very + fast, otherwise requires a read of the secret key file + + In the success case a pointer to the keys is returned. The pointer is + to storage allocated for the internal cache and must be used asap. + + The list returned is a sequence of one or more keys which match the + progId. There is no separator, each 16 byte sequence represents a key. + The sequence is followed by two iterations of the SXE magic + bytes,eg 0xBA, 0xD4, 0xD4, 0xBA, 0xBA, 0xD4, 0xD4, 0xBA + + NULL is returned in the following cases: + \list + \o the keyfiles could not be accessed - error condition + \o there was no key for the supplied program id - key auth failed + \endlist + + Note that for the keyfiles, there is multi-thread and multi-process + concurrency issues: they can be read by the qpe process when + QTransportAuth calls getClientKey to verify a request, and they can be + read or written by the packagemanager when updating package data. + + To protect against this, the keyfileMutex & SxeRegistryLocker is used. + + The sxe_installer tool can also update inode and device numbers in + the manifest file, but this only occurs outside of normal operation, + so qpe and packagemanager are never running when this occurs. +*/ +const unsigned char *QTransportAuthPrivate::getClientKey(unsigned char progId) +{ + int manifestMatchCount = 0; + struct IdBlock mr; + int total_size = 0; + char *result = 0; + char *result_ptr; + int keysFound = 0; + bool foundKey; + int keysRead = 0; + struct usr_key_entry keys_list[128]; + + if ( keyCache.contains( progId )) + return (const unsigned char *)keyCache[progId]; + + SxeRegistryLocker rlock( m_packageRegistry ); + + // ### Qt 4.3: this is hacky - see documentation for setKeyFilePath + QString manifestPath = m_keyFilePath + QLatin1String("/manifest"); + QString actualKeyPath = QLatin1String("/proc/lids/keys"); + bool noFailOnKeyMissing = true; + if ( !QFile::exists( actualKeyPath )) { + actualKeyPath = m_keyFilePath + QLatin1String( "/" QSXE_KEYFILE ); + } + QFile kf( actualKeyPath ); + QFile mn( manifestPath ); + if ( !__fileOpen( &mn )) + goto key_not_found; + // first find how much storage is needed + while ( mn.read( (char*)&mr, sizeof(struct IdBlock)) > 0 ) + if ( mr.progId == progId ) + manifestMatchCount++; + if ( manifestMatchCount == 0 ) + goto key_not_found; + if ( !__fileOpen( &kf )) + { + noFailOnKeyMissing = false; + goto key_not_found; + } + total_size = 2 * QSXE_MAGIC_BYTES + manifestMatchCount * QSXE_KEY_LEN; + result = (char*)malloc( total_size ); + Q_CHECK_PTR( result ); + mn.seek( 0 ); + result_ptr = result; + /* reading whole key array in is much more efficient, 99% case is this loop only + executes once, should not have more than 128 keyed items */ + while (( keysRead = kf.read( (char*)keys_list, sizeof(struct usr_key_entry)*128 )) > 0 ) + { + /* qDebug("PID %d: getClientKey() - read %d bytes = %d keys from %s", getpid(), keysRead, + keysRead/sizeof(struct usr_key_entry), qPrintable(actualKeyPath)); */ + keysRead /= sizeof(struct usr_key_entry); + while ( mn.read( (char*)&mr, sizeof(struct IdBlock)) > 0 ) + { + if ( mr.progId == progId ) + { + foundKey = false; + for ( int i = 0; i < keysRead; ++i ) + { + /* if ( i == 0 ) + qDebug() << " pid" << getpid() << "looking for device" << (dev_t)mr.device << "inode" << (ino_t)mr.inode; + qDebug() << " pid" << getpid() << "trying device" << keys_list[i].dev << "inode" << keys_list[i].ino; */ + if ( keys_list[i].ino == (ino_t)mr.inode && keys_list[i].dev == (dev_t)mr.device ) + { + memcpy( result_ptr, keys_list[i].key, QSXE_KEY_LEN ); + result_ptr += QSXE_KEY_LEN; + foundKey = true; + break; + } + } + if ( foundKey ) + { + keysFound++; + if ( keysFound == manifestMatchCount ) + break; + } + } + } + } + if ( result_ptr == result ) // nothing found! + goto key_not_found; + // 2 x magic bytes sentinel at end of sequence + for ( int i = 0; i < 2; ++i ) + for ( int j = 0; j < QSXE_MAGIC_BYTES; ++j ) + *result_ptr++ = magic[j]; + keyCache.insert( progId, result, total_size / 10 ); + /* qDebug( "PID %d : Found %d client keys for prog %u", getpid(), keysFound, progId ); */ + goto success_out; + +key_not_found: + if ( noFailOnKeyMissing ) // return an "empty" set of keys in this case + { + if ( result == 0 ) + { + result = (char*)malloc( 2 * QSXE_MAGIC_BYTES ); + Q_CHECK_PTR( result ); + } + result_ptr = result; + for ( int i = 0; i < 2; ++i ) + for ( int j = 0; j < QSXE_MAGIC_BYTES; ++j ) + *result_ptr++ = magic[j]; + return (unsigned char *)result; + } + qWarning( "PID %d : Not found client key for prog %u", getpid(), progId ); + if ( result ) + { + free( result ); + result = 0; + } +success_out: + if ( mn.isOpen() ) + mn.close(); + if ( kf.isOpen() ) + kf.close(); + return (unsigned char *)result; +} + +void QTransportAuthPrivate::invalidateClientKeyCache() +{ + keyfileMutex.lock(); + keyCache.clear(); + keyfileMutex.unlock(); +} + +//////////////////////////////////////////////////////////////////////// +//// +//// RequestAnalyzer definition +//// + + +RequestAnalyzer::RequestAnalyzer() + : moreData( false ) + , dataSize( 0 ) +{ +} + +RequestAnalyzer::~RequestAnalyzer() +{ +} + +/*! + Analzye the data in the\a msgQueue according to some protocol + and produce a request string for policy analysis. + + If enough data is in the queue for analysis of a complete message, + return a non-null string, and set a flag so requireMoreData() will + return false; otherwise return a null string and requireMoreData() + return true. + + The amount of bytes analyzed is then available via bytesAnalyzed(). + + A null string is also returned in the case where the message was + corrupt and could not be analyzed. In this case requireMoreData() + returns false. + +Note: this method will modify the msgQueue and pull off the data + deemed to be corrupt, in the case of corrupt data. + + In all other cases the msgQueue is left alone. The calling code + should then pull off the analyzed data. Use bytesAnalzyed() to + find how much data to pull off the queue. +*/ +QString RequestAnalyzer::analyze( QByteArray *msgQueue ) +{ +#ifdef Q_WS_QWS + dataSize = 0; + moreData = false; + QBuffer cmdBuf( msgQueue ); + cmdBuf.open( QIODevice::ReadOnly | QIODevice::Unbuffered ); + QWSCommand::Type command_type = (QWSCommand::Type)(qws_read_uint( &cmdBuf )); + QWSCommand *command = QWSCommand::factory(command_type); + // if NULL, factory will have already printed warning for bogus + // command_type just purge the bad stuff and attempt to recover + if ( command == NULL ) + { + *msgQueue = msgQueue->mid( sizeof(int) ); + return QString(); + } + QString request = QLatin1String(qws_getCommandTypeString(command_type)); +#ifndef QT_NO_COP + if ( !command->read( &cmdBuf )) + { + // not all command arrived yet - come back later + delete command; + moreData = true; + return QString(); + } + if ( command_type == QWSCommand::QCopSend ) + { + QWSQCopSendCommand *sendCommand = static_cast<QWSQCopSendCommand*>(command); + request += QString( QLatin1String("/QCop/%1/%2") ).arg( sendCommand->channel ).arg( sendCommand->message ); + } + if ( command_type == QWSCommand::QCopRegisterChannel ) + { + QWSQCopRegisterChannelCommand *registerCommand = static_cast<QWSQCopRegisterChannelCommand*>(command); + request += QString( QLatin1String("/QCop/RegisterChannel/%1") ).arg( registerCommand->channel ); + } +#endif + dataSize = QWS_PROTOCOL_ITEM_SIZE( *command ); + delete command; + return request; +#else + Q_UNUSED(msgQueue); + return QString(); +#endif +} + +//////////////////////////////////////////////////////////////////////// +//// +//// AuthDevice definition +//// + +/*! + Constructs a new auth device for the transport \a data and I/O device \a parent. + + Incoming or outgoing data will be authenticated according to the auth direction \a dir. + + The auth device will take ownership of the transport \a data and delete it when the device + is destroyed. +*/ +QAuthDevice::QAuthDevice( QIODevice *parent, QTransportAuth::Data *data, AuthDirection dir ) + : QIODevice( parent ) + , d( data ) + , way( dir ) + , m_target( parent ) + , m_client( 0 ) + , m_bytesAvailable( 0 ) + , m_skipWritten( 0 ) + , analyzer( 0 ) +{ + if ( dir == Receive ) // server side + { + connect( m_target, SIGNAL(readyRead()), + this, SLOT(recvReadyRead())); + } else { + connect( m_target, SIGNAL(readyRead()), + this, SIGNAL(readyRead())); + } + connect( m_target, SIGNAL(bytesWritten(qint64)), + this, SLOT(targetBytesWritten(qint64)) ); + open( QIODevice::ReadWrite | QIODevice::Unbuffered ); +} + +QAuthDevice::~QAuthDevice() +{ + if ( analyzer ) + delete analyzer; + delete d; +} + +/*! + \internal + Store a pointer to the related device or instance which this + authorizer is proxying for +*/ +void QAuthDevice::setClient( QObject *cli ) +{ + m_client = cli; + QTransportAuth::getInstance()->d_func()->buffersByClient[cli] = this; + QObject::connect( cli, SIGNAL(destroyed(QObject*)), + QTransportAuth::getInstance(), SLOT(bufferDestroyed(QObject*)) ); + // qDebug( "@@@@@@@@@@@@ client set %p @@@@@@@@@", cli ); + // qDebug( " client count %d", QTransportAuth::getInstance()->d_func()->buffersByClient.count() ); +} + +QObject *QAuthDevice::client() const +{ + return m_client; +} + +/* + \fn void QAuthDevice::authViolation(QTransportAuth::Data &) + + This signal is emitted if an authorization failure is generated, as + described in checkAuth(); + + \sa checkAuth() +*/ + + +/* + \fn void QAuthDevice::policyCheck(QTransportAuth::Data &transport, const QString &request ) + + This signal is emitted when a transport successfully delivers a request + and gives the opportunity to either deny or accept the request. + + This signal must be connected in the same thread, ie it cannot be queued. + + As soon as all handlers connected to this signal are processed the Allow or + Deny state on the \a transport is checked, and the request is allowed or denied + accordingly. + + \sa checkAuth() +*/ + +/*! + \internal + Reimplement QIODevice writeData method. + + For client end, when the device is written to the incoming data is + processed and an authentication header calculated. This is pushed + into the target device, followed by the actual incoming data (the + payload). + + For server end, it is a fatal error to write to the device. +*/ +qint64 QAuthDevice::writeData(const char *data, qint64 len) +{ + if ( way == Receive ) // server + return m_target->write( data, len ); + // client +#ifdef QTRANSPORTAUTH_DEBUG + char displaybuf[1024]; +#endif + char header[QSXE_HEADER_LEN]; + ::memset( header, 0, QSXE_HEADER_LEN ); + qint64 bytes = 0; + if ( QTransportAuth::getInstance()->authToMessage( *d, header, data, len )) + { + m_target->write( header, QSXE_HEADER_LEN ); +#ifdef QTRANSPORTAUTH_DEBUG + hexstring( displaybuf, (const unsigned char *)header, QSXE_HEADER_LEN ); + qDebug( "%d QAuthDevice::writeData - CLIENT: Header written: %s", getpid(), displaybuf ); +#endif + m_skipWritten += QSXE_HEADER_LEN; + } + m_target->write( data, len ); + bytes += len; +#ifdef QTRANSPORTAUTH_DEBUG + int bytesToDisplay = bytes; + const unsigned char *dataptr = (const unsigned char *)data; + while ( bytesToDisplay > 0 ) + { + int amt = bytes < 500 ? bytes : 500; + hexstring( displaybuf, dataptr, amt ); + qDebug( "%d QAuthDevice::writeData - CLIENT: %s", getpid(), bytes > 0 ? displaybuf : "(null)" ); + dataptr += 500; + bytesToDisplay -= 500; + } +#endif + if ( m_target->inherits( "QAbstractSocket" )) + static_cast<QAbstractSocket*>(m_target)->flush(); + return bytes; +} + +/*! + Reimplement from QIODevice + + Read data out of the internal message queue, reduce the queue by the amount + read. Note that the amount available is only ever the size of a command + (although a command can be very big) since we need to check at command + boundaries for new authentication headers. +*/ +qint64 QAuthDevice::readData( char *data, qint64 maxSize ) +{ + if ( way == Send ) // client + return m_target->read( data, maxSize ); + if ( msgQueue.size() == 0 ) + return 0; +#ifdef QTRANSPORTAUTH_DEBUG + char displaybuf[1024]; + hexstring( displaybuf, reinterpret_cast<const unsigned char *>(msgQueue.constData()), + msgQueue.size() > 500 ? 500 : msgQueue.size() ); + qDebug() << getpid() << "QAuthDevice::readData() buffered/requested/avail" + << msgQueue.size() << maxSize << m_bytesAvailable << displaybuf; +#endif + Q_ASSERT( m_bytesAvailable <= msgQueue.size() ); + qint64 bytes = ( maxSize > m_bytesAvailable ) ? m_bytesAvailable : maxSize; + ::memcpy( data, msgQueue.constData(), bytes ); + msgQueue = msgQueue.mid( bytes ); + m_bytesAvailable -= bytes; + return bytes; +} + +/*! + \internal + Receive readyRead signal from the target recv device. In response + authorize the data, and write results out to the recvBuf() device + for processing by the application. Trigger the readyRead signal. + + Authorizing involves first checking the transport is valid, ie the + handshake has either already been done and is cached on a trusted + transport, or was valid with this message; then second passing the + string representation of the service request up to any policyReceivers + + If either of these fail, the message is denied. In discovery mode + denied messages are allowed, but the message is logged. +*/ +void QAuthDevice::recvReadyRead() +{ + qint64 bytes = m_target->bytesAvailable(); + if ( bytes <= 0 ) return; + open( QIODevice::ReadWrite | QIODevice::Unbuffered ); + QUnixSocket *usock = static_cast<QUnixSocket*>(m_target); + QUnixSocketMessage msg = usock->read(); + msgQueue.append( msg.bytes() ); + d->processId = msg.processId(); + // if "fragmented" packet 1/2 way through start of a command, ie + // in the QWS msg type, cant do anything, come back later when + // there's more of the packet + if ( msgQueue.size() < (int)sizeof(int) ) + { + // qDebug() << "returning: msg size too small" << msgQueue.size(); + return; + } +#ifdef QTRANSPORTAUTH_DEBUG + char displaybuf[1024]; + hexstring( displaybuf, reinterpret_cast<const unsigned char *>(msgQueue.constData()), + msgQueue.size() > 500 ? 500 : msgQueue.size() ); + qDebug( "%d ***** SERVER read %lli bytes - msg %s", getpid(), bytes, displaybuf ); +#endif + + bool bufHasMessages = msgQueue.size() >= (int)sizeof(int); + while ( bufHasMessages ) + { + unsigned char saveStatus = d->status; + if (( d->status & QTransportAuth::ErrMask ) == QTransportAuth::NoSuchKey ) + { + QTransportAuth::getInstance()->authorizeRequest( *d, QLatin1String("NoSuchKey") ); + break; + } + if ( !QTransportAuth::getInstance()->authFromMessage( *d, msgQueue, msgQueue.size() )) + { + // not all arrived yet? come back later + if (( d->status & QTransportAuth::ErrMask ) == QTransportAuth::TooSmall ) + { + d->status = saveStatus; + return; + } + } + if (( d->status & QTransportAuth::ErrMask ) == QTransportAuth::NoMagic ) + { + // no msg auth header, don't change the success status for connections + if ( d->connection() ) + d->status = saveStatus; + } + else + { + // msg auth header detected and auth determined, remove hdr + msgQueue = msgQueue.mid( QSXE_HEADER_LEN ); + } + if ( !authorizeMessage() ) + break; + bufHasMessages = msgQueue.size() >= (int)sizeof(int); + } +} + +/** + \internal + Handle bytesWritten signals from the underlying target device. + We adjust the target's value for bytes that are part of auth packets. +*/ +void QAuthDevice::targetBytesWritten( qint64 bytes ) +{ + if ( m_skipWritten >= bytes ) { + m_skipWritten -= bytes; + bytes = 0; + } else if ( m_skipWritten > 0 ) { + bytes -= m_skipWritten; + m_skipWritten = 0; + } + if ( bytes > 0 ) { + emit bytesWritten( bytes ); + } +} + +/** + \internal + Pre-process the message to determine what QWS command it is. This + information is used as the "request" for the purposes of authorization. + + The request and other data on the connection (id, PID, etc.) are forwarded + to all policy listeners by emitting a signal. + + The signal must be processed synchronously because on return the allow/deny + status is used immediately to either drop or continue processing the message. +*/ +bool QAuthDevice::authorizeMessage() +{ + if ( analyzer == NULL ) + analyzer = new RequestAnalyzer(); + QString request = (*analyzer)( &msgQueue ); + if ( analyzer->requireMoreData() ) + return false; + bool isAuthorized = true; + + if ( !request.isEmpty() && request != QLatin1String("Unknown") ) + { + isAuthorized = QTransportAuth::getInstance()->authorizeRequest( *d, request ); + } + + bool moreToProcess = ( msgQueue.size() - analyzer->bytesAnalyzed() ) > (int)sizeof(int); + if ( isAuthorized ) + { +#ifdef QTRANSPORTAUTH_DEBUG + qDebug() << getpid() << "SERVER authorized: releasing" << analyzer->bytesAnalyzed() << "byte command" << request; +#endif + m_bytesAvailable = analyzer->bytesAnalyzed(); + emit QIODevice::readyRead(); + return moreToProcess; + } + else + { + msgQueue = msgQueue.mid( analyzer->bytesAnalyzed() ); + } + + return true; +} + +void QAuthDevice::setRequestAnalyzer( RequestAnalyzer *ra ) +{ + Q_ASSERT( ra ); + if ( analyzer ) + delete analyzer; + analyzer = ra; +} + +/*! + \internal + Add authentication header to the beginning of a message + + Note that the per-process auth cookie is used. This key should be rewritten in + the binary image of the executable at install time to make it unique. + + For this to be secure some mechanism (eg MAC kernel or other + permissions) must prevent other processes from reading the key. + + The buffer must have AUTH_SPACE(0) bytes spare at the beginning for the + authentication header to be added. + + Returns true if header successfully added. Will fail if the + per-process key has not yet been set with setProcessKey() +*/ +bool QTransportAuth::authToMessage( QTransportAuth::Data &d, char *hdr, const char *msg, int msgLen ) +{ + // qDebug( "authToMessage(): prog id %u", d.progId ); + // only authorize connection oriented transports once, unless key has changed + if ( d.connection() && ((d.status & QTransportAuth::ErrMask) != QTransportAuth::Pending) && + d_func()->authKey.progId == d.progId ) + return false; + d.progId = d_func()->authKey.progId; + // If Unix socket credentials are being used the key wont be set + if ( !d_func()->keyInitialised ) + return false; + unsigned char digest[QSXE_KEY_LEN]; + char *msgPtr = hdr; + // magic always goes on the beginning + for ( int m = 0; m < QSXE_MAGIC_BYTES; ++m ) + *msgPtr++ = magic[m]; + hdr[ QSXE_LEN_IDX ] = (unsigned char)msgLen; + if ( !d.trusted()) + { + // Use HMAC + int rc = hmac_md5( (unsigned char *)msg, msgLen, d_func()->authKey.key, QSXE_KEY_LEN, digest ); + if ( rc == -1 ) + return false; + memcpy( hdr + QSXE_KEY_IDX, digest, QSXE_KEY_LEN ); + } + else + { + memcpy( hdr + QSXE_KEY_IDX, d_func()->authKey.key, QSXE_KEY_LEN ); + } + + hdr[ QSXE_PROG_IDX ] = d_func()->authKey.progId; + +#ifdef QTRANSPORTAUTH_DEBUG + char keydisplay[QSXE_KEY_LEN*2+1]; + hexstring( keydisplay, d_func()->authKey.key, QSXE_KEY_LEN ); + + qDebug( "%d CLIENT Auth to message %s against prog id %u and key %s\n", + getpid(), msg, d_func()->authKey.progId, keydisplay ); +#endif + + // TODO implement sequence to prevent replay attack, not required + // for trusted transports + hdr[ QSXE_SEQ_IDX ] = 1; // dummy sequence + + d.status = ( d.status & QTransportAuth::StatusMask ) | QTransportAuth::Success; + return true; +} + + +/*! + Check authorization on the \a msg, which must be of size \a msgLen, + for the transport \a d. + + If able to determine authorization, return the program identity of + the message source in the reference \a progId, and return true. + + Otherwise return false. + + If data is being received on a socket, it may be that more data is yet + needed before authentication can proceed. + + Also the message may not be an authenticated at all. + + In these cases the method returns false to indicate authorization could + not be determined: + \list + \i The message is too small to carry the authentication data + (status TooSmall is set on the \a d transport ) + \i The 4 magic bytes are missing from the message start + (status NoMagic is set on the \a d transport ) + \i The message is too small to carry the auth + claimed payload + (status TooSmall is set on the \a d transport ) + \endlist + + If however the authentication header (preceded by the magic bytes) and + any authenticated payload is received the method will determine the + authentication status, and return true. + + In the following cases as well as returning true it will also emit + an authViolation(): + \list + \i If the program id claimed by the message is not found in the key file + (status NoSuchKey is set on the \a d transport ) + \i The authentication token failed against the claimed program id: + \list + \i in the case of trusted transports, the secret did not match + \i in the case of untrusted transports the HMAC code did not match + \endlist + (status FailMatch is set on the \a d transport ) + \endlist + + In these cases the authViolation( QTransportAuth::Data d ) signal is emitted + and the error string can be obtained from the status like this: + \snippet doc/src/snippets/code/src_gui_embedded_qtransportauth_qws.cpp 4 +*/ +bool QTransportAuth::authFromMessage( QTransportAuth::Data &d, const char *msg, int msgLen ) +{ + if ( msgLen < QSXE_MAGIC_BYTES ) + { + d.status = ( d.status & QTransportAuth::StatusMask ) | QTransportAuth::TooSmall; + return false; + } + // if no magic bytes, exit straight away + int m; + const unsigned char *mptr = reinterpret_cast<const unsigned char *>(msg); + for ( m = 0; m < QSXE_MAGIC_BYTES; ++m ) + { + if ( *mptr++ != magic[m] ) + { + d.status = ( d.status & QTransportAuth::StatusMask ) | QTransportAuth::NoMagic; + return false; + } + } + + if ( msgLen < AUTH_SPACE(1) ) + { + d.status = ( d.status & QTransportAuth::StatusMask ) | QTransportAuth::TooSmall; + return false; + } + + // At this point we know the header is at least long enough to contain valid auth + // data, however the data may be spoofed. If it is not verified then the status will + // be set to uncertified so the spoofed data will not be relied on. However we want to + // know the program id which is being reported (even if it might be spoofed) for + // policy debugging purposes. So set it here, rather than after verification. + d.progId = msg[QSXE_PROG_IDX]; + +#ifdef QTRANSPORTAUTH_DEBUG + char authhdr[QSXE_HEADER_LEN*2+1]; + hexstring( authhdr, reinterpret_cast<const unsigned char *>(msg), QSXE_HEADER_LEN ); + qDebug( "%d SERVER authFromMessage(): message header is %s", + getpid(), authhdr ); +#endif + + unsigned char authLen = (unsigned char)(msg[ QSXE_LEN_IDX ]); + + if ( msgLen < AUTH_SPACE(authLen) ) + { + d.status = ( d.status & QTransportAuth::StatusMask ) | QTransportAuth::TooSmall; + return false; + } + + bool isCached = d_func()->keyCache.contains( d.progId ); + const unsigned char *clientKey = d_func()->getClientKey( d.progId ); + if ( clientKey == NULL ) + { + d.status = ( d.status & QTransportAuth::StatusMask ) | QTransportAuth::NoSuchKey; + return false; + } + +#ifdef QTRANSPORTAUTH_DEBUG + char keydisplay[QSXE_KEY_LEN*2+1]; + hexstring( keydisplay, clientKey, QSXE_KEY_LEN ); + qDebug( "\t\tauthFromMessage(): message %s against prog id %u and key %s\n", + AUTH_DATA(msg), ((unsigned int)d.progId), keydisplay ); +#endif + + const unsigned char *auth_tok; + unsigned char digest[QSXE_KEY_LEN]; + bool multi_tok = false; + + bool need_to_recheck=false; + do + { + if ( !d.trusted()) + { + hmac_md5( AUTH_DATA(msg), authLen, clientKey, QSXE_KEY_LEN, digest ); + auth_tok = digest; + } + else + { + auth_tok = clientKey; + multi_tok = true; // 1 or more keys are in the clientKey + } + while( true ) + { + if ( memcmp( auth_tok, magic, QSXE_MAGIC_BYTES ) == 0 + && memcmp( auth_tok + QSXE_MAGIC_BYTES, magic, QSXE_MAGIC_BYTES ) == 0 ) + break; + if ( memcmp( msg + QSXE_KEY_IDX, auth_tok, QSXE_KEY_LEN ) == 0 ) + { + d.status = ( d.status & QTransportAuth::StatusMask ) | QTransportAuth::Success; + return true; + } + if ( !multi_tok ) + break; + auth_tok += QSXE_KEY_LEN; + } + //the keys cached on d.progId may not contain the binary key because the cache entry was made + //before the binary had first started, must search for client key again. + if ( isCached ) + { + d_func()->keyCache.remove(d.progId); + isCached = false; + +#ifdef QTRANSPORTAUTH_DEBUG + qDebug() << "QTransportAuth::authFromMessage(): key not found in set of keys cached" + << "against prog Id =" << d.progId << ". Re-obtaining client key. "; +#endif + clientKey = d_func()->getClientKey( d.progId ); + if ( clientKey == NULL ) + { + d.status = ( d.status & QTransportAuth::StatusMask ) | QTransportAuth::NoSuchKey; + return false; + } + need_to_recheck = true; + } + else + { + need_to_recheck = false; + } + } while( need_to_recheck ); + + d.status = ( d.status & QTransportAuth::StatusMask ) | QTransportAuth::FailMatch; + qWarning() << "QTransportAuth::authFromMessage():failed authentication"; + FAREnforcer::getInstance()->logAuthAttempt( QDateTime::currentDateTime() ); + emit authViolation( d ); + return false; +} + + +#ifdef QTRANSPORTAUTH_DEBUG +/*! + sprintf into hex - dest \a buf, src \a key, \a key_len is length of key. + + The target buf should be [ key_len * 2 + 1 ] in size +*/ +void hexstring( char *buf, const unsigned char* key, size_t key_len ) +{ + unsigned int i, p; + for ( i = 0, p = 0; i < key_len; i++, p+=2 ) + { + unsigned char lo_nibble = key[i] & 0x0f; + unsigned char hi_nibble = key[i] >> 4; + buf[p] = (int)hi_nibble > 9 ? hi_nibble-10 + 'A' : hi_nibble + '0'; + buf[p+1] = (int)lo_nibble > 9 ? lo_nibble-10 + 'A' : lo_nibble + '0'; + } + buf[p] = '\0'; +} +#endif + +/* + HMAC MD5 as listed in RFC 2104 + + This code is taken from: + + http://www.faqs.org/rfcs/rfc2104.html + + with the allowance for keys other than length 16 removed, but otherwise + a straight cut-and-paste. + + The HMAC_MD5 transform looks like: + + \snippet doc/src/snippets/code/src.gui.embedded.qtransportauth_qws.cpp 5 + + \list + \i where K is an n byte key + \i ipad is the byte 0x36 repeated 64 times + \i opad is the byte 0x5c repeated 64 times + \i and text is the data being protected + \endlist + + Hardware is available with accelerated implementations of HMAC-MD5 and + HMAC-SHA1. Where this hardware is available, this routine should be + replaced with a call into the accelerated version. +*/ + +static int hmac_md5( + unsigned char* text, /* pointer to data stream */ + int text_length, /* length of data stream */ + const unsigned char* key, /* pointer to authentication key */ + int key_length, /* length of authentication key */ + unsigned char * digest /* caller digest to be filled in */ + ) +{ + MD5Context context; + unsigned char k_ipad[65]; /* inner padding - * key XORd with ipad */ + unsigned char k_opad[65]; /* outer padding - * key XORd with opad */ + int i; + + /* in this implementation key_length == 16 */ + if ( key_length != 16 ) + { + fprintf( stderr, "Key length was %d - must be 16 bytes", key_length ); + return 0; + } + + /* start out by storing key in pads */ + memset( k_ipad, 0, sizeof k_ipad ); + memset( k_opad, 0, sizeof k_opad ); + memcpy( k_ipad, key, key_length ); + memcpy( k_opad, key, key_length ); + + /* XOR key with ipad and opad values */ + for (i=0; i<64; i++) { + k_ipad[i] ^= 0x36; + k_opad[i] ^= 0x5c; + } + + /* perform inner MD5 */ + MD5Init(&context); /* init context for 1st pass */ + MD5Update(&context, k_ipad, 64); /* start with inner pad */ + MD5Update(&context, text, text_length); /* then text of datagram */ + MD5Final(&context, digest); /* finish up 1st pass */ + + /* perform outer MD5 */ + MD5Init(&context); /* init context for 2nd pass */ + MD5Update(&context, k_opad, 64); /* start with outer pad */ + MD5Update(&context, digest, 16); /* then results of 1st * hash */ + MD5Final(&context, digest); /* finish up 2nd pass */ + return 1; +} + + +const int FAREnforcer::minutelyRate = 4; //allowed number of false authentication attempts per minute +const QString FAREnforcer::FARMessage = QLatin1String("FAR_Exceeded"); +const QString FAREnforcer::SxeTag = QLatin1String("<SXE Breach>"); +const int FAREnforcer::minute = 60; + +FAREnforcer::FAREnforcer():authAttempts() +{ + QDateTime nullDateTime = QDateTime(); + for (int i = 0; i < minutelyRate; i++ ) + authAttempts << nullDateTime; +} + + +FAREnforcer *FAREnforcer::getInstance() +{ + static FAREnforcer theInstance; + return &theInstance; +} + +void FAREnforcer::logAuthAttempt( QDateTime time ) +{ + QDateTime dt = authAttempts.takeFirst(); + + authAttempts.append( time ); + if ( dt.secsTo( authAttempts.last() ) <= minute ) + { +#if defined(SXE_DISCOVERY) + if ( QTransportAuth::getInstance()->isDiscoveryMode() ) { + static QBasicAtomicInt reported = Q_BASIC_ATOMIC_INITIALIZER(0); + if ( reported.testAndSetRelaxed(0,1) ) { +#ifndef QT_NO_TEXTSTREAM + QString logFilePath = QTransportAuth::getInstance()->logFilePath(); + if ( !logFilePath.isEmpty() ) { + QFile log( logFilePath ); + if ( !log.open(QIODevice::WriteOnly | QIODevice::Append) ) { + qWarning("Could not write to log in discovery mode: %s", + qPrintable(logFilePath) ); + } else { + QTextStream ts( &log ); + ts << "\t\tWarning: False Authentication Rate of " << minutelyRate << "\n" + << "\t\tserver connections/authentications per minute has been exceeded,\n" + << "\t\tno further warnings will be issued\n"; + } + } + } +#endif + reset(); + return; + } +#endif + syslog( LOG_ERR | LOG_LOCAL6, "%s %s", + qPrintable( FAREnforcer::SxeTag ), + qPrintable( FAREnforcer::FARMessage ) ); + reset(); + } +} + +void FAREnforcer::reset() +{ + QDateTime nullDateTime = QDateTime(); + for (int i = 0; i < minutelyRate; i++ ) + authAttempts[i] = nullDateTime; +} + +QT_END_NAMESPACE + +#include "moc_qtransportauth_qws_p.cpp" + +#endif // QT_NO_SXE diff --git a/src/gui/embedded/qtransportauth_qws.h b/src/gui/embedded/qtransportauth_qws.h new file mode 100644 index 0000000..2d2dd52 --- /dev/null +++ b/src/gui/embedded/qtransportauth_qws.h @@ -0,0 +1,281 @@ +/**************************************************************************** +** +** 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$ +** +****************************************************************************/ + +#ifndef QTRANSPORTAUTH_QWS_H +#define QTRANSPORTAUTH_QWS_H + +#include <QtCore/qglobal.h> + +#if !defined(QT_NO_SXE) || defined(SXE_INSTALLER) + +#include <QtCore/qobject.h> +#include <QtCore/qhash.h> +#include <QtCore/qstring.h> +#include <QtCore/qbuffer.h> +#include <QtCore/qpointer.h> + +#include <sys/types.h> + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +QT_MODULE(Gui) + +class QAuthDevice; +class QWSClient; +class QIODevice; +class QTransportAuthPrivate; +class QMutex; + +class Q_GUI_EXPORT QTransportAuth : public QObject +{ + Q_OBJECT +public: + static QTransportAuth *getInstance(); + + enum Result { + // Error codes + Pending = 0x00, + TooSmall = 0x01, + CacheMiss = 0x02, + NoMagic = 0x03, + NoSuchKey = 0x04, + FailMatch = 0x05, + OutOfDate = 0x06, + // reserved for expansion + Success = 0x1e, + ErrMask = 0x1f, + + // Verification codes + Allow = 0x20, + Deny = 0x40, + Ask = 0x60, + // reserved + StatusMask = 0xe0 + }; + + enum Properties { + Trusted = 0x01, + Connection = 0x02, + UnixStreamSock = 0x04, + SharedMemory = 0x08, + MessageQueue = 0x10, + UDP = 0x20, + TCP = 0x40, + UserDefined = 0x80, + TransportType = 0xfc + }; + + struct Data + { + Data() { processId = -1; } + Data( unsigned char p, int d ) + : properties( p ) + , descriptor( d ) + , processId( -1 ) + { + if (( properties & TransportType ) == TCP || + ( properties & TransportType ) == UnixStreamSock ) + properties |= Connection; + } + + unsigned char properties; + unsigned char progId; + unsigned char status; + unsigned int descriptor; // socket fd or shmget key + pid_t processId; + + bool trusted() const; + void setTrusted( bool ); + bool connection() const; + void setConnection( bool ); + }; + + static const char *errorString( const QTransportAuth::Data & ); + + QTransportAuth::Data *connectTransport( unsigned char, int ); + + QAuthDevice *authBuf( QTransportAuth::Data *, QIODevice * ); + QAuthDevice *recvBuf( QTransportAuth::Data *, QIODevice * ); + QIODevice *passThroughByClient( QWSClient * ) const; + + void setKeyFilePath( const QString & ); + QString keyFilePath() const; + const unsigned char *getClientKey( unsigned char progId ); + void invalidateClientKeyCache(); + QMutex *getKeyFileMutex(); + void setLogFilePath( const QString & ); + QString logFilePath() const; + void setPackageRegistry( QObject *registry ); + bool isDiscoveryMode() const; + void setProcessKey( const char * ); + void setProcessKey( const char *, const char * ); + void registerPolicyReceiver( QObject * ); + void unregisterPolicyReceiver( QObject * ); + + bool authToMessage( QTransportAuth::Data &d, char *hdr, const char *msg, int msgLen ); + bool authFromMessage( QTransportAuth::Data &d, const char *msg, int msgLen ); + + bool authorizeRequest( QTransportAuth::Data &d, const QString &request ); + +Q_SIGNALS: + void policyCheck( QTransportAuth::Data &, const QString & ); + void authViolation( QTransportAuth::Data & ); +private Q_SLOTS: + void bufferDestroyed( QObject * ); + +private: + // users should never construct their own + QTransportAuth(); + ~QTransportAuth(); + + friend class QAuthDevice; + Q_DECLARE_PRIVATE(QTransportAuth) +}; + +class Q_GUI_EXPORT RequestAnalyzer +{ +public: + RequestAnalyzer(); + virtual ~RequestAnalyzer(); + QString operator()( QByteArray *data ) { return analyze( data ); } + bool requireMoreData() const { return moreData; } + qint64 bytesAnalyzed() const { return dataSize; } +protected: + virtual QString analyze( QByteArray * ); + bool moreData; + qint64 dataSize; +}; + +/*! + \internal + \class QAuthDevice + + \brief Pass-through QIODevice sub-class for authentication. + + Use this class to forward on or receive forwarded data over a real + device for authentication. +*/ +class Q_GUI_EXPORT QAuthDevice : public QIODevice +{ + Q_OBJECT +public: + enum AuthDirection { + Receive, + Send + }; + QAuthDevice( QIODevice *, QTransportAuth::Data *, AuthDirection ); + ~QAuthDevice(); + void setTarget( QIODevice *t ) { m_target = t; } + QIODevice *target() const { return m_target; } + void setClient( QObject* ); + QObject *client() const; + void setRequestAnalyzer( RequestAnalyzer * ); + bool isSequential() const; + bool atEnd() const; + qint64 bytesAvailable() const; + qint64 bytesToWrite() const; + bool seek( qint64 ); + QByteArray & buffer(); + +protected: + qint64 readData( char *, qint64 ); + qint64 writeData(const char *, qint64 ); +private Q_SLOTS: + void recvReadyRead(); + void targetBytesWritten( qint64 ); +private: + bool authorizeMessage(); + + QTransportAuth::Data *d; + AuthDirection way; + QIODevice *m_target; + QObject *m_client; + QByteArray msgQueue; + qint64 m_bytesAvailable; + qint64 m_skipWritten; + + RequestAnalyzer *analyzer; +}; + +inline bool QAuthDevice::isSequential() const +{ + return true; +} + +inline bool QAuthDevice::seek( qint64 ) +{ + return false; +} + +inline bool QAuthDevice::atEnd() const +{ + return msgQueue.isEmpty(); +} + +inline qint64 QAuthDevice::bytesAvailable() const +{ + if ( way == Receive ) + return m_bytesAvailable; + else + return ( m_target ? m_target->bytesAvailable() : 0 ); +} + +inline qint64 QAuthDevice::bytesToWrite() const +{ + return msgQueue.size(); +} + +inline QByteArray &QAuthDevice::buffer() +{ + return msgQueue; +} + + + + +QT_END_NAMESPACE + +QT_END_HEADER + +#endif // QT_NO_SXE +#endif // QTRANSPORTAUTH_QWS_H diff --git a/src/gui/embedded/qtransportauth_qws_p.h b/src/gui/embedded/qtransportauth_qws_p.h new file mode 100644 index 0000000..33e2edc --- /dev/null +++ b/src/gui/embedded/qtransportauth_qws_p.h @@ -0,0 +1,189 @@ +/**************************************************************************** +** +** 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$ +** +****************************************************************************/ + +#ifndef QTRANSPORTAUTH_QWS_P_H +#define QTRANSPORTAUTH_QWS_P_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +#include <QtCore/qglobal.h> + +#ifndef QT_NO_SXE + +#include "qtransportauth_qws.h" +#include "qtransportauthdefs_qws.h" +#include "qbuffer.h" + +#include <qmutex.h> +#include <qdatetime.h> +#include "private/qobject_p.h" + +#include <QtCore/qcache.h> + +QT_BEGIN_NAMESPACE + +// Uncomment to generate debug output +// #define QTRANSPORTAUTH_DEBUG 1 + +#ifdef QTRANSPORTAUTH_DEBUG +void hexstring( char *buf, const unsigned char* key, size_t sz ); +#endif + +// proj id for ftok usage in sxe +#define SXE_PROJ 10022 + +/*! + \internal + memset for security purposes, guaranteed not to be optimized away + http://www.faqs.org/docs/Linux-HOWTO/Secure-Programs-HOWTO.html +*/ +void *guaranteed_memset(void *v,int c,size_t n); + +class QUnixSocketMessage; + +/*! + \internal + \class AuthCookie + Struct to carry process authentication key and id +*/ +#define QSXE_HEADER_LEN 24 + +/*! + \macro AUTH_ID + Macro to manage authentication header. Format of header is: + \table + \header \i BYTES \i CONTENT + \row \i 0-3 \i magic numbers + \row \i 4 \i length of authenticated data (max 255 bytes) + \row i\ 5 \i reserved + \row \i 6-21 \i MAC digest, or shared secret in case of simple auth + \row \i 22 \i program id + \row \i 23 \i sequence number + \endtable + Total length of the header is 24 bytes + + However this may change. Instead of coding these numbers use the AUTH_ID, + AUTH_KEY, AUTH_DATA and AUTH_SPACE macros. +*/ + +#define AUTH_ID(k) ((unsigned char)(k[QSXE_KEY_LEN])) +#define AUTH_KEY(k) ((unsigned char *)(k)) + +#define AUTH_DATA(x) (unsigned char *)((x) + QSXE_HEADER_LEN) +#define AUTH_SPACE(x) ((x) + QSXE_HEADER_LEN) +#define QSXE_LEN_IDX 4 +#define QSXE_KEY_IDX 6 +#define QSXE_PROG_IDX 22 +#define QSXE_SEQ_IDX 23 + +class SxeRegistryLocker : public QObject +{ + Q_OBJECT +public: + SxeRegistryLocker( QObject * ); + ~SxeRegistryLocker(); + bool success() const { return m_success; } +private: + bool m_success; + QObject *m_reg; +}; + +class QTransportAuthPrivate : public QObjectPrivate +{ + Q_DECLARE_PUBLIC(QTransportAuth) +public: + QTransportAuthPrivate(); + ~QTransportAuthPrivate(); + + const unsigned char *getClientKey( unsigned char progId ); + void invalidateClientKeyCache(); + + bool keyInitialised; + QString m_logFilePath; + QString m_keyFilePath; + QObject *m_packageRegistry; + AuthCookie authKey; + QCache<unsigned char, char> keyCache; + QHash< QObject*, QIODevice*> buffersByClient; + QMutex keyfileMutex; +}; + +/*! + \internal + Enforces the False Authentication Rate. If more than 4 authentications + are received per minute the sxemonitor is notified that the FAR has been exceeded +*/ +class FAREnforcer +{ + public: + static FAREnforcer *getInstance(); + void logAuthAttempt( QDateTime time = QDateTime::currentDateTime() ); + void reset(); + +#ifndef TEST_FAR_ENFORCER + private: +#endif + FAREnforcer(); + FAREnforcer( const FAREnforcer & ); + FAREnforcer &operator=(FAREnforcer const & ); + + static const QString FARMessage; + static const int minutelyRate; + static const QString SxeTag; + static const int minute; + + QList<QDateTime> authAttempts; +}; + +QT_END_NAMESPACE + +#endif // QT_NO_SXE +#endif // QTRANSPORTAUTH_QWS_P_H + diff --git a/src/gui/embedded/qtransportauthdefs_qws.h b/src/gui/embedded/qtransportauthdefs_qws.h new file mode 100644 index 0000000..85218b6 --- /dev/null +++ b/src/gui/embedded/qtransportauthdefs_qws.h @@ -0,0 +1,174 @@ +/**************************************************************************** +** +** 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$ +** +****************************************************************************/ + +#ifndef QTRANSPORTAUTHDEFS_QWS_H +#define QTRANSPORTAUTHDEFS_QWS_H + +#include <sys/types.h> +#include <string.h> + +#include <QtCore/qglobal.h> + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +QT_MODULE(Gui) + +#define QSXE_KEY_LEN 16 +#define QSXE_MAGIC_BYTES 4 + +// Number of bytes of each message to authenticate. Just need to ensure +// that the command at the beginning hasn't been tampered with. This value +// does not matter for trusted transports. +#define AMOUNT_TO_AUTHENTICATE 200 + +#define AUTH_ID(k) ((unsigned char)(k[QSXE_KEY_LEN])) +#define AUTH_KEY(k) ((unsigned char *)(k)) + +// must be a largish -ve number under any endianess when cast as an int +const unsigned char magic[QSXE_MAGIC_BYTES] = { 0xBA, 0xD4, 0xD4, 0xBA }; +const int magicInt = 0xBAD4D4BA; + +#define QSXE_KEYFILE "keyfile" + +/* + Header in above format, less the magic bytes. + Useful for reading off the socket +*/ +struct AuthHeader +{ + unsigned char len; + unsigned char pad; + unsigned char digest[QSXE_KEY_LEN]; + unsigned char id; + unsigned char seq; +}; + +/* + Header in a form suitable for authentication routines +*/ +struct AuthMessage +{ + AuthMessage() + { + ::memset( authData, 0, sizeof(authData) ); + ::memcpy( pad_magic, magic, QSXE_MAGIC_BYTES ); + } + unsigned char pad_magic[QSXE_MAGIC_BYTES]; + union { + AuthHeader hdr; + char authData[sizeof(AuthHeader)]; + }; + char payLoad[AMOUNT_TO_AUTHENTICATE]; +}; + +/** + Auth data as stored in _key +*/ +struct AuthCookie +{ + unsigned char key[QSXE_KEY_LEN]; + unsigned char pad; + unsigned char progId; +}; + +/* + Auth data as written to the key file - SUPERSEDED by usr_key_entry + + This is still used internally for some functions, ie the socket + related calls. +*/ +struct AuthRecord +{ + union { + AuthCookie auth; + char data[sizeof(struct AuthCookie)]; + }; + time_t change_time; +}; + +/*! + \class usr_key_entry + This comes from the SXE kernel patch file include/linux/lidsif.h + + This is the (new) data record for the key file (version 2). + + The key file is (now) either /proc/lids/keys (and the per-process + keys in /proc/<pid>/lids_key) OR for desktop/development ONLY (not + for production) it is $QPEDIR/etc/keyfile + + The key file maps keys to files. + + File are identified by inode and device numbers, not paths. + + (See the "installs" file for path to inode/device mapping) +*/ +struct usr_key_entry +{ + char key[QSXE_KEY_LEN]; + ino_t ino; + dev_t dev; +}; + + +/*! + \class IdBlock + \brief Data record for the manifest file. + The manifest file maps program id's to files +*/ +struct IdBlock +{ + quint64 inode; + quint64 device; + unsigned char pad; + unsigned char progId; + unsigned short installId; + unsigned int keyOffset; + qint64 install_time; +}; + +QT_END_NAMESPACE + +QT_END_HEADER + +#endif // QTRANSPORTAUTHDEFS_QWS_H + diff --git a/src/gui/embedded/qunixsocket.cpp b/src/gui/embedded/qunixsocket.cpp new file mode 100644 index 0000000..16f2cae --- /dev/null +++ b/src/gui/embedded/qunixsocket.cpp @@ -0,0 +1,1794 @@ +/**************************************************************************** +** +** 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 "qunixsocket_p.h" + +// #define QUNIXSOCKET_DEBUG 1 + +#include <QtCore/qsocketnotifier.h> +#include <QtCore/qqueue.h> +#include <QtCore/qdatetime.h> + +#ifdef QUNIXSOCKET_DEBUG +#include <QtCore/qdebug.h> +#endif + +extern "C" { +#include <unistd.h> +#include <string.h> +#include <errno.h> +#include <sys/socket.h> +#include <sys/un.h> +}; + +#define UNIX_PATH_MAX 108 // From unix(7) + +#ifdef QT_LINUXBASE +// LSB doesn't declare ucred +struct ucred +{ + pid_t pid; /* PID of sending process. */ + uid_t uid; /* UID of sending process. */ + gid_t gid; /* GID of sending process. */ +}; + +// LSB doesn't define the ones below +#ifndef SO_PASSCRED +# define SO_PASSCRED 16 +#endif +#ifndef SCM_CREDENTIALS +# define SCM_CREDENTIALS 0x02 +#endif +#ifndef MSG_DONTWAIT +# define MSG_DONTWAIT 0x40 +#endif +#ifndef MSG_NOSIGNAL +# define MSG_NOSIGNAL 0x4000 +#endif + +#endif // QT_LINUXBASE + +QT_BEGIN_NAMESPACE + +/////////////////////////////////////////////////////////////////////////////// +// class QUnixSocketRights +/////////////////////////////////////////////////////////////////////////////// +/*! + \class QUnixSocketRights + \internal + + \brief The QUnixSocketRights class encapsulates QUnixSocket rights data. + \omit + \ingroup Platform::DeviceSpecific + \ingroup Platform::OS + \ingroup Platform::Communications + \endomit + \ingroup qws + + \l QUnixSocket allows you to transfer Unix file descriptors between processes. + A file descriptor is referred to as "rights data" as it allows one process to + transfer its right to access a resource to another. + + The Unix system verifies resource permissions only when the resource is first + opened. For example, consider a file on disk readable only by the user "qt". + A process running as user "qt" will be able to open this file for reading. + If, while the process was still reading from the file, the ownership was + changed from user "qt" to user "root", the process would be allowed to + continue reading from the file, even though attempting to reopen the file + would be denied. Permissions are associated with special descriptors called + file descriptors which are returned to a process after it initially opens a + resource. + + File descriptors can be duplicated within a process through the dup(2) system + call. File descriptors can be passed between processes using the + \l QUnixSocket class in the same way. Even though the receiving process never + opened the resource directly, it has the same permissions to access it as the + process that did. + + \sa QUnixSocket + */ +struct QUnixSocketRightsPrivate : public QSharedData +{ + virtual ~QUnixSocketRightsPrivate() { +#ifdef QUNIXSOCKET_DEBUG + int closerv = +#endif + ::close(fd); +#ifdef QUNIXSOCKET_DEBUG + if(0 != closerv) { + qDebug() << "QUnixSocketRightsPrivate: Unable to close managed" + " file descriptor (" << ::strerror(errno) << ")"; + } +#endif + } + + int fd; +}; + +/*! + Create a new QUnixSocketRights instance containing the file descriptor \a fd. + \a fd will be dup(2)'d internally, so the application is free to close \a fd + following this call. + + If the dup(2) fails, or you pass an invalid \a fd, an + \l {QUnixSocketRights::isValid()}{invalid } object will be + constructed. + + QUnixSocketRights instances are immutable and the internal file descriptor + will be shared between any copies made of this object. The system will + close(2) the file descriptor once it is no longer needed. + */ +QUnixSocketRights::QUnixSocketRights(int fd) +{ + d = new QUnixSocketRightsPrivate(); + if(-1 == fd) { + d->fd = -1; + } else { + d->fd = ::dup(fd); +#ifdef QUNIXSOCKET_DEBUG + if(-1 == d->fd) { + qDebug() << "QUnixSocketRights: Unable to duplicate fd " + << fd << " (" << ::strerror(errno) << ")"; + } +#endif + } +} + +/*! + \internal + + Construct a QUnixSocketRights instance on \a fd without dup(2)'ing the file + descriptor. + */ +QUnixSocketRights::QUnixSocketRights(int fd,int) +{ + Q_ASSERT(-1 != fd); + d = new QUnixSocketRightsPrivate(); + d->fd = fd; +} + +/*! + Destroys the QUnixSocketRights instance. + */ +QUnixSocketRights::~QUnixSocketRights() +{ +} + +/*! + Create a copy of \a other. + */ +QUnixSocketRights & +QUnixSocketRights::operator=(const QUnixSocketRights & other) +{ + d = other.d; + return *this; +} + +/*! + Create a copy of \a other. + */ +QUnixSocketRights::QUnixSocketRights(const QUnixSocketRights & other) +: d(other.d) +{ +} + +/*! + Returns true if this QUnixSocketRights instance is managing a valid file + descriptor. This method is equivalent to (-1 != peekFd()). + + \sa QUnixSocketRights::peekFd() + */ +bool QUnixSocketRights::isValid() const +{ + return d->fd != -1; +} + +/*! + Return a duplicate of the file descriptor contained in this object. If this + is an \l {QUnixSocketRights::isValid()}{invalid } object, or the + dup(2) call fails, an invalid file descriptor (-1) will be returned. + + \sa QUnixSocketRights::peekFd() + */ +int QUnixSocketRights::dupFd() const +{ + if(-1 == d->fd) return -1; + + int rv = ::dup(d->fd); + +#ifdef QUNIXSOCKET_DEBUG + if(-1 == rv) + qDebug() << "QUnixSocketRights: Unable to duplicate managed file " + "descriptor (" << ::strerror(errno) << ")"; +#endif + + return rv; +} + +/*! + Returns the file descriptor contained in this object. If this + is an \l {QUnixSocketRights::isValid()}{invalid } object an invalid + file descriptor (-1) will be returned. + + The lifetime of this file descriptor is tied to the lifetime of the + QUnixSocketRights instance. The file descriptor returned by this method + \e may be close(2)'d when the QUnixSocketRights instance is destroyed. If + you want to continue to use the file descriptor use + \l QUnixSocketRights::dupFd() instead. + + \sa QUnixSocketRights::dupFd() + */ +int QUnixSocketRights::peekFd() const +{ + return d->fd; +} + +/////////////////////////////////////////////////////////////////////////////// +// class QUnixSocketMessage +/////////////////////////////////////////////////////////////////////////////// +struct QUnixSocketMessagePrivate : public QSharedData +{ + QUnixSocketMessagePrivate() + : state(Default), vec(0), iovecLen(0), dataSize(0) {} + QUnixSocketMessagePrivate(const QByteArray & b) + : bytes(b), state(Default), vec(0), iovecLen(0), dataSize(0) {} + QUnixSocketMessagePrivate(const QByteArray & b, + const QList<QUnixSocketRights> & r) + : bytes(b), rights(r), state(Default), vec(0), iovecLen(0), dataSize(0) {} + + int size() const { return vec ? dataSize : bytes.size(); } + void removeBytes( unsigned int ); + + QByteArray bytes; + QList<QUnixSocketRights> rights; + + enum AncillaryDataState { + Default = 0x00, + Truncated = 0x01, + Credential = 0x02 + }; + AncillaryDataState state; + + pid_t pid; + gid_t gid; + uid_t uid; + + ::iovec *vec; + int iovecLen; // number of vectors in array + int dataSize; // total size of vectors = payload +}; + +/*! + \internal + Remove \a bytesToDequeue bytes from the front of this message +*/ +void QUnixSocketMessagePrivate::removeBytes( unsigned int bytesToDequeue ) +{ + if ( vec ) + { + ::iovec *vecPtr = vec; + if ( bytesToDequeue > (unsigned int)dataSize ) bytesToDequeue = dataSize; + while ( bytesToDequeue > 0 && iovecLen > 0 ) + { + if ( vecPtr->iov_len > bytesToDequeue ) + { + // dequeue the bytes by taking them off the front of the + // current vector. since we don't own the iovec, its okay + // to "leak" this away by pointing past it + char **base = reinterpret_cast<char**>(&(vecPtr->iov_base)); + *base += bytesToDequeue; + vecPtr->iov_len -= bytesToDequeue; + bytesToDequeue = 0; + } + else + { + // dequeue bytes by skipping a whole vector. again, its ok + // to lose the pointers to this data + bytesToDequeue -= vecPtr->iov_len; + iovecLen--; + vecPtr++; + } + } + dataSize -= bytesToDequeue; + if ( iovecLen == 0 ) vec = 0; + } + else + { + bytes.remove(0, bytesToDequeue ); + } +} + + +/*! + \class QUnixSocketMessage + \internal + + \brief The QUnixSocketMessage class encapsulates a message sent or received + through the QUnixSocket class. + \omit + \ingroup Platform::DeviceSpecific + \ingroup Platform::OS + \ingroup Platform::Communications + \endomit + \ingroup qws + + In addition to transmitting regular byte stream data, messages sent over Unix + domain sockets may have special ancillary properties. QUnixSocketMessage + instances allow programmers to retrieve and control these properties. + + Every QUnixSocketMessage sent has an associated set of credentials. A + message's credentials consist of the process id, the user id and the group id + of the sending process. Normally these credentials are set automatically for + you by the QUnixSocketMessage class and can be queried by the receiving + process using the \l QUnixSocketMessage::processId(), + \l QUnixSocketMessage::userId() and \l QUnixSocketMessage::groupId() methods + respectively. + + Advanced applications may wish to change the credentials that their message + is sent with, and may do so though the \l QUnixSocketMessage::setProcessId(), + \l QUnixSocketMessage::setUserId() and \l QUnixSocketMessage::setGroupId() + methods. The validity of these credentials is verified by the system kernel. + Only the root user can send messages with credentials that are not his own. + Sending of the message will fail for any non-root user who attempts to + fabricate credentials. Note that this failure is enforced by the system + kernel - receivers can trust the accuracy of credential data! + + Unix domain socket messages may also be used to transmit Unix file descriptors + between processes. In this context, file descriptors are known as rights data + and are encapsulated by the \l QUnixSocketRights class. Senders can set the + file descriptors to transmit using the \l QUnixSocketMessage::setRights() and + receivers can retrieve this data through a call to + \l QUnixSocketMessage::rights(). \l QUnixSocket and \l QUnixSocketRights + discuss the specific copy and ordering semantic associated with rights data. + + QUnixSocketMessage messages are sent by the \l QUnixSocket::write() method. + Like any normal network message, attempting to transmit an empty + QUnixSocketMessage will succeed, but result in a no-op. Limitations in the + Unix domain protocol semantic will cause a transmission of a + QUnixSocketMessage with rights data, but no byte data portion, to fail. + + \sa QUnixSocket QUnixSocketRights + */ + +/*! + Construct an empty QUnixSocketMessage. This instance will have not data and + no rights information. The message's credentials will be set to the + application's default credentials. + */ +QUnixSocketMessage::QUnixSocketMessage() +: d(new QUnixSocketMessagePrivate()) +{ +} + +/*! + Construct a QUnixSocketMessage with an initial data payload of \a bytes. The + message's credentials will be set to the application's default credentials. + */ +QUnixSocketMessage::QUnixSocketMessage(const QByteArray & bytes) +: d(new QUnixSocketMessagePrivate(bytes)) +{ +} + +/*! + Construct a QUnixSocketMessage with an initial data payload of \a bytes and + an initial rights payload of \a rights. The message's credentials will be set + to the application's default credentials. + + A message with rights data but an empty data payload cannot be transmitted + by the system. + */ +QUnixSocketMessage::QUnixSocketMessage(const QByteArray & bytes, + const QList<QUnixSocketRights> & rights) +: d(new QUnixSocketMessagePrivate(bytes, rights)) +{ +} + +/*! + Create a copy of \a other. + */ +QUnixSocketMessage::QUnixSocketMessage(const QUnixSocketMessage & other) +: d(other.d) +{ +} + +/*! + \fn QUnixSocketMessage::QUnixSocketMessage(const iovec* data, int vecLen) + + Construct a QUnixSocketMessage with an initial data payload of \a + data which points to an array of \a vecLen iovec structures. The + message's credentials will be set to the application's default + credentials. + + This method can be used to avoid the overhead of copying buffers of data + and will directly send the data pointed to by \a data on the socket. It also + avoids the syscall overhead of making a number of small socket write calls, + if a number of data items can be delivered with one write. + + Caller must ensure the iovec * \a data remains valid until the message + is flushed. Caller retains ownership of the iovec structs. + */ +QUnixSocketMessage::QUnixSocketMessage(const ::iovec* data, int vecLen ) +: d(new QUnixSocketMessagePrivate()) +{ + for ( int v = 0; v < vecLen; v++ ) + d->dataSize += data[v].iov_len; + d->vec = const_cast<iovec*>(data); + d->iovecLen = vecLen; +} + +/*! + Assign the contents of \a other to this object. + */ +QUnixSocketMessage & QUnixSocketMessage::operator=(const QUnixSocketMessage & other) +{ + d = other.d; + return *this; +} + +/*! + Destroy this instance. + */ +QUnixSocketMessage::~QUnixSocketMessage() +{ +} + +/*! + Set the data portion of the message to \a bytes. + + \sa QUnixSocketMessage::bytes() + */ +void QUnixSocketMessage::setBytes(const QByteArray & bytes) +{ + d.detach(); + d->bytes = bytes; +} + +/*! + Set the rights portion of the message to \a rights. + + A message with rights data but an empty byte data payload cannot be + transmitted by the system. + + \sa QUnixSocketMessage::rights() + */ +void QUnixSocketMessage::setRights(const QList<QUnixSocketRights> & rights) +{ + d.detach(); + d->rights = rights; +} + +/*! + Return the rights portion of the message. + + \sa QUnixSocketMessage::setRights() + */ +const QList<QUnixSocketRights> & QUnixSocketMessage::rights() const +{ + return d->rights; +} + +/*! + Returns true if the rights portion of the message was truncated on reception + due to insufficient buffer size. The rights buffer size can be adjusted + through calls to the \l QUnixSocket::setRightsBufferSize() method. + \l QUnixSocket contains a discussion of the buffering and truncation + characteristics of the Unix domain protocol. + + \sa QUnixSocket QUnixSocket::setRightsBufferSize() + */ +bool QUnixSocketMessage::rightsWereTruncated() const +{ + return d->state & QUnixSocketMessagePrivate::Truncated; +} + +/*! + Return the data portion of the message. + + \sa QUnixSocketMessage::setBytes() + */ +const QByteArray & QUnixSocketMessage::bytes() const +{ + return d->bytes; +} + +/*! + Returns the process id credential associated with this message. + + \sa QUnixSocketMessage::setProcessId() + */ +pid_t QUnixSocketMessage::processId() const +{ + if(QUnixSocketMessagePrivate::Credential & d->state) + return d->pid; + else + return ::getpid(); +} + +/*! + Returns the user id credential associated with this message. + + \sa QUnixSocketMessage::setUserId() + */ +uid_t QUnixSocketMessage::userId() const +{ + if(QUnixSocketMessagePrivate::Credential & d->state) + return d->uid; + else + return ::geteuid(); +} + +/*! + Returns the group id credential associated with this message. + + \sa QUnixSocketMessage::setGroupId() + */ +gid_t QUnixSocketMessage::groupId() const +{ + if(QUnixSocketMessagePrivate::Credential & d->state) + return d->gid; + else + return ::getegid(); +} + +/*! + Set the process id credential associated with this message to \a pid. Unless + you are the root user, setting a fraudulant credential will cause this message + to fail. + + \sa QUnixSocketMessage::processId() + */ +void QUnixSocketMessage::setProcessId(pid_t pid) +{ + if(!(d->state & QUnixSocketMessagePrivate::Credential)) { + d->state = (QUnixSocketMessagePrivate::AncillaryDataState)( d->state | QUnixSocketMessagePrivate::Credential ); + d->uid = ::geteuid(); + d->gid = ::getegid(); + } + d->pid = pid; +} + +/*! + Set the user id credential associated with this message to \a uid. Unless + you are the root user, setting a fraudulant credential will cause this message + to fail. + + \sa QUnixSocketMessage::userId() + */ +void QUnixSocketMessage::setUserId(uid_t uid) +{ + if(!(d->state & QUnixSocketMessagePrivate::Credential)) { + d->state = (QUnixSocketMessagePrivate::AncillaryDataState)( d->state | QUnixSocketMessagePrivate::Credential ); + d->pid = ::getpid(); + d->gid = ::getegid(); + } + d->uid = uid; +} + +/*! + Set the group id credential associated with this message to \a gid. Unless + you are the root user, setting a fraudulant credential will cause this message + to fail. + + \sa QUnixSocketMessage::groupId() + */ +void QUnixSocketMessage::setGroupId(gid_t gid) +{ + if(!(d->state & QUnixSocketMessagePrivate::Credential)) { + d->state = (QUnixSocketMessagePrivate::AncillaryDataState)( d->state | QUnixSocketMessagePrivate::Credential ); + d->pid = ::getpid(); + d->uid = ::geteuid(); + } + d->gid = gid; +} + +/*! + Return true if this message is valid. A message with rights data but an empty + byte data payload cannot be transmitted by the system and is marked as + invalid. + */ +bool QUnixSocketMessage::isValid() const +{ + return d->rights.isEmpty() || !d->bytes.isEmpty(); +} + +/////////////////////////////////////////////////////////////////////////////// +// class QUnixSocket +/////////////////////////////////////////////////////////////////////////////// +#define QUNIXSOCKET_DEFAULT_READBUFFER 1024 +#define QUNIXSOCKET_DEFAULT_ANCILLARYBUFFER 0 + +/*! + \class QUnixSocket + \internal + + \brief The QUnixSocket class provides a Unix domain socket. + + \omit + \ingroup Platform::DeviceSpecific + \ingroup Platform::OS + \ingroup Platform::Communications + \endomit + \ingroup qws + + Unix domain sockets provide an efficient mechanism for communications between + Unix processes on the same machine. Unix domain sockets support a reliable, + stream-oriented, connection-oriented transport protocol, much like TCP + sockets. Unlike IP based sockets, the connection endpoint of a Unix domain + socket is a file on disk of type socket. + + In addition to transporting raw data bytes, Unix domain sockets are able to + transmit special ancillary data. The two types of ancillary data supported + by the QUnixSocket class are: + + \list + \o Credential Data - Allows a receiver + to reliably identify the process sending each message. + \o \l {QUnixSocketRights}{Rights Data } - Allows Unix file descriptors + to be transmitted between processes. + \endlist + + Because of the need to support ancillary data, QUnixSocket is not a QIODevice, + like QTcpSocket and QUdpSocket. Instead, QUnixSocket contains a number of + read and write methods that clients must invoke directly. Rather than + returning raw data bytes, \l QUnixSocket::read() returns \l QUnixSocketMessage + instances that encapsulate the message's byte data and any other ancillary + data. + + Ancillary data is transmitted "out of band". Every \l QUnixSocketMessage + received will have credential data associated with it that the client can + access through calls to \l QUnixSocketMessage::processId(), + \l QUnixSocketMessage::groupId() and \l QUnixSocketMessage::userId(). + Likewise, message creators can set the credential data to send through calls + to \l QUnixSocketMessage::setProcessId(), \l QUnixSocketMessage::setGroupId() + and \l QUnixSocketMessage::setUserId() respectively. The authenticity of the + credential values is verified by the system kernel and cannot be fabricated + by unprivileged processes. Only processes running as the root user can + specify credential data that does not match the sending process. + + Unix file descriptors, known as "rights data", transmitted between processes + appear as though they had been dup(2)'d between the two. As Unix + domain sockets present a continuous stream of bytes to the receiver, the + rights data - which is transmitted out of band - must be "slotted" in at some + point. The rights data is logically associated with the first byte - called + the anchor byte - of the \l QUnixSocketMessage to which they are attached. + Received rights data will be available from the + \l QUnixSocketMessage::rights() method for the \l QUnixSocketMessage + instance that contains the anchor byte. + + In addition to a \l QUnixSocket::write() that takes a \l QUnixSocketMessage + instance - allowing a client to transmit both byte and rights data - a + number of convenience overloads are provided for use when only transmitting + simple byte data. Unix requires that at least one byte of raw data be + transmitted in order to send rights data. A \l QUnixSocketMessage instance + with rights data, but no byte data, cannot be transmitted. + + Unix sockets present a stream interface, such that, for example, a single + six byte transmission might be received as two three byte messages. Rights + data, on the other hand, is conceptually transmitted as unfragmentable + datagrams. If the receiving buffer is not large enough to contain all the + transmitted rights information, the data is truncated and irretreivably lost. + Users should use the \l QUnixSocket::setRightsBufferSize() method to control + the buffer size used for this data, and develop protocols that avoid the + problem. If the buffer size is too small and rights data is truncated, + the \l QUnixSocketMessage::rightsWereTruncated() flag will be set. + + \sa QUnixSocketMessage QUnixSocketRights +*/ + +/*! + \enum QUnixSocket::SocketError + + The SocketError enumeration represents the various errors that can occur on + a Unix domain socket. The most recent error for the socket is available + through the \l QUnixSocket::error() method. + + \value NoError No error has occurred. + \value InvalidPath An invalid path endpoint was passed to + \l QUnixSocket::connect(). As defined by unix(7), invalid paths + include an empty path, or what more than 107 characters long. + \value ResourceError An error acquiring or manipulating the system's socket + resources occurred. For example, if the process runs out of available + socket descriptors, a ResourceError will occur. + \value NonexistentPath The endpoing passed to \l QUnixSocket::connect() does + not refer to a Unix domain socket entity on disk. + \value ConnectionRefused The connection to the specified endpoint was refused. + Generally this means that there is no server listening on that + endpoint. + \value UnknownError An unknown error has occurred. + \value ReadFailure An error occurred while reading bytes from the connection. + \value WriteFailure An error occurred while writing bytes into the connection. + */ + +/*! + \enum QUnixSocket::SocketState + + The SocketState enumeration represents the connection state of a QUnixSocket + instance. + + \value UnconnectedState The connection is not established. + \value ConnectedState The connection is established. + \value ClosingState The connection is being closed, following a call to + \l QUnixSocket::close(). While closing, any pending data will be + transmitted, but further writes by the application will be refused. + */ + +/* + \fn QUnixSocket::bytesWritten(qint64 bytes) + + This signal is emitted every time a payload of data has been written to the + connection. The \a bytes argument is set to the number of bytes that were + written in this payload. + + \sa QUnixSocket::readyRead() +*/ + +/* + \fn QUnixSocket::readyRead() + + This signal is emitted once every time new data is available for reading from + the connection. It will only be emitted again once new data is available. + + \sa QUnixSocket::bytesWritten() +*/ + +/*! + \fn QUnixSocket::stateChanged(SocketState socketState) + + This signal is emitted each time the socket changes connection state. + \a socketState will be set to the socket's new state. +*/ + +class QUnixSocketPrivate : public QObject { +Q_OBJECT +public: + QUnixSocketPrivate(QUnixSocket * _me) + : me(_me), fd(-1), readNotifier(0), writeNotifier(0), + state(QUnixSocket::UnconnectedState), error(QUnixSocket::NoError), + writeQueueBytes(0), messageValid(false), dataBuffer(0), + dataBufferLength(0), dataBufferCapacity(0), ancillaryBuffer(0), + ancillaryBufferCount(0), closingTimer(0) { + QObject::connect(this, SIGNAL(readyRead()), me, SIGNAL(readyRead())); + QObject::connect(this, SIGNAL(bytesWritten(qint64)), + me, SIGNAL(bytesWritten(qint64))); + } + ~QUnixSocketPrivate() + { + if(dataBuffer) + delete [] dataBuffer; + if(ancillaryBuffer) + delete [] ancillaryBuffer; + } + + enum { CausedAbort = 0x70000000 }; + + QUnixSocket * me; + + int fd; + + QSocketNotifier * readNotifier; + QSocketNotifier * writeNotifier; + + QUnixSocket::SocketState state; + QUnixSocket::SocketError error; + + QQueue<QUnixSocketMessage> writeQueue; + unsigned int writeQueueBytes; + + bool messageValid; + ::msghdr message; + inline void flushAncillary() + { + if(!messageValid) return; + ::cmsghdr * h = (::cmsghdr *)CMSG_FIRSTHDR(&(message)); + while(h) { + + if(SCM_RIGHTS == h->cmsg_type) { + int * fds = (int *)CMSG_DATA(h); + int numFds = (h->cmsg_len - CMSG_LEN(0)) / sizeof(int); + + for(int ii = 0; ii < numFds; ++ii) + ::close(fds[ii]); + } + + h = (::cmsghdr *)CMSG_NXTHDR(&(message), h); + } + + messageValid = false; + } + + + char * dataBuffer; + unsigned int dataBufferLength; + unsigned int dataBufferCapacity; + + char * ancillaryBuffer; + inline unsigned int ancillaryBufferCapacity() + { + return CMSG_SPACE(sizeof(::ucred)) + CMSG_SPACE(sizeof(int) * ancillaryBufferCount); + } + unsigned int ancillaryBufferCount; + + QByteArray address; + + int closingTimer; + + virtual void timerEvent(QTimerEvent *) + { + me->abort(); + killTimer(closingTimer); + closingTimer = 0; + } +signals: + void readyRead(); + void bytesWritten(qint64); + +public slots: + void readActivated(); + qint64 writeActivated(); +}; + +/*! + Construct a QUnixSocket instance, with \a parent. + + The read buffer is initially set to 1024 bytes, and the rights buffer to 0 + entries. + + \sa QUnixSocket::readBufferSize() QUnixSocket::rightsBufferSize() + */ +QUnixSocket::QUnixSocket(QObject * parent) +: QIODevice(parent), d(new QUnixSocketPrivate(this)) +{ + setOpenMode(QIODevice::NotOpen); + setReadBufferSize(QUNIXSOCKET_DEFAULT_READBUFFER); + setRightsBufferSize(QUNIXSOCKET_DEFAULT_ANCILLARYBUFFER); +} + +/*! + Construct a QUnixSocket instance, with \a parent. + + The read buffer is initially set to \a readBufferSize bytes, and the rights + buffer to \a rightsBufferSize entries. + + \sa QUnixSocket::readBufferSize() QUnixSocket::rightsBufferSize() + */ +QUnixSocket::QUnixSocket(qint64 readBufferSize, qint64 rightsBufferSize, + QObject * parent) +: QIODevice(parent), d(new QUnixSocketPrivate(this)) +{ + Q_ASSERT(readBufferSize > 0 && rightsBufferSize >= 0); + + setOpenMode(QIODevice::NotOpen); + setReadBufferSize(readBufferSize); + setRightsBufferSize(rightsBufferSize); +} + +/*! + Destroys the QUnixSocket instance. Any unsent data is discarded. + */ +QUnixSocket::~QUnixSocket() +{ + abort(); + delete d; +} + +/*! + Attempt to connect to \a path. + + This method is synchronous and will return true if the connection succeeds and + false otherwise. In the case of failure, \l QUnixSocket::error() will be set + accordingly. + + Any existing connection will be aborted, and all pending data will be + discarded. + + \sa QUnixSocket::close() QUnixSocket::abort() QUnixSocket::error() + */ +bool QUnixSocket::connect(const QByteArray & path) +{ + int _true; + int crv; +#ifdef QUNIXSOCKET_DEBUG + qDebug() << "QUnixSocket: Connect requested to '" + << path << "'"; +#endif + + abort(); // Reset any existing connection + + if(UnconnectedState != d->state) // abort() caused a signal and someone messed + // with us. We'll assume they know what + // they're doing and bail. Alternative is to + // have a special "Connecting" state + return false; + + + if(path.isEmpty() || path.size() > UNIX_PATH_MAX) { + d->error = InvalidPath; + return false; + } + + // Create the socket + d->fd = ::socket(PF_UNIX, SOCK_STREAM, 0); + if(-1 == d->fd) { +#ifdef QUNIXSOCKET_DEBUG + qDebug() << "QUnixSocket: Unable to create socket (" + << strerror(errno) << ")"; +#endif + d->error = ResourceError; + goto connect_error; + } + + // Set socket options + _true = 1; + crv = ::setsockopt(d->fd, SOL_SOCKET, SO_PASSCRED, (void *)&_true, + sizeof(int)); + if(-1 == crv) { +#ifdef QUNIXSOCKET_DEBUG + qDebug() << "QUnixSocket: Unable to configure socket (" + << ::strerror(errno) << ")"; +#endif + d->error = ResourceError; + + goto connect_error; + } + + // Construct our unix address + struct ::sockaddr_un addr; + addr.sun_family = AF_UNIX; + ::memcpy(addr.sun_path, path.data(), path.size()); + if(path.size() < UNIX_PATH_MAX) + addr.sun_path[path.size()] = '\0'; + + // Attempt the connect + crv = ::connect(d->fd, (sockaddr *)&addr, sizeof(sockaddr_un)); + if(-1 == crv) { +#ifdef QUNIXSOCKET_DEBUG + qDebug() << "QUnixSocket: Unable to connect (" + << ::strerror(errno) << ")"; +#endif + if(ECONNREFUSED == errno) + d->error = ConnectionRefused; + else if(ENOENT == errno) + d->error = NonexistentPath; + else + d->error = UnknownError; + + goto connect_error; + } + + // We're connected! + d->address = path; + d->state = ConnectedState; + d->readNotifier = new QSocketNotifier(d->fd, QSocketNotifier::Read, d); + d->writeNotifier = new QSocketNotifier(d->fd, QSocketNotifier::Write, d); + QObject::connect(d->readNotifier, SIGNAL(activated(int)), + d, SLOT(readActivated())); + QObject::connect(d->writeNotifier, SIGNAL(activated(int)), + d, SLOT(writeActivated())); + d->readNotifier->setEnabled(true); + d->writeNotifier->setEnabled(false); + setOpenMode(QIODevice::ReadWrite); + emit stateChanged(ConnectedState); + +#ifdef QUNIXSOCKET_DEBUG + qDebug() << "QUnixSocket: Connected to " << path; +#endif + return true; + +connect_error: // Cleanup failed connection + if(-1 != d->fd) { +#ifdef QUNIXSOCKET_DEBUG + int closerv = +#endif + ::close(d->fd); +#ifdef QUNIXSOCKET_DEBUG + if(0 != closerv) { + qDebug() << "QUnixSocket: Unable to close file descriptor after " + "failed connect (" << ::strerror(errno) << ")"; + } +#endif + } + d->fd = -1; + return false; +} + +/*! + Sets the socket descriptor to use to \a socketDescriptor, bypassing + QUnixSocket's connection infrastructure, and return true on success and false + on failure. \a socketDescriptor must be in the connected state, and must be + a Unix domain socket descriptor. Following a successful call to this method, + the QUnixSocket instance will be in the Connected state and will have assumed + ownership of \a socketDescriptor. + + Any existing connection will be aborted, and all pending data will be + discarded. + + \sa QUnixSocket::connect() +*/ +bool QUnixSocket::setSocketDescriptor(int socketDescriptor) +{ + abort(); + + if(UnconnectedState != state()) // See QUnixSocket::connect() + return false; + + // Attempt to set the socket options + if(-1 == socketDescriptor) { +#ifdef QUNIXSOCKET_DEBUG + qDebug() << "QUnixSocket: User provided socket is invalid"; +#endif + d->error = ResourceError; + return false; + } + + // Set socket options + int _true = 1; + int crv = ::setsockopt(socketDescriptor, SOL_SOCKET, + SO_PASSCRED, (void *)&_true, sizeof(int)); + if(-1 == crv) { +#ifdef QUNIXSOCKET_DEBUG + qDebug() << "QUnixSocket: Unable to configure client provided socket (" + << ::strerror(errno) << ")"; +#endif + d->error = ResourceError; + + return false; + } + + d->fd = socketDescriptor; + d->state = ConnectedState; + d->address = QByteArray(); + setOpenMode(QIODevice::ReadWrite); + d->readNotifier = new QSocketNotifier(d->fd, QSocketNotifier::Read, d); + d->writeNotifier = new QSocketNotifier(d->fd, QSocketNotifier::Write, d); + QObject::connect(d->readNotifier, SIGNAL(activated(int)), + d, SLOT(readActivated())); + QObject::connect(d->writeNotifier, SIGNAL(activated(int)), + d, SLOT(writeActivated())); + d->readNotifier->setEnabled(true); + d->writeNotifier->setEnabled(false); + emit stateChanged(d->state); + + return true; +} + +/*! + Returns the socket descriptor currently in use. This method will return -1 + if the QUnixSocket instance is in the UnconnectedState \l {QUnixSocket::state()}{state. } + + \sa QUnixSocket::setSocketDescriptor() + */ +int QUnixSocket::socketDescriptor() const +{ + return d->fd; +} + +/*! + Abort the connection. This will immediately disconnect (if connected) and + discard any pending data. Following a call to QUnixSocket::abort() the + object will always be in the disconnected \link QUnixSocket::state() state. + \endlink + + \sa QUnixSocket::close() +*/ +void QUnixSocket::abort() +{ + setOpenMode(QIODevice::NotOpen); + + // We want to be able to use QUnixSocket::abort() to cleanup our state but + // also preserve the error message that caused the abort. It is not + // possible to reorder code to do this: + // abort(); + // d->error = SomeError + // as QUnixSocket::abort() might emit a signal and we need the error to be + // set within that signal. So, if we want an error message to be preserved + // across a *single* call to abort(), we set the + // QUnixSocketPrivate::CausedAbort flag in the error. + if(d->error & QUnixSocketPrivate::CausedAbort) + d->error = (QUnixSocket::SocketError)(d->error & + ~QUnixSocketPrivate::CausedAbort); + else + d->error = NoError; + + if( UnconnectedState == d->state) return; + +#ifdef QUNIXSOCKET_DEBUG + int closerv = +#endif + ::close(d->fd); +#ifdef QUNIXSOCKET_DEBUG + if(0 != closerv) { + qDebug() << "QUnixSocket: Unable to close socket during abort (" + << strerror(errno) << ")"; + } +#endif + + // Reset variables + d->fd = -1; + d->state = UnconnectedState; + d->dataBufferLength = 0; + d->flushAncillary(); + d->address = QByteArray(); + if(d->readNotifier) { + d->readNotifier->setEnabled(false); + d->readNotifier->deleteLater(); + } + if(d->writeNotifier) { + d->writeNotifier->setEnabled(false); + d->writeNotifier->deleteLater(); + } + d->readNotifier = 0; + d->writeNotifier = 0; + d->writeQueue.clear(); + d->writeQueueBytes = 0; + if(d->closingTimer) { + d->killTimer(d->closingTimer); + } + d->closingTimer = 0; + emit stateChanged(d->state); +} + +/*! + Close the connection. The instance will enter the Closing + \l {QUnixSocket::state()}{state } until all pending data has been + transmitted, at which point it will enter the Unconnected state. + + Even if there is no pending data for transmission, the object will never + jump directly to Disconnect without first passing through the + Closing state. + + \sa QUnixSocket::abort() + */ +void QUnixSocket::close() +{ + if(ConnectedState != state()) return; + + d->state = ClosingState; + if(d->writeQueue.isEmpty()) { + d->closingTimer = d->startTimer(0); // Start a timer to "fake" + // completing writes + } + emit stateChanged(d->state); +} + +/*! + This function writes as much as possible from the internal write buffer to + the underlying socket, without blocking. If any data was written, this + function returns true; otherwise false is returned. +*/ +// Note! docs partially copied from QAbstractSocket::flush() +bool QUnixSocket::flush() +{ + // This needs to have the same semantics as QAbstractSocket, if it is to + // be used interchangeably with that class. + if (d->writeQueue.isEmpty()) + return false; + + d->writeActivated(); + return true; +} + +/*! + Returns the last error to have occurred on this object. This method is not + destructive, so multiple calls to QUnixSocket::error() will return the same + value. The error is only reset by a call to \l QUnixSocket::connect() or + \l QUnixSocket::abort() + */ +QUnixSocket::SocketError QUnixSocket::error() const +{ + return (QUnixSocket::SocketError) + (d->error & ~QUnixSocketPrivate::CausedAbort); +} + +/*! + Returns the connection state of this instance. + */ +QUnixSocket::SocketState QUnixSocket::state() const +{ + return d->state; +} + +/*! + Returns the Unix path address passed to \l QUnixSocket::connect(). This + method will return an empty path if the object is in the Unconnected + \l {QUnixSocket::state()}{state } or was connected through a call + to \l QUnixSocket::setSocketDescriptor() + + \sa QUnixSocket::connect() QUnixSocket::setSocketDescriptor() + */ +QByteArray QUnixSocket::address() const +{ + return d->address; +} + +/*! + Returns the number of bytes available for immediate retrieval through a call + to \l QUnixSocket::read(). + */ +qint64 QUnixSocket::bytesAvailable() const +{ + return QIODevice::bytesAvailable() + d->dataBufferLength; +} + +/*! + Returns the number of enqueued bytes still to be written to the socket. + */ +qint64 QUnixSocket::bytesToWrite() const +{ + return d->writeQueueBytes; +} + +/*! + Returns the size of the read buffer in bytes. The read buffer size + determines the amount of byte data that can be read from the socket in one go. + The read buffer size caps the maximum value that can be returned by + \l QUnixSocket::bytesAvailable() and will always be greater than zero. By + default, the read buffer size is 1024 bytes. + + The size of the read buffer is independent of the rights buffer, which can be + queried by \l QUnixSocket::rightsBufferSize(). + + \sa QUnixSocket::setReadBufferSize() + */ +qint64 QUnixSocket::readBufferSize() const +{ + return d->dataBufferCapacity; +} + +/*! + Sets the \a size of the socket's read buffer in bytes. + + The size of the read buffer is independent of the rights buffer, which can be + set by \l QUnixSocket::setRightsBufferSize(). + + Attempting to reduce the buffer size while bytes are available for reading + (ie. while the buffer is in use) will fail. + + \sa QUnixSocket::readBufferSize() + */ +void QUnixSocket::setReadBufferSize(qint64 size) +{ + Q_ASSERT(size > 0); + if(size == d->dataBufferCapacity || d->dataBufferLength) return; + if(d->dataBuffer) delete [] d->dataBuffer; + d->dataBuffer = new char[size]; + d->dataBufferCapacity = size; +} + +/*! + Returns the size of the rights buffer in rights entries. The rights buffer + size determines the number of rights transferences that can be received in + any message. Unlike byte stream data which can be fragmented into many + smaller messages if the \link QUnixSocket::readBufferSize() read buffer + \endlink is not large enough to contain all the available data, rights data + is transmitted as unfragmentable datagrams. If the rights buffer is not + large enough to contain this unfragmentable datagram, the datagram will be + truncated and rights data irretrievably lost. If truncation occurs, the + \l QUnixSocketMessage::rightsWereTruncated() flag will be set. By default + the rights buffer size is 0 entries - rights data cannot be received. + + The size of the rights buffer is independent of the read buffer, which can be + queried by \l QUnixSocket::readBufferSize(). + + \sa QUnixSocket::setRightsBufferSize() + */ +qint64 QUnixSocket::rightsBufferSize() const +{ + return d->ancillaryBufferCount; +} + +/*! + Sets the \a size of the socket's rights buffer in rights entries. + + The size of the rights buffer is independent of the read buffer, which can be + set by \l QUnixSocket::setReadBufferSize(). + + Attempting to reduce the buffer size while bytes are available for reading + (ie. while the buffer is in use) will fail. + + \sa QUnixSocket::rightsBufferSize() + */ +void QUnixSocket::setRightsBufferSize(qint64 size) +{ + Q_ASSERT(size >= 0); + + if((size == d->ancillaryBufferCount || d->dataBufferLength) && + d->ancillaryBuffer) + return; + + qint64 byteSize = CMSG_SPACE(sizeof(::ucred)) + + CMSG_SPACE(size * sizeof(int)); + + if(d->ancillaryBuffer) delete [] d->ancillaryBuffer; + d->ancillaryBuffer = new char[byteSize]; + d->ancillaryBufferCount = size; +} + +/*! + \overload + + Writes \a socketdata to the socket. In addition to failing if the socket + is not in the Connected state, writing will fail if \a socketdata is + \l {QUnixSocketMessage::isValid()}{invalid. } + + Writes through the QUnixSocket class are asynchronous. Rather than being + written immediately, data is enqueued and written once the application + reenters the Qt event loop and the socket becomes available for writing. + Thus, this method will only fail if the socket is not in the Connected state + - it is illegal to attempt a write on a Unconnected or Closing socket. + + Applications can monitor the progress of data writes through the + \l QUnixSocket::bytesWritten() signal and \l QUnixSocket::bytesToWrite() + method. + + \sa QUnixSocketMessage + */ +qint64 QUnixSocket::write(const QUnixSocketMessage & socketdata) +{ + if(ConnectedState != state() || !socketdata.isValid()) return -1; + if(socketdata.d->size() == 0) return 0; + + d->writeQueue.enqueue(socketdata); + d->writeQueueBytes += socketdata.d->size(); + d->writeNotifier->setEnabled(true); + + return socketdata.d->size(); +} + +/*! + Return the next available message, or an empty message if none is available. + + To avoid retrieving empty messages, applications should connect to the + \l QUnixSocket::readyRead() signal to be notified when new messages are + available or periodically poll the \l QUnixSocket::bytesAvailable() method. + + \sa QUnixSocket::readyRead() QUnixSocket::bytesAvailable() + */ +QUnixSocketMessage QUnixSocket::read() +{ + QUnixSocketMessage data; + if(!d->dataBufferLength) + return data; + + data.d->state = QUnixSocketMessagePrivate::Credential; + + // Bytes are easy + data.setBytes(QByteArray(d->dataBuffer, d->dataBufferLength)); + + // Extract ancillary data + QList<QUnixSocketRights> a; + + ::cmsghdr * h = (::cmsghdr *)CMSG_FIRSTHDR(&(d->message)); + while(h) { + + if(SCM_CREDENTIALS == h->cmsg_type) { + ::ucred * cred = (::ucred *)CMSG_DATA(h); +#ifdef QUNIXSOCKET_DEBUG + qDebug( "Credentials recd: pid %lu - gid %lu - uid %lu", + cred->pid, cred->gid, cred->uid ); +#endif + data.d->pid = cred->pid; + data.d->gid = cred->gid; + data.d->uid = cred->uid; + + } else if(SCM_RIGHTS == h->cmsg_type) { + + int * fds = (int *)CMSG_DATA(h); + int numFds = (h->cmsg_len - CMSG_LEN(0)) / sizeof(int); + + for(int ii = 0; ii < numFds; ++ii) { + QUnixSocketRights qusr(fds[ii], 0); + a.append(qusr); + } + + } else { + +#ifdef QUNIXSOCKET_DEBUG + qFatal("QUnixSocket: Unknown ancillary data type (%d) received.", + h->cmsg_type); +#endif + + } + + h = (::cmsghdr *)CMSG_NXTHDR(&(d->message), h); + } + + if(d->message.msg_flags & MSG_CTRUNC) { + data.d->state = (QUnixSocketMessagePrivate::AncillaryDataState)(QUnixSocketMessagePrivate::Truncated | + QUnixSocketMessagePrivate::Credential ); + } + + if(!a.isEmpty()) + data.d->rights = a; + + d->dataBufferLength = 0; + d->messageValid = false; + d->readNotifier->setEnabled(true); + + return data; +} + +/*! \internal */ +bool QUnixSocket::isSequential() const +{ + return true; +} + +/*! \internal */ +bool QUnixSocket::waitForReadyRead(int msecs) +{ + if(UnconnectedState == d->state) + return false; + + if(d->messageValid) { + return true; + } + + Q_ASSERT(-1 != d->fd); + + int timeout = msecs; + struct timeval tv; + struct timeval *ptrTv = 0; + QTime stopWatch; + + stopWatch.start(); + + do + { + fd_set readset; + + FD_ZERO(&readset); + FD_SET(d->fd, &readset); + + if(-1 != msecs) { + tv.tv_sec = timeout / 1000; + tv.tv_usec = (timeout % 1000) * 1000; + ptrTv = &tv; + } + + int rv = ::select(d->fd + 1, &readset, 0, 0, ptrTv); + switch(rv) { + case 0: + // timeout + return false; + case 1: + // ok + d->readActivated(); + return true; + default: + if (errno != EINTR) + abort(); // error + break; + } + + timeout = msecs - stopWatch.elapsed(); + } + while (timeout > 0); + + return false; +} + +bool QUnixSocket::waitForBytesWritten(int msecs) +{ + if(UnconnectedState == d->state) + return false; + + Q_ASSERT(-1 != d->fd); + + if ( d->writeQueue.isEmpty() ) + return true; + + QTime stopWatch; + stopWatch.start(); + + while ( true ) + { + fd_set fdwrite; + FD_ZERO(&fdwrite); + FD_SET(d->fd, &fdwrite); + int timeout = msecs < 0 ? 0 : msecs - stopWatch.elapsed(); + struct timeval tv; + struct timeval *ptrTv = 0; + if ( -1 != msecs ) + { + tv.tv_sec = timeout / 1000; + tv.tv_usec = (timeout % 1000) * 1000; + ptrTv = &tv; + } + + int rv = ::select(d->fd + 1, 0, &fdwrite, 0, ptrTv); + switch ( rv ) + { + case 0: + // timeout + return false; + case 1: + { + // ok to write + qint64 bytesWritten = d->writeActivated(); + if (bytesWritten == 0) { + // We need to retry + int delay = 1; + do { + if (-1 != msecs) { + timeout = msecs - stopWatch.elapsed(); + if (timeout <= 0) { + // We have exceeded our allotted time + return false; + } else { + if (delay > timeout) + delay = timeout; + } + } + + // Pause before we make another attempt to send + ::usleep(delay * 1000); + if (delay < 1024) + delay *= 2; + + bytesWritten = d->writeActivated(); + } while (bytesWritten == 0); + } + return (bytesWritten != -1); + } + default: + // error - or an uncaught signal!!!!!!!!! + if ( rv == EINTR ) + continue; + abort(); + return false; + } + } + return false; // fix warnings +} + +/*! \internal */ +bool QUnixSocket::canReadLine() const +{ + for(unsigned int ii = 0; ii < d->dataBufferLength; ++ii) + if(d->dataBuffer[ii] == '\n') return true; + return false; +} + +/*! \internal */ +qint64 QUnixSocket::readData(char * data, qint64 maxSize) +{ + Q_ASSERT(data); + if(0 >= maxSize) return 0; + if(!d->dataBufferLength) return 0; + + // Read data + unsigned int size = d->dataBufferLength>maxSize?maxSize:d->dataBufferLength; + memcpy(data, d->dataBuffer, size); + if(size == d->dataBufferLength) { + d->dataBufferLength = 0; + } else { + memmove(d->dataBuffer, d->dataBuffer + size, d->dataBufferLength - size); + d->dataBufferLength -= size; + } + + + // Flush ancillary + d->flushAncillary(); + + if(0 == d->dataBufferLength) + d->readNotifier->setEnabled(true); + + return size; +} + +/*! \internal */ +qint64 QUnixSocket::writeData (const char * data, qint64 maxSize) +{ + return write(QUnixSocketMessage(QByteArray(data, maxSize))); +} + +qint64 QUnixSocketPrivate::writeActivated() +{ + writeNotifier->setEnabled(false); + + QUnixSocketMessage & m = writeQueue.head(); + const QList<QUnixSocketRights> & a = m.rights(); + + // + // Construct the message + // + ::iovec vec; + if ( !m.d->vec ) // message does not already have an iovec + { + vec.iov_base = (void *)m.bytes().constData(); + vec.iov_len = m.bytes().size(); + } + + // Allocate the control buffer + ::msghdr sendmessage; + ::bzero(&sendmessage, sizeof(::msghdr)); + if ( m.d->vec ) + { + sendmessage.msg_iov = m.d->vec; + sendmessage.msg_iovlen = m.d->iovecLen; + } + else + { + sendmessage.msg_iov = &vec; + sendmessage.msg_iovlen = 1; + } + unsigned int required = CMSG_SPACE(sizeof(::ucred)) + + a.size() * CMSG_SPACE(sizeof(int)); + sendmessage.msg_control = new char[required]; + ::bzero(sendmessage.msg_control, required); + sendmessage.msg_controllen = required; + + // Create ancillary buffer + ::cmsghdr * h = CMSG_FIRSTHDR(&sendmessage); + + if(m.d->state & QUnixSocketMessagePrivate::Credential) { + h->cmsg_len = CMSG_LEN(sizeof(::ucred)); + h->cmsg_level = SOL_SOCKET; + h->cmsg_type = SCM_CREDENTIALS; + ((::ucred *)CMSG_DATA(h))->pid = m.d->pid; + ((::ucred *)CMSG_DATA(h))->gid = m.d->gid; + ((::ucred *)CMSG_DATA(h))->uid = m.d->uid; + h = CMSG_NXTHDR(&sendmessage, h); + } else { + sendmessage.msg_controllen -= CMSG_SPACE(sizeof(::ucred)); + } + + for(int ii = 0; ii < a.count(); ++ii) { + const QUnixSocketRights & r = a.at(ii); + + if(r.isValid()) { + h->cmsg_len = CMSG_LEN(sizeof(int)); + h->cmsg_level = SOL_SOCKET; + h->cmsg_type = SCM_RIGHTS; + *((int *)CMSG_DATA(h)) = r.peekFd(); + h = CMSG_NXTHDR(&sendmessage, h); + } else { + sendmessage.msg_controllen -= CMSG_SPACE(sizeof(int)); + } + } + +#ifdef QUNIXSOCKET_DEBUG + qDebug() << "QUnixSocket: Transmitting message (length" << m.d->size() << ")"; +#endif + ::ssize_t s = ::sendmsg(fd, &sendmessage, MSG_DONTWAIT | MSG_NOSIGNAL); +#ifdef QUNIXSOCKET_DEBUG + qDebug() << "QUnixSocket: Transmitted message (" << s << ")"; +#endif + + if(-1 == s) { + if(EAGAIN == errno || EWOULDBLOCK == errno || EINTR == errno) { + writeNotifier->setEnabled(true); + } else if(EPIPE == errno) { +#ifdef QUNIXSOCKET_DEBUG + qDebug() << "QUnixSocket: Remote side disconnected during transmit " + "(" << ::strerror(errno) << ")"; +#endif + me->abort(); + } else { +#ifdef QUNIXSOCKET_DEBUG + qDebug() << "QUnixSocket: Unable to transmit data (" + << ::strerror(errno) << ")"; +#endif + error = (QUnixSocket::SocketError)(QUnixSocket::WriteFailure | + CausedAbort); + me->abort(); + } + } else if(s != m.d->size()) { + + // A partial transmission + writeNotifier->setEnabled(true); + delete [] (char *)sendmessage.msg_control; + m.d->rights = QList<QUnixSocketRights>(); + m.d->removeBytes( s ); + writeQueueBytes -= s; + emit bytesWritten(s); + return s; + + } else { + + // Success! + writeQueue.dequeue(); + Q_ASSERT(writeQueueBytes >= (unsigned)s); + writeQueueBytes -= s; + emit bytesWritten(s); + + } + + delete [] (char *)sendmessage.msg_control; + if(-1 != s && !writeQueue.isEmpty()) + return writeActivated(); + else if(QUnixSocket::ClosingState == me->state() && writeQueue.isEmpty()) + me->abort(); + + if((-1 == s) && (EAGAIN == errno || EWOULDBLOCK == errno || EINTR == errno)) + // Return zero bytes written to indicate retry may be required + return 0; + else + return s; +} + +void QUnixSocketPrivate::readActivated() +{ +#ifdef QUNIXSOCKET_DEBUG + qDebug() << "QUnixSocket: readActivated"; +#endif + readNotifier->setEnabled(false); + + ::iovec vec; + vec.iov_base = dataBuffer; + vec.iov_len = dataBufferCapacity; + + bzero(&message, sizeof(::msghdr)); + message.msg_iov = &vec; + message.msg_iovlen = 1; + message.msg_controllen = ancillaryBufferCapacity(); + message.msg_control = ancillaryBuffer; + + int recvrv = ::recvmsg(fd, &message, 0); +#ifdef QUNIXSOCKET_DEBUG + qDebug() << "QUnixSocket: Received message (" << recvrv << ")"; +#endif + if(-1 == recvrv) { +#ifdef QUNIXSOCKET_DEBUG + qDebug() << "QUnixSocket: Unable to receive data (" + << ::strerror(errno) << ")"; +#endif + error = (QUnixSocket::SocketError)(QUnixSocket::ReadFailure | + CausedAbort); + me->abort(); + } else if(0 == recvrv) { + me->abort(); + } else { + Q_ASSERT(recvrv); + Q_ASSERT((unsigned)recvrv <= dataBufferCapacity); + dataBufferLength = recvrv; + messageValid = true; + +#ifdef QUNIXSOCKET_DEBUG + qDebug() << "QUnixSocket: readyRead() " << dataBufferLength; +#endif + emit readyRead(); + } +} + +QT_END_NAMESPACE + +#include "qunixsocket.moc" diff --git a/src/gui/embedded/qunixsocket_p.h b/src/gui/embedded/qunixsocket_p.h new file mode 100644 index 0000000..6b9bf61 --- /dev/null +++ b/src/gui/embedded/qunixsocket_p.h @@ -0,0 +1,202 @@ +/**************************************************************************** +** +** 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$ +** +****************************************************************************/ + +#ifndef QUNIXSOCKET_P_H +#define QUNIXSOCKET_P_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +#include <QtNetwork/qabstractsocket.h> +#include <QtCore/qiodevice.h> +#include <QtCore/qlist.h> +#include <QtCore/qshareddata.h> + +extern "C" { +#include <sys/types.h> +}; + +QT_BEGIN_NAMESPACE + +class QUnixSocketRights; +class QUnixSocketRightsPrivate; +class QUnixSocketPrivate; +class QUnixSocketMessagePrivate; +struct iovec; + +class Q_GUI_EXPORT QUnixSocketRights { +public: + QUnixSocketRights(int); + ~QUnixSocketRights(); + + QUnixSocketRights(const QUnixSocketRights &); + QUnixSocketRights & operator=(const QUnixSocketRights &); + + bool isValid() const; + + int dupFd() const; + int peekFd() const; + +private: + friend class QUnixSocket; + QUnixSocketRights(int,int); + QSharedDataPointer<QUnixSocketRightsPrivate> d; +}; + +class Q_GUI_EXPORT QUnixSocketMessage { +public: + QUnixSocketMessage(); + QUnixSocketMessage(const QByteArray &); + QUnixSocketMessage(const QByteArray &, const QList<QUnixSocketRights> &); + QUnixSocketMessage(const QUnixSocketMessage &); + QUnixSocketMessage(const iovec*, int); + QUnixSocketMessage & operator=(const QUnixSocketMessage &); + ~QUnixSocketMessage(); + + void setBytes(const QByteArray &); + void setRights(const QList<QUnixSocketRights> &); + + const QList<QUnixSocketRights> & rights() const; + bool rightsWereTruncated() const; + + const QByteArray & bytes() const; + + pid_t processId() const; + uid_t userId() const; + gid_t groupId() const; + + void setProcessId(pid_t); + void setUserId(uid_t); + void setGroupId(gid_t); + + bool isValid() const; +private: + friend class QUnixSocket; + friend class QUnixSocketPrivate; + QSharedDataPointer<QUnixSocketMessagePrivate> d; +}; + +class Q_GUI_EXPORT QUnixSocket : public QIODevice +{ + Q_OBJECT +public: + QUnixSocket(QObject * = 0); + QUnixSocket(qint64, qint64, QObject * = 0); + virtual ~QUnixSocket(); + + enum SocketState { + UnconnectedState = QAbstractSocket::UnconnectedState, + HostLookupState = QAbstractSocket::HostLookupState, + ConnectingState = QAbstractSocket::ConnectingState, + ConnectedState = QAbstractSocket::ConnectedState, + BoundState = QAbstractSocket::BoundState, + ClosingState = QAbstractSocket::ClosingState, + ListeningState = QAbstractSocket::ListeningState, + }; + + enum SocketError { NoError, InvalidPath, ResourceError, + NonexistentPath, ConnectionRefused, UnknownError, + ReadFailure, WriteFailure }; + + bool connect(const QByteArray & path); + bool setSocketDescriptor(int socketDescriptor); + int socketDescriptor() const; + void abort(); + void close(); + + bool flush(); + + SocketError error() const; + + SocketState state() const; + QByteArray address() const; + + qint64 bytesAvailable() const; + qint64 bytesToWrite() const; + + qint64 readBufferSize() const; + void setReadBufferSize(qint64 size); + qint64 rightsBufferSize() const; + void setRightsBufferSize(qint64 size); + + bool canReadLine() const; + + qint64 write(const char * data, qint64 maxSize) + { return QIODevice::write(data, maxSize); } + qint64 write(const QByteArray & byteArray) + { return QIODevice::write(byteArray); } + qint64 read(char * data, qint64 maxSize) + { return QIODevice::read(data, maxSize); } + QByteArray read(qint64 maxSize) + { return QIODevice::read(maxSize); } + + qint64 write(const QUnixSocketMessage &); + QUnixSocketMessage read(); + + virtual bool isSequential() const; + virtual bool waitForReadyRead(int msec = 300); + virtual bool waitForBytesWritten(int msec = 300); + +Q_SIGNALS: + void stateChanged(SocketState socketState); + +protected: + virtual qint64 readData(char * data, qint64 maxSize); + virtual qint64 writeData (const char * data, qint64 maxSize); + +private: + QUnixSocket(const QUnixSocket &); + QUnixSocket & operator=(const QUnixSocket &); + + QUnixSocketPrivate * d; +}; + +QT_END_NAMESPACE + +#endif // QUNIXSOCKET_P_H diff --git a/src/gui/embedded/qunixsocketserver.cpp b/src/gui/embedded/qunixsocketserver.cpp new file mode 100644 index 0000000..6e9347b --- /dev/null +++ b/src/gui/embedded/qunixsocketserver.cpp @@ -0,0 +1,376 @@ +/**************************************************************************** +** +** 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 "qunixsocketserver_p.h" + +// #define QUNIXSOCKETSERVER_DEBUG + +#ifdef QUNIXSOCKETSERVER_DEBUG +#include <QDebug> +#endif + +#include <QtCore/qsocketnotifier.h> + +extern "C" { +#include <sys/types.h> +#include <sys/socket.h> +#include <sys/un.h> +#include <unistd.h> +#include <errno.h> +}; + +#define UNIX_PATH_MAX 108 // From unix(7) + +QT_BEGIN_NAMESPACE + +class QUnixSocketServerPrivate : public QObject +{ +Q_OBJECT +public: + QUnixSocketServerPrivate(QUnixSocketServer * parent) + : QObject(), me(parent), fd(-1), maxConns(30), + error(QUnixSocketServer::NoError), acceptNotifier(0) + {} + + QUnixSocketServer * me; + int fd; + int maxConns; + QByteArray address; + QUnixSocketServer::ServerError error; + QSocketNotifier * acceptNotifier; +public slots: + void acceptActivated(); +}; + +/*! + \class QUnixSocketServer + \internal + + \brief The QUnixSocketServer class provides a Unix domain socket based server. + \omit + \ingroup Platform::DeviceSpecific + \ingroup Platform::OS + \ingroup Platform::Communications + \endomit + \ingroup qws + + This class makes it possible to accept incoming Unix domain socket + connections. Call \l QUnixSocketServer::listen() to have the server listen + for incoming connections on a specified path. The pure virtual + \l QUnixSocketServer::incomingConnection() is called each time a new + connection is established. Users must inherit from QUnixSocketServer and + implement this method. + + If an error occurs, \l QUnixSocketServer::serverError() returns the type of + error. Errors can only occur during server establishment - that is, during a + call to \l QUnixSocketServer::listen(). Calling \l QUnixSocketServer::close() + causes QUnixSocketServer to stop listening for connections and reset its + state. + + QUnixSocketServer is often used in conjunction with the \l QUnixSocket class. + + \sa QUnixSocket +*/ + +/*! + \enum QUnixSocketServer::ServerError + + The ServerError enumeration represents the errors that can occur during server + establishment. The most recent error can be retrieved through a call to + \l QUnixSocketServer::serverError(). + + \value NoError No error has occurred. + \value InvalidPath An invalid path endpoint was passed to + \l QUnixSocketServer::listen(). As defined by unix(7), invalid paths + include an empty path, or what more than 107 characters long. + \value ResourceError An error acquiring or manipulating the system's socket + resources occurred. For example, if the process runs out of available + socket descriptors, a ResourceError will occur. + \value BindError The server was unable to bind to the specified path. + \value ListenError The server was unable to listen on the specified path for + incoming connections. + */ + +/*! + Create a new Unix socket server with the given \a parent. + */ +QUnixSocketServer::QUnixSocketServer(QObject *parent) +: QObject(parent), d(0) +{ +} + +/*! + Stops listening for incoming connection and destroys the Unix socket server. + */ +QUnixSocketServer::~QUnixSocketServer() +{ + close(); + if(d) + delete d; +} + +/*! + Stop listening for incoming connections and resets the Unix socket server's + state. Calling this method while \l {QUnixSocketServer::isListening()}{not listening } for incoming connections is a no-op. + + \sa QUnixSocketServer::listen() + */ +void QUnixSocketServer::close() +{ + if(!d) + return; + + if(d->acceptNotifier) { + d->acceptNotifier->setEnabled(false); + delete d->acceptNotifier; + } + d->acceptNotifier = 0; + + if(-1 != d->fd) { +#ifdef QUNIXSOCKET_DEBUG + int closerv = +#endif + ::close(d->fd); +#ifdef QUNIXSOCKET_DEBUG + if(0 != closerv) { + qDebug() << "QUnixSocketServer: Unable to close socket (" + << strerror(errno) << ")"; + } +#endif + } + d->fd = -1; + d->address = QByteArray(); + d->error = NoError; +} + +/*! + Returns the last server error. Errors may only occur within a call to + \l QUnixSocketServer::listen(), and only when such a call fails. + + This method is not destructive, so multiple calls to + QUnixSocketServer::serverError() will return the same value. The error is + only reset by an explicit call to \l QUnixSocketServer::close() or + by further calls to \l QUnixSocketServer::listen(). + */ +QUnixSocketServer::ServerError QUnixSocketServer::serverError() const +{ + if(!d) + return NoError; + + return d->error; +} + +/*! + Returns true if this server is listening for incoming connections, false + otherwise. + + \sa QUnixSocketServer::listen() + */ +bool QUnixSocketServer::isListening() const +{ + if(!d) + return false; + + return (-1 != d->fd); +} + +/*! + Tells the server to listen for incoming connections on \a path. Returns true + if it successfully initializes, false otherwise. In the case of failure, the + \l QUnixSocketServer::serverError() error status is set accordingly. + + Calling this method while the server is already running will result in the + server begin reset, and then attempting to listen on \a path. This will not + affect connections established prior to the server being reset, but further + incoming connections on the previous path will be refused. + + The server can be explicitly reset by a call to \l QUnixSocketServer::close(). + + \sa QUnixSocketServer::close() + */ +bool QUnixSocketServer::listen(const QByteArray & path) +{ + if(d) { + close(); // Any existing server is destroyed + } else { + d = new QUnixSocketServerPrivate(this); + } + + if(path.isEmpty() || path.size() > UNIX_PATH_MAX) { + d->error = InvalidPath; + return false; + } + unlink( path ); // ok if this fails + + // Create the socket + d->fd = ::socket(PF_UNIX, SOCK_STREAM, 0); + if(-1 == d->fd) { +#ifdef QUNIXSOCKETSERVER_DEBUG + qDebug() << "QUnixSocketServer: Unable to create socket (" + << strerror(errno) << ")"; +#endif + close(); + d->error = ResourceError; + return false; + } + + // Construct our unix address + struct ::sockaddr_un addr; + addr.sun_family = AF_UNIX; + ::memcpy(addr.sun_path, path.data(), path.size()); + if(path.size() < UNIX_PATH_MAX) + addr.sun_path[path.size()] = '\0'; + + // Attempt to bind + if(-1 == ::bind(d->fd, (sockaddr *)&addr, sizeof(sockaddr_un))) { +#ifdef QUNIXSOCKETSERVER_DEBUG + qDebug() << "QUnixSocketServer: Unable to bind socket (" + << strerror(errno) << ")"; +#endif + close(); + d->error = BindError; + return false; + } + + // Listen to socket + if(-1 == ::listen(d->fd, d->maxConns)) { +#ifdef QUNIXSOCKETSERVER_DEBUG + qDebug() << "QUnixSocketServer: Unable to listen socket (" + << strerror(errno) << ")"; +#endif + close(); + d->error = ListenError; + return false; + } + + // Success! + d->address = path; + d->acceptNotifier = new QSocketNotifier(d->fd, QSocketNotifier::Read, d); + d->acceptNotifier->setEnabled(true); + QObject::connect(d->acceptNotifier, SIGNAL(activated(int)), + d, SLOT(acceptActivated())); + + return true; +} + +/*! + Returns the Unix path on which this server is listening. If this server is + not listening, and empty address will be returned. + */ +QByteArray QUnixSocketServer::serverAddress() const +{ + if(!d) + return QByteArray(); + return d->address; +} + +int QUnixSocketServer::socketDescriptor() const +{ + if (!d) + return -1; + return d->fd; +} + + +/*! + Returns the maximum length the queue of pending connections may grow to. That + is, the maximum number of clients attempting to connect for which the Unix + socket server has not yet accepted and passed to + \l QUnixSocketServer::incomingConnection(). If a connection request arrives + with the queue full, the client may receive a connection refused notification. + + By default a queue length of 30 is used. + + \sa QUnixSocketServer::setMaxPendingConnections() + */ +int QUnixSocketServer::maxPendingConnections() const +{ + if(!d) + return 30; + + return d->maxConns; +} + +/*! + Sets the maximum length the queue of pending connections may grow to + \a numConnections. This value will only apply to + \l QUnixSocketServer::listen() calls made following the value change - it will + not be retroactively applied. + + \sa QUnixSocketServer::maxPendingConnections() + */ +void QUnixSocketServer::setMaxPendingConnections(int numConnections) +{ + Q_ASSERT(numConnections >= 1); + if(!d) + d = new QUnixSocketServerPrivate(this); + + d->maxConns = numConnections; +} + +/*! + \fn void QUnixSocketServer::incomingConnection(int socketDescriptor) + + This method is invoked each time a new incoming connection is established with + the server. Clients must reimplement this function in their QUnixSocketServer + derived class to handle the connection. + + A common approach to handling the connection is to pass \a socketDescriptor to + a QUnixSocket instance. + + \sa QUnixSocket + */ + +void QUnixSocketServerPrivate::acceptActivated() +{ + ::sockaddr_un r; + socklen_t len = sizeof(sockaddr_un); + int connsock = ::accept(fd, (sockaddr *)&r, &len); +#ifdef QUNIXSOCKETSERVER_DEBUG + qDebug() << "QUnixSocketServer: Accept connection " << connsock; +#endif + if(-1 != connsock) + me->incomingConnection(connsock); +} + +QT_END_NAMESPACE + +#include "qunixsocketserver.moc" diff --git a/src/gui/embedded/qunixsocketserver_p.h b/src/gui/embedded/qunixsocketserver_p.h new file mode 100644 index 0000000..60d4cfc --- /dev/null +++ b/src/gui/embedded/qunixsocketserver_p.h @@ -0,0 +1,98 @@ +/**************************************************************************** +** +** 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$ +** +****************************************************************************/ + +#ifndef QUNIXSOCKETSERVER_P_H +#define QUNIXSOCKETSERVER_P_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +#include <QtCore/qobject.h> + +QT_BEGIN_NAMESPACE + +class QUnixSocketServerPrivate; +class Q_GUI_EXPORT QUnixSocketServer : public QObject +{ + Q_OBJECT +public: + enum ServerError { NoError, InvalidPath, ResourceError, BindError, + ListenError }; + + QUnixSocketServer(QObject *parent=0); + virtual ~QUnixSocketServer(); + + void close(); + + ServerError serverError() const; + + bool isListening() const; + bool listen(const QByteArray & path); + + int socketDescriptor() const; + QByteArray serverAddress() const; + + int maxPendingConnections() const; + void setMaxPendingConnections(int numConnections); + +protected: + virtual void incomingConnection(int socketDescriptor) = 0; + +private: + QUnixSocketServer(const QUnixSocketServer &); + QUnixSocketServer & operator=(const QUnixSocketServer &); + + friend class QUnixSocketServerPrivate; + QUnixSocketServerPrivate * d; +}; + + +QT_END_NAMESPACE +#endif // QUNIXSOCKETSERVER_P_H + diff --git a/src/gui/embedded/qvfbhdr.h b/src/gui/embedded/qvfbhdr.h new file mode 100644 index 0000000..421d991 --- /dev/null +++ b/src/gui/embedded/qvfbhdr.h @@ -0,0 +1,89 @@ +/**************************************************************************** +** +** 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$ +** +****************************************************************************/ + +#ifndef QVFBHDR_H +#define QVFBHDR_H + +#include <QtGui/qcolor.h> +#include <QtGui/qwindowdefs.h> +#include <QtCore/qrect.h> + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +QT_MODULE(Gui) + +#define QT_VFB_MOUSE_PIPE "/tmp/.qtvfb_mouse-%1" +#define QT_VFB_KEYBOARD_PIPE "/tmp/.qtvfb_keyboard-%1" +#define QT_VFB_MAP "/tmp/.qtvfb_map-%1" + +struct QVFbHeader +{ + int width; + int height; + int depth; + int linestep; + int dataoffset; + QRect update; + bool dirty; + int numcols; + QRgb clut[256]; + int viewerVersion; + int serverVersion; + int brightness; // since 4.4.0 + WId windowId; // since 4.5.0 +}; + +struct QVFbKeyData +{ + unsigned int keycode; + Qt::KeyboardModifiers modifiers; + unsigned short int unicode; + bool press; + bool repeat; +}; + +QT_END_NAMESPACE + +QT_END_HEADER + +#endif // QVFBHDR_H diff --git a/src/gui/embedded/qwindowsystem_p.h b/src/gui/embedded/qwindowsystem_p.h new file mode 100644 index 0000000..7782206 --- /dev/null +++ b/src/gui/embedded/qwindowsystem_p.h @@ -0,0 +1,315 @@ +/**************************************************************************** +** +** 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$ +** +****************************************************************************/ + +#ifndef QWINDOWSYSTEM_QWS_P_H +#define QWINDOWSYSTEM_QWS_P_H + + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists for the convenience +// of the QWSServer class. This header file may change from +// version to version without notice, or even be removed. +// +// We mean it. +// + +#include "private/qobject_p.h" +#include "qwindowsystem_qws.h" +#include "qbrush.h" +#include "qwsproperty_qws.h" +#include "qwscommand_qws_p.h" +#include "QtCore/qbasictimer.h" + +QT_BEGIN_NAMESPACE + +class QWSServerPrivate : public QObjectPrivate { + friend class QCopChannel; + friend class QWSMouseHandler; + friend class QWSWindow; + friend class QWSDisplay; + friend class QWSInputMethod; + Q_DECLARE_PUBLIC(QWSServer) + +public: + QWSServerPrivate() + : screensaverintervals(0) + , screensavereventblocklevel(-1), screensaverblockevents(false) + , saver(0), cursorClient(0), mouseState(0), nReserved(0) + , doClientIsActive(false) + { + } + ~QWSServerPrivate() + { + closeDisplay(); + + qDeleteAll(deletedWindows); + delete [] screensaverintervals; + delete saver; + + qDeleteAll(windows); + windows.clear(); + + delete bgBrush; + bgBrush = 0; + } + QTime screensavertime; + QTimer* screensavertimer; + int* screensaverintervals; + int screensavereventblocklevel; + bool screensaverblockevents; + bool screensaverblockevent( int index, int *screensaverinterval, bool isDown ); + QWSScreenSaver* saver; + QWSClient *cursorClient; + int mouseState; +// bool prevWin; + QList<QWSWindow*> deletedWindows; + QList<int> crashedClientIds; + + void update_regions(); +//private functions moved from class + +private: + void initServer(int flags); +#ifndef QT_NO_COP + static void sendQCopEvent(QWSClient *c, const QString &ch, + const QString &msg, const QByteArray &data, + bool response = false); +#endif + void move_region(const QWSRegionMoveCommand *); + void set_altitude(const QWSChangeAltitudeCommand *); + void set_opacity(const QWSSetOpacityCommand *); + void request_focus(const QWSRequestFocusCommand *); + QRegion reserve_region(QWSWindow *window, const QRegion ®ion); + void request_region(int winId, const QString &surfaceKey, + const QByteArray &surfaceData, + const QRegion ®ion); + void repaint_region(int winId, int windowFlags, bool opaque, const QRegion &); + void destroy_region(const QWSRegionDestroyCommand *); + void name_region(const QWSRegionNameCommand *); + void set_identity(const QWSIdentifyCommand *); +#ifndef QT_NO_QWS_PROPERTIES + bool get_property(int winId, int property, const char *&data, int &len); +#endif +#ifndef QT_NO_QWS_INPUTMETHODS + void im_response(const QWSIMResponseCommand *); + + void im_update(const QWSIMUpdateCommand *); + + void send_im_mouse(const QWSIMMouseCommand *); +#endif + // not in ifndef as this results in more readable functions. + static void sendKeyEventUnfiltered(int unicode, int keycode, Qt::KeyboardModifiers modifiers, + bool isPress, bool autoRepeat); + static void sendMouseEventUnfiltered(const QPoint &pos, int state, int wheel = 0); + static void emergency_cleanup(); + + static QBrush *bgBrush; + + void sendMaxWindowRectEvents(const QRect &rect); + + void invokeIdentify(const QWSIdentifyCommand *cmd, QWSClient *client); + void invokeCreate(QWSCreateCommand *cmd, QWSClient *client); + void invokeRegionName(const QWSRegionNameCommand *cmd, QWSClient *client); + void invokeRegion(QWSRegionCommand *cmd, QWSClient *client); + void invokeRegionMove(const QWSRegionMoveCommand *cmd, QWSClient *client); + void invokeRegionDestroy(const QWSRegionDestroyCommand *cmd, QWSClient *client); + void invokeSetAltitude(const QWSChangeAltitudeCommand *cmd, QWSClient *client); + void invokeSetOpacity(const QWSSetOpacityCommand *cmd, QWSClient *client); +#ifndef QT_NO_QWS_PROPERTIES + void invokeAddProperty(QWSAddPropertyCommand *cmd); + void invokeSetProperty(QWSSetPropertyCommand *cmd); + void invokeRemoveProperty(QWSRemovePropertyCommand *cmd); + void invokeGetProperty(QWSGetPropertyCommand *cmd, QWSClient *client); +#endif //QT_NO_QWS_PROPERTIES + void invokeSetSelectionOwner(QWSSetSelectionOwnerCommand *cmd); + void invokeConvertSelection(QWSConvertSelectionCommand *cmd); + void invokeSetFocus(const QWSRequestFocusCommand *cmd, QWSClient *client); + + void initIO(); + void setFocus(QWSWindow*, bool gain); +#ifndef QT_NO_QWS_CURSOR + void invokeDefineCursor(QWSDefineCursorCommand *cmd, QWSClient *client); + void invokeSelectCursor(QWSSelectCursorCommand *cmd, QWSClient *client); + void invokePositionCursor(QWSPositionCursorCommand *cmd, QWSClient *client); +#endif + void invokeGrabMouse(QWSGrabMouseCommand *cmd, QWSClient *client); + void invokeGrabKeyboard(QWSGrabKeyboardCommand *cmd, QWSClient *client); +#ifndef QT_NO_SOUND + void invokePlaySound(QWSPlaySoundCommand *cmd, QWSClient *client); +#endif +#ifndef QT_NO_COP + void invokeRegisterChannel(QWSQCopRegisterChannelCommand *cmd, + QWSClient *client); + void invokeQCopSend(QWSQCopSendCommand *cmd, QWSClient *client); +#endif + void invokeRepaintRegion(QWSRepaintRegionCommand *cmd, + QWSClient *client); +#ifndef QT_NO_QWSEMBEDWIDGET + void invokeEmbed(QWSEmbedCommand *cmd, QWSClient *client); +#endif +#ifndef QT_NO_QWS_INPUTMETHODS + void invokeIMResponse(const QWSIMResponseCommand *cmd, + QWSClient *client); + void invokeIMUpdate(const QWSIMUpdateCommand *cmd, + QWSClient *client); +#endif + void invokeFont(const QWSFontCommand *cmd, QWSClient *client); + void invokeScreenTransform(const QWSScreenTransformCommand *cmd, + QWSClient *client); + + QWSMouseHandler* newMouseHandler(const QString& spec); + void openDisplay(); + void closeDisplay(); + + void showCursor(); + void hideCursor(); + void initializeCursor(); + + void resetEngine(); + +//private Q_SLOTS: + +#ifndef QT_NO_QWS_MULTIPROCESS + void _q_clientClosed(); + void _q_doClient(); + void _q_deleteWindowsLater(); +#endif + + void _q_screenSaverWake(); + void _q_screenSaverSleep(); + void _q_screenSaverTimeout(); +#ifndef QT_NO_QWS_MULTIPROCESS + void _q_newConnection(); +#endif + +//other private moved from class + + void disconnectClient(QWSClient *); + void screenSave(int level); + void doClient(QWSClient *); + typedef QMap<int,QWSClient*>::Iterator ClientIterator; + typedef QMap<int,QWSClient*> ClientMap; + void handleWindowClose(QWSWindow *w); + void releaseMouse(QWSWindow* w); + void releaseKeyboard(QWSWindow* w); + void updateClientCursorPos(); + + uchar* sharedram; + int ramlen; + + ClientMap clientMap; +#ifndef QT_NO_QWS_PROPERTIES + QWSPropertyManager propertyManager; +#endif + struct SelectionOwner { + int windowid; + struct Time { + void set(int h, int m, int s, int s2) { + hour = h; minute = m; sec = s; ms = s2; + } + int hour, minute, sec, ms; + } time; + } selectionOwner; + QTime timer; + int* screensaverinterval; + + QWSWindow *focusw; + QWSWindow *mouseGrabber; + bool mouseGrabbing; + bool inputMethodMouseGrabbed; + int swidth, sheight, sdepth; +#ifndef QT_NO_QWS_CURSOR + bool haveviscurs; + QWSCursor *cursor; // cursor currently shown + QWSCursor *nextCursor; // cursor to show once grabbing is off +#endif + + bool disablePainting; + QList<QWSMouseHandler*> mousehandlers; +#ifndef QT_NO_QWS_KEYBOARD + QList<QWSKeyboardHandler*> keyboardhandlers; +#endif + + QList<QWSCommandStruct*> commandQueue; + + // Window management + QList<QWSWindow*> windows; // first=topmost + int nReserved; + QWSWindow* newWindow(int id, QWSClient* client); + QWSWindow* findWindow(int windowid, QWSClient* client = 0); + void moveWindowRegion(QWSWindow*, int dx, int dy); + void setWindowRegion(QWSWindow*, const QRegion &r); + void raiseWindow(QWSWindow *, int = 0); + void lowerWindow(QWSWindow *, int = -1); + void exposeRegion(const QRegion &, int index = 0); + + void setCursor(QWSCursor *curs); + + // multimedia +#ifndef QT_NO_SOUND + QWSSoundServer *soundserver; +#endif +#ifndef QT_NO_COP + QMap<QString, QList<QWSClient*> > channels; +#endif + +#ifndef QT_NO_QWS_MULTIPROCESS + QWSServerSocket *ssocket; +#endif + + // filename -> refcount + QMap<QByteArray, int> fontReferenceCount; + QBasicTimer fontCleanupTimer; + void referenceFont(QWSClientPrivate *client, const QByteArray &font); + void dereferenceFont(QWSClientPrivate *client, const QByteArray &font); + void cleanupFonts(bool force = false); + void sendFontRemovedEvent(const QByteArray &font); + + bool doClientIsActive; + QList<QWSClient*> pendingDoClients; +}; + +QT_END_NAMESPACE + +#endif 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" diff --git a/src/gui/embedded/qwindowsystem_qws.h b/src/gui/embedded/qwindowsystem_qws.h new file mode 100644 index 0000000..8af59a3 --- /dev/null +++ b/src/gui/embedded/qwindowsystem_qws.h @@ -0,0 +1,508 @@ +/**************************************************************************** +** +** 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$ +** +****************************************************************************/ + +#ifndef QWINDOWSYSTEM_QWS_H +#define QWINDOWSYSTEM_QWS_H + +#include <QtCore/qbytearray.h> +#include <QtCore/qmap.h> +#include <QtCore/qdatetime.h> +#include <QtCore/qlist.h> + +#include <QtGui/qwsevent_qws.h> +#include <QtGui/qkbd_qws.h> +#include <QtGui/qregion.h> + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +QT_MODULE(Gui) + +struct QWSWindowPrivate; +class QWSCursor; +class QWSClient; +class QWSRegionManager; +class QBrush; +class QVariant; +class QInputMethodEvent; +class QWSInputMethod; +class QWSBackingStore; +class QWSWindowSurface; + +#ifdef QT3_SUPPORT +class QImage; +class QColor; +#endif + +class QWSInternalWindowInfo +{ +public: + int winid; + unsigned int clientid; + QString name; // Corresponds to QObject name of top-level widget +}; + + +class Q_GUI_EXPORT QWSScreenSaver +{ +public: + virtual ~QWSScreenSaver(); + virtual void restore()=0; + virtual bool save(int level)=0; +}; + + +class Q_GUI_EXPORT QWSWindow +{ + friend class QWSServer; + friend class QWSServerPrivate; + +public: + QWSWindow(int i, QWSClient* client); + ~QWSWindow(); + + int winId() const { return id; } + const QString &name() const { return rgnName; } + const QString &caption() const { return rgnCaption; } + QWSClient* client() const { return c; } + const QRegion &requestedRegion() const { return requested_region; } + QRegion allocatedRegion() const; + QRegion paintedRegion() const; + bool isVisible() const { return !requested_region.isEmpty(); } + bool isPartiallyObscured() const { return requested_region != allocatedRegion(); } + bool isFullyObscured() const { return allocatedRegion().isEmpty(); } + + enum State { NoState, Hidden, Showing, Visible, Hiding, Raising, Lowering, Moving, ChangingGeometry, Destroyed }; + State state() const; + Qt::WindowFlags windowFlags() const; + QRegion dirtyOnScreen() const; + + void raise(); + void lower(); + void show(); + void hide(); + void setActiveWindow(); + + bool isOpaque() const {return opaque && _opacity == 255;} + uint opacity() const { return _opacity; } + + QWSWindowSurface* windowSurface() const { return surface; } + +private: + bool hidden() const { return requested_region.isEmpty(); } + bool forClient(const QWSClient* cl) const { return cl==c; } + + void setName(const QString &n); + void setCaption(const QString &c); + + void focus(bool get); + int focusPriority() const { return last_focus_time; } + void operation(QWSWindowOperationEvent::Operation o); + void shuttingDown() { last_focus_time=0; } + +#ifdef QT_QWS_CLIENTBLIT + QRegion directPaintRegion() const; + inline void setDirectPaintRegion(const QRegion &topmost); +#endif + inline void setAllocatedRegion(const QRegion ®ion); + + void createSurface(const QString &key, const QByteArray &data); + +#ifndef QT_NO_QWSEMBEDWIDGET + void startEmbed(QWSWindow *window); + void stopEmbed(QWSWindow *window); +#endif + +private: + int id; + QString rgnName; + QString rgnCaption; + bool modified; + bool onTop; + QWSClient* c; + QRegion requested_region; + QRegion exposed; + int last_focus_time; + QWSWindowSurface *surface; + uint _opacity; + bool opaque; + QWSWindowPrivate *d; +#ifdef QT3_SUPPORT + inline QT3_SUPPORT QRegion requested() const { return requested_region; } +// inline QT3_SUPPORT QRegion allocation() const { return allocated_region; } +#endif +}; + + +#ifndef QT_NO_SOUND +class QWSSoundServer; +#ifdef QT_USE_OLD_QWS_SOUND +class QWSSoundServerData; + +class Q_GUI_EXPORT QWSSoundServer : public QObject { + Q_OBJECT +public: + QWSSoundServer(QObject* parent); + ~QWSSoundServer(); + void playFile(const QString& filename); +private Q_SLOTS: + void feedDevice(int fd); +private: + QWSSoundServerData* d; +}; +#endif +#endif + + +/********************************************************************* + * + * Class: QWSServer + * + *********************************************************************/ + +class QWSMouseHandler; +struct QWSCommandStruct; +class QWSServerPrivate; +class QWSServer; + +extern Q_GUI_EXPORT QWSServer *qwsServer; + +class Q_GUI_EXPORT QWSServer : public QObject +{ + friend class QCopChannel; + friend class QWSMouseHandler; + friend class QWSWindow; + friend class QWSDisplay; + friend class QWSInputMethod; + Q_OBJECT + Q_DECLARE_PRIVATE(QWSServer) +public: + explicit QWSServer(int flags = 0, QObject *parent=0); +#ifdef QT3_SUPPORT + QT3_SUPPORT_CONSTRUCTOR QWSServer(int flags, QObject *parent, const char *name); +#endif + ~QWSServer(); + enum ServerFlags { DisableKeyboard = 0x01, + DisableMouse = 0x02 }; + + static void sendKeyEvent(int unicode, int keycode, Qt::KeyboardModifiers modifiers, + bool isPress, bool autoRepeat); +#ifndef QT_NO_QWS_KEYBOARD + static void processKeyEvent(int unicode, int keycode, Qt::KeyboardModifiers modifiers, + bool isPress, bool autoRepeat); +#endif + + static QWSServer* instance() { return qwsServer; } + +#ifndef QT_NO_QWS_INPUTMETHODS +#ifdef QT3_SUPPORT + enum IMState { IMCompose, IMEnd, IMStart = IMCompose }; +#endif + enum IMMouse { MousePress, MouseRelease, MouseMove, MouseOutside }; //MouseMove reserved but not used + void sendIMEvent(const QInputMethodEvent*); + void sendIMQuery(int property); +#endif + +#ifndef QT_NO_QWS_KEYBOARD + class KeyboardFilter + { + public: + virtual ~KeyboardFilter() {} + virtual bool filter(int unicode, int keycode, int modifiers, + bool isPress, bool autoRepeat)=0; + }; + static void addKeyboardFilter(KeyboardFilter *f); + static void removeKeyboardFilter(); +#endif + +#ifndef QT_NO_QWS_INPUTMETHODS + static void setCurrentInputMethod(QWSInputMethod *im); + static void resetInputMethod(); +#endif + + static void setDefaultMouse(const char *); + static void setDefaultKeyboard(const char *); + static void setMaxWindowRect(const QRect&); + static void sendMouseEvent(const QPoint& pos, int state, int wheel = 0); + + static void setBackground(const QBrush &); +#ifdef QT3_SUPPORT + static QT3_SUPPORT void setDesktopBackground(const QImage &img); + static QT3_SUPPORT void setDesktopBackground(const QColor &); +#endif + static QWSMouseHandler *mouseHandler(); + static const QList<QWSMouseHandler*>& mouseHandlers(); + static void setMouseHandler(QWSMouseHandler*); +#ifndef QT_NO_QWS_KEYBOARD + static QWSKeyboardHandler* keyboardHandler(); + static void setKeyboardHandler(QWSKeyboardHandler* kh); +#endif + QWSWindow *windowAt(const QPoint& pos); + + const QList<QWSWindow*> &clientWindows(); + + void openMouse(); + void closeMouse(); + void suspendMouse(); + void resumeMouse(); +#ifndef QT_NO_QWS_KEYBOARD + void openKeyboard(); + void closeKeyboard(); +#endif + + static void setScreenSaver(QWSScreenSaver*); + static void setScreenSaverIntervals(int* ms); + static void setScreenSaverInterval(int); + static void setScreenSaverBlockLevel(int); + static bool screenSaverActive(); + static void screenSaverActivate(bool); + + // the following are internal. + void refresh(); + void refresh(QRegion &); + + void enablePainting(bool); + static void processEventQueue(); + static QList<QWSInternalWindowInfo*> * windowList(); + + void sendPropertyNotifyEvent(int property, int state); + + static QPoint mousePosition; + + static void startup(int flags); + static void closedown(); + + static void beginDisplayReconfigure(); + static void endDisplayReconfigure(); + +#ifndef QT_NO_QWS_CURSOR + static void setCursorVisible(bool); + static bool isCursorVisible(); +#endif + + const QBrush &backgroundBrush() const; + + enum WindowEvent { Create=0x0001, Destroy=0x0002, Hide=0x0004, Show=0x0008, + Raise=0x0010, Lower=0x0020, Geometry=0x0040, Active = 0x0080, + Name=0x0100 }; + +Q_SIGNALS: + void windowEvent(QWSWindow *w, QWSServer::WindowEvent e); + +#ifndef QT_NO_COP + void newChannel(const QString& channel); + void removedChannel(const QString& channel); + +#endif +#ifndef QT_NO_QWS_INPUTMETHODS + void markedText(const QString &); +#endif + +protected: + void timerEvent(QTimerEvent *e); + +private: + friend class QApplicationPrivate; + void updateWindowRegions() const; + +#ifdef QT3_SUPPORT +#ifndef QT_NO_QWS_KEYBOARD + static inline QT3_SUPPORT void setKeyboardFilter(QWSServer::KeyboardFilter *f) + { if (f) addKeyboardFilter(f); else removeKeyboardFilter(); } +#endif +#endif + +private: +#ifndef QT_NO_QWS_MULTIPROCESS + Q_PRIVATE_SLOT(d_func(), void _q_clientClosed()) + Q_PRIVATE_SLOT(d_func(), void _q_doClient()) + Q_PRIVATE_SLOT(d_func(), void _q_deleteWindowsLater()) +#endif + + Q_PRIVATE_SLOT(d_func(), void _q_screenSaverWake()) + Q_PRIVATE_SLOT(d_func(), void _q_screenSaverSleep()) + Q_PRIVATE_SLOT(d_func(), void _q_screenSaverTimeout()) + +#ifndef QT_NO_QWS_MULTIPROCESS + Q_PRIVATE_SLOT(d_func(), void _q_newConnection()) +#endif +}; + +#ifndef QT_NO_QWS_INPUTMETHODS +class Q_GUI_EXPORT QWSInputMethod : public QObject +{ + Q_OBJECT +public: + QWSInputMethod(); + virtual ~QWSInputMethod(); + + enum UpdateType {Update, FocusIn, FocusOut, Reset, Destroyed}; + + virtual bool filter(int unicode, int keycode, int modifiers, + bool isPress, bool autoRepeat); + + virtual bool filter(const QPoint &, int state, int wheel); + + virtual void reset(); + virtual void updateHandler(int type); + virtual void mouseHandler(int pos, int state); + virtual void queryResponse(int property, const QVariant&); + +protected: + uint setInputResolution(bool isHigh); + uint inputResolutionShift() const; + // needed for required transform + void sendMouseEvent(const QPoint &pos, int state, int wheel); + + void sendEvent(const QInputMethodEvent*); + void sendPreeditString(const QString &preeditString, int cursorPosition, int selectionLength = 0); + void sendCommitString(const QString &commitString, int replaceFrom = 0, int replaceLength = 0); + void sendQuery(int property); + +#ifdef QT3_SUPPORT + inline void sendIMEvent(QWSServer::IMState, const QString& txt, int cpos, int selLen = 0); +#endif +private: + bool mIResolution; +}; + +inline void QWSInputMethod::sendEvent(const QInputMethodEvent *ime) +{ + qwsServer->sendIMEvent(ime); +} +#ifdef QT3_SUPPORT +inline void QWSInputMethod::sendIMEvent(QWSServer::IMState state, const QString& txt, int cpos, int selLen) +{ + if (state == QWSServer::IMCompose) sendPreeditString(txt, cpos, selLen); else sendCommitString(txt); +} +#endif + +inline void QWSInputMethod::sendQuery(int property) +{ + qwsServer->sendIMQuery(property); +} + +// mouse events not inline as involve transformations. +#endif // QT_NO_QWS_INPUTMETHODS + + + +/********************************************************************* + * + * Class: QWSClient + * + *********************************************************************/ + +struct QWSMouseEvent; + +typedef QMap<int, QWSCursor*> QWSCursorMap; + +class QWSClientPrivate; +class QWSCommand; +class QWSConvertSelectionCommand; + +class Q_GUI_EXPORT QWSClient : public QObject +{ + Q_OBJECT + Q_DECLARE_PRIVATE(QWSClient) +public: + QWSClient(QObject* parent, QWS_SOCK_BASE *, int id); + ~QWSClient(); + + int socket() const; + + void setIdentity(const QString&); + QString identity() const { return id; } + + void sendEvent(QWSEvent* event); + void sendConnectedEvent(const char *display_spec); + void sendMaxWindowRectEvent(const QRect &rect); + void sendPropertyNotifyEvent(int property, int state); + void sendPropertyReplyEvent(int property, int len, const char *data); + void sendSelectionClearEvent(int windowid); + void sendSelectionRequestEvent(QWSConvertSelectionCommand *cmd, int windowid); +#ifndef QT_QWS_CLIENTBLIT + void sendRegionEvent(int winid, QRegion rgn, int type); +#else + void sendRegionEvent(int winid, QRegion rgn, int type, int id = 0); +#endif +#ifndef QT_NO_QWSEMBEDWIDGET + void sendEmbedEvent(int winid, QWSEmbedEvent::Type type, + const QRegion ®ion = QRegion()); +#endif + QWSCommand* readMoreCommand(); + + int clientId() const { return cid; } + + QWSCursorMap cursors; // cursors defined by this client +Q_SIGNALS: + void connectionClosed(); + void readyRead(); +private Q_SLOTS: + void closeHandler(); + void errorHandler(); + +private: +#ifndef QT_NO_QWS_MULTIPROCESS + friend class QWSWindow; + void removeUnbufferedSurface(); + void addUnbufferedSurface(); +#endif + +private: + int socketDescriptor; +#ifndef QT_NO_QWS_MULTIPROCESS + QWSSocket *csocket; +#endif + QWSCommand* command; + uint isClosed : 1; + QString id; + int cid; + + friend class QWSServerPrivate; +}; + +QT_END_NAMESPACE + +QT_END_HEADER + +#endif // QWINDOWSYSTEM_QWS_H diff --git a/src/gui/embedded/qwscommand_qws.cpp b/src/gui/embedded/qwscommand_qws.cpp new file mode 100644 index 0000000..88e33a3 --- /dev/null +++ b/src/gui/embedded/qwscommand_qws.cpp @@ -0,0 +1,610 @@ +/**************************************************************************** +** +** 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 "qwscommand_qws_p.h" +#include "qtransportauth_qws.h" +#include "qtransportauth_qws_p.h" + +#include <sys/uio.h> +#include <unistd.h> + +// #define QWSCOMMAND_DEBUG 1 // Uncomment to debug client/server communication + +#ifdef QWSCOMMAND_DEBUG +# include <qdebug.h> +# include "qfile.h" +# include <ctype.h> +#endif + +QT_BEGIN_NAMESPACE + +#ifdef QWSCOMMAND_DEBUG +// QWSHexDump -[ start ]--------------------------------------------- +# define QWSHEXDUMP_MAX 32 +class QWSHexDump +{ +public: + + QWSHexDump(const void *address, int len, int wrapAt = 16) + : wrap(wrapAt), dataSize(len) + { + init(); + data = reinterpret_cast<const char*>(address); + if (len < 0) + dataSize = 0; + } + + QWSHexDump(const char *str, int len = -1, int wrapAt = 16) + : wrap(wrapAt), dataSize(len) + { + init(); + data = str; + if (len == -1) + dataSize = str ? strlen(str) : 0; + } + + QWSHexDump(const QByteArray &array, int wrapAt = 16) + : wrap(wrapAt) + { + init(); + data = array.data(); + dataSize = array.size(); + } + + // Sets a customized prefix for the hexdump + void setPrefix(const char *str) { prefix = str; } + + // Sets number of bytes to cluster together + void setClusterSize(uint num) { clustering = num; } + + // Output hexdump to a text stream + void intoTextStream(QTextStream &strm) { + outstrm = &strm; + hexDump(); + } + + // Output hexdump to a QString + QString toString(); + +protected: + void init(); + void hexDump(); + void sideviewDump(int at); + +private: + uint wrap; + uint clustering; + uint dataSize; + int dataWidth; + const char *data; + const char *prefix; + bool dirty; + + char sideviewLayout[QWSHEXDUMP_MAX + 1]; + char sideview[15]; + + QTextStream *outstrm; +}; + +void QWSHexDump::init() +{ + prefix = "> "; // Standard line prefix + clustering = 2; // Word-size clustering by default + if (wrap > QWSHEXDUMP_MAX) // No wider than QWSHexDump_MAX bytes + wrap = QWSHEXDUMP_MAX; +} + +void QWSHexDump::hexDump() +{ + *outstrm << "(" << dataSize << " bytes):\n" << prefix; + sprintf(sideviewLayout, " [%%-%us]", wrap); + dataWidth = (2 * wrap) + (wrap / clustering); + + dirty = false; + uint wrapIndex = 0; + for (uint i = 0; i < dataSize; i++) { + uint c = static_cast<uchar>(data[i]); + sideview[wrapIndex = i%wrap] = isprint(c) ? c : '.'; + + if (wrapIndex && (wrapIndex % clustering == 0)) + *outstrm << " "; + + outstrm->setFieldWidth(2); + outstrm->setPadChar('0'); + outstrm->setNumberFlags( QTextStream::ShowBase ); + *outstrm << hex << c; + dirty = true; + + if (wrapIndex == wrap-1) { + sideviewDump(wrapIndex); + wrapIndex = 0; + if (i+1 < dataSize) + *outstrm << endl << prefix; + } + + } + sideviewDump(wrapIndex); +} + +void QWSHexDump::sideviewDump(int at) +{ + if (dirty) { + dirty = false; + ++at; + sideview[at] = '\0'; + int currentWidth = (2 * at) + (at / clustering) - (at%clustering?0:1); + int missing = qMax(dataWidth - currentWidth, 0); + while (missing--) + *outstrm << " "; + + *outstrm << " ["; + outstrm->setPadChar(' '); + outstrm->setFieldWidth(wrap); + outstrm->setFieldAlignment( QTextStream::AlignLeft ); + *outstrm << sideview; + *outstrm << "]"; + } +} + +// Output hexdump to a QString +QString QWSHexDump::toString() { + QString result; + QTextStream strm(&result, QFile::WriteOnly); + outstrm = &strm; + hexDump(); + return result; +} + +#ifndef QT_NO_DEBUG +QDebug &operator<<(QDebug &dbg, QWSHexDump *hd) { + if (!hd) + return dbg << "QWSHexDump(0x0)"; + QString result = hd->toString(); + dbg.nospace() << result; + return dbg.space(); +} + +// GCC & Intel wont handle references here +QDebug operator<<(QDebug dbg, QWSHexDump hd) { + return dbg << &hd; +} +#endif +// QWSHexDump -[ end ]----------------------------------------------- + + +QDebug &operator<<(QDebug &dbg, QWSCommand::Type tp) +{ + dbg << qws_getCommandTypeString( tp ); + return dbg; +} + +#define N_EVENTS 19 +const char * eventNames[N_EVENTS] = { + "NoEvent", + "Connected", + "Mouse", "Focus", "Key", + "Region", + "Creation", + "PropertyNotify", + "PropertyReply", + "SelectionClear", + "SelectionRequest", + "SelectionNotify", + "MaxWindowRect", + "QCopMessage", + "WindowOperation", + "IMEvent", + "IMQuery", + "IMInit", + "Font" + }; + +class QWSServer; +extern QWSServer *qwsServer; +#endif + +const char *qws_getCommandTypeString( QWSCommand::Type tp ) +{ + const char *typeStr; + switch(tp) { + case QWSCommand::Create: + typeStr = "Create"; + break; + case QWSCommand::Shutdown: + typeStr = "Shutdown"; + break; + case QWSCommand::Region: + typeStr = "Region"; + break; + case QWSCommand::RegionMove: + typeStr = "RegionMove"; + break; + case QWSCommand::RegionDestroy: + typeStr = "RegionDestroy"; + break; + case QWSCommand::SetProperty: + typeStr = "SetProperty"; + break; + case QWSCommand::AddProperty: + typeStr = "AddProperty"; + break; + case QWSCommand::RemoveProperty: + typeStr = "RemoveProperty"; + break; + case QWSCommand::GetProperty: + typeStr = "GetProperty"; + break; + case QWSCommand::SetSelectionOwner: + typeStr = "SetSelectionOwner"; + break; + case QWSCommand::ConvertSelection: + typeStr = "ConvertSelection"; + break; + case QWSCommand::RequestFocus: + typeStr = "RequestFocus"; + break; + case QWSCommand::ChangeAltitude: + typeStr = "ChangeAltitude"; + break; + case QWSCommand::SetOpacity: + typeStr = "SetOpacity"; + break; + case QWSCommand::DefineCursor: + typeStr = "DefineCursor"; + break; + case QWSCommand::SelectCursor: + typeStr = "SelectCursor"; + break; + case QWSCommand::PositionCursor: + typeStr = "PositionCursor"; + break; + case QWSCommand::GrabMouse: + typeStr = "GrabMouse"; + break; + case QWSCommand::PlaySound: + typeStr = "PlaySound"; + break; + case QWSCommand::QCopRegisterChannel: + typeStr = "QCopRegisterChannel"; + break; + case QWSCommand::QCopSend: + typeStr = "QCopSend"; + break; + case QWSCommand::RegionName: + typeStr = "RegionName"; + break; + case QWSCommand::Identify: + typeStr = "Identify"; + break; + case QWSCommand::GrabKeyboard: + typeStr = "GrabKeyboard"; + break; + case QWSCommand::RepaintRegion: + typeStr = "RepaintRegion"; + break; + case QWSCommand::IMMouse: + typeStr = "IMMouse"; + break; + case QWSCommand::IMUpdate: + typeStr = "IMUpdate"; + break; + case QWSCommand::IMResponse: + typeStr = "IMResponse"; + break; + case QWSCommand::Font: + typeStr = "Font"; + break; + case QWSCommand::Unknown: + default: + typeStr = "Unknown"; + break; + } + return typeStr; +} + + +/********************************************************************* + * + * Functions to read/write commands on/from a socket + * + *********************************************************************/ + +#ifndef QT_NO_QWS_MULTIPROCESS +void qws_write_command(QIODevice *socket, int type, char *simpleData, int simpleLen, + char *rawData, int rawLen) +{ +#ifdef QWSCOMMAND_DEBUG + if (simpleLen) qDebug() << "WRITE simpleData " << QWSHexDump(simpleData, simpleLen); + if (rawLen > 0) qDebug() << "WRITE rawData " << QWSHexDump(rawData, rawLen); +#endif + +#ifndef QT_NO_SXE + QTransportAuth *a = QTransportAuth::getInstance(); + // ###### as soon as public API can be modified get rid of horrible casts + QIODevice *ad = a->passThroughByClient(reinterpret_cast<QWSClient*>(socket)); + if (ad) + socket = ad; +#endif + + qws_write_uint(socket, type); + + if (rawLen > MAX_COMMAND_SIZE) { + qWarning("qws_write_command: Message of size %d too big. " + "Truncated to %d", rawLen, MAX_COMMAND_SIZE); + rawLen = MAX_COMMAND_SIZE; + } + + qws_write_uint(socket, rawLen == -1 ? 0 : rawLen); + + if (simpleData && simpleLen) + socket->write(simpleData, simpleLen); + + if (rawLen && rawData) + socket->write(rawData, rawLen); +} + +/* + command format: [type][rawLen][simpleData][rawData] + type is already read when entering this function +*/ + +bool qws_read_command(QIODevice *socket, char *&simpleData, int &simpleLen, + char *&rawData, int &rawLen, int &bytesRead) +{ + + // read rawLen + if (rawLen == -1) { + rawLen = qws_read_uint(socket); + if (rawLen == -1) + return false; + } + + // read simpleData, assumes socket is capable of buffering all the data + if (simpleLen && !rawData) { + if (socket->bytesAvailable() < uint(simpleLen)) + return false; + int tmp = socket->read(simpleData, simpleLen); + Q_ASSERT(tmp == simpleLen); + Q_UNUSED(tmp); + } + + if (rawLen > MAX_COMMAND_SIZE) { + socket->close(); + qWarning("qws_read_command: Won't read command of length %d, " + "connection closed.", rawLen); + return false; + } + + // read rawData + if (rawLen && !rawData) { + rawData = new char[rawLen]; + bytesRead = 0; + } + if (bytesRead < rawLen && socket->bytesAvailable()) + bytesRead += socket->read(rawData + bytesRead, rawLen - bytesRead); + + return (bytesRead == rawLen); +} +#endif + +/********************************************************************* + * + * QWSCommand base class - only use derived classes from that + * + *********************************************************************/ +QWSProtocolItem::~QWSProtocolItem() { + if (deleteRaw) + delete []rawDataPtr; +} + +#ifndef QT_NO_QWS_MULTIPROCESS +void QWSProtocolItem::write(QIODevice *s) { +#ifdef QWSCOMMAND_DEBUG + if (!qwsServer) + qDebug() << "QWSProtocolItem::write sending type " << static_cast<QWSCommand::Type>(type); + else + qDebug() << "QWSProtocolItem::write sending event " << (type < N_EVENTS ? eventNames[type] : "unknown"); +#endif + qws_write_command(s, type, simpleDataPtr, simpleLen, rawDataPtr, rawLen); +} + +bool QWSProtocolItem::read(QIODevice *s) { +#ifdef QWSCOMMAND_DEBUG + QLatin1String reread( (rawLen == -1) ? "" : "REREAD"); + if (qwsServer) + qDebug() << "QWSProtocolItem::read reading type " << static_cast<QWSCommand::Type>(type) << reread; + else + qDebug() << "QWSProtocolItem::read reading event " << (type < N_EVENTS ? eventNames[type] : "unknown") << reread; + //qDebug("QWSProtocolItem::read reading event %s", type < N_EVENTS ? eventNames[type] : "unknown"); +#endif + bool b = qws_read_command(s, simpleDataPtr, simpleLen, rawDataPtr, rawLen, bytesRead); + if (b) { + setData(rawDataPtr, rawLen, false); + deleteRaw = true; + } +#ifdef QWSCOMMAND_DEBUG + else + { + qDebug() << "error in reading command " << static_cast<QWSCommand::Type>(type); + } +#endif + return b; +} +#endif // QT_NO_QWS_MULTIPROCESS + +void QWSProtocolItem::copyFrom(const QWSProtocolItem *item) { + if (this == item) + return; + simpleLen = item->simpleLen; + memcpy(simpleDataPtr, item->simpleDataPtr, simpleLen); + setData(item->rawDataPtr, item->rawLen); +} + +void QWSProtocolItem::setData(const char *data, int len, bool allocateMem) { + if (deleteRaw) + delete [] rawDataPtr; + if (!data || len <= 0) { + rawDataPtr = 0; + rawLen = 0; + return; + } + if (allocateMem) { + rawDataPtr = new char[len]; + memcpy(rawDataPtr, data, len); + deleteRaw = true; + } else { + rawDataPtr = const_cast<char *>(data); + deleteRaw = false; + } + rawLen = len; +} + +QWSCommand *QWSCommand::factory(int type) +{ + QWSCommand *command = 0; + switch (type) { + case QWSCommand::Create: + command = new QWSCreateCommand; + break; + case QWSCommand::Shutdown: + command = new QWSCommand(type, 0, 0); + break; + case QWSCommand::Region: + command = new QWSRegionCommand; + break; + case QWSCommand::RegionMove: + command = new QWSRegionMoveCommand; + break; + case QWSCommand::RegionDestroy: + command = new QWSRegionDestroyCommand; + break; + case QWSCommand::AddProperty: + command = new QWSAddPropertyCommand; + break; + case QWSCommand::SetProperty: + command = new QWSSetPropertyCommand; + break; + case QWSCommand::RemoveProperty: + command = new QWSRemovePropertyCommand; + break; + case QWSCommand::GetProperty: + command = new QWSGetPropertyCommand; + break; + case QWSCommand::SetSelectionOwner: + command = new QWSSetSelectionOwnerCommand; + break; + case QWSCommand::RequestFocus: + command = new QWSRequestFocusCommand; + break; + case QWSCommand::ChangeAltitude: + command = new QWSChangeAltitudeCommand; + break; + case QWSCommand::SetOpacity: + command = new QWSSetOpacityCommand; + break; + case QWSCommand::DefineCursor: + command = new QWSDefineCursorCommand; + break; + case QWSCommand::SelectCursor: + command = new QWSSelectCursorCommand; + break; + case QWSCommand::GrabMouse: + command = new QWSGrabMouseCommand; + break; + case QWSCommand::GrabKeyboard: + command = new QWSGrabKeyboardCommand; + break; +#ifndef QT_NO_SOUND + case QWSCommand::PlaySound: + command = new QWSPlaySoundCommand; + break; +#endif +#ifndef QT_NO_COP + case QWSCommand::QCopRegisterChannel: + command = new QWSQCopRegisterChannelCommand; + break; + case QWSCommand::QCopSend: + command = new QWSQCopSendCommand; + break; +#endif + case QWSCommand::RegionName: + command = new QWSRegionNameCommand; + break; + case QWSCommand::Identify: + command = new QWSIdentifyCommand; + break; + case QWSCommand::RepaintRegion: + command = new QWSRepaintRegionCommand; + break; +#ifndef QT_NO_QWS_INPUTMETHODS + case QWSCommand::IMUpdate: + command = new QWSIMUpdateCommand; + break; + + case QWSCommand::IMMouse: + command = new QWSIMMouseCommand; + break; + + case QWSCommand::IMResponse: + command = new QWSIMResponseCommand; + break; +#endif + case QWSCommand::PositionCursor: + command = new QWSPositionCursorCommand; + break; +#ifndef QT_NO_QWSEMBEDWIDGET + case QWSCommand::Embed: + command = new QWSEmbedCommand; + break; +#endif + case QWSCommand::Font: + command = new QWSFontCommand; + break; + case QWSCommand::ScreenTransform: + command = new QWSScreenTransformCommand; + break; + default: + qWarning("QWSCommand::factory : Type error - got %08x!", type); + } + return command; +} + +QT_END_NAMESPACE diff --git a/src/gui/embedded/qwscommand_qws_p.h b/src/gui/embedded/qwscommand_qws_p.h new file mode 100644 index 0000000..2155333 --- /dev/null +++ b/src/gui/embedded/qwscommand_qws_p.h @@ -0,0 +1,853 @@ +/**************************************************************************** +** +** 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$ +** +****************************************************************************/ + +#ifndef QWSCOMMAND_QWS_P_H +#define QWSCOMMAND_QWS_P_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +// When reading commands "off the wire" in the server, the rawLen is read +// and then that many bytes are allocated. If the rawLen is corrupted (or +// the protocol is being attacked) too many bytes can be allocated. Set +// a hard limit here for security. +#define MAX_COMMAND_SIZE (16 * 1024) + +#include <QtCore/qbytearray.h> +#include <QtGui/qwsutils_qws.h> +#include <QtGui/qfont.h> +#include <QtCore/qdatastream.h> +#include <QtCore/qvariant.h> +#include <QtCore/qrect.h> +#include <QtGui/qregion.h> +#include <QtCore/qvector.h> +#include <QtCore/qvarlengtharray.h> +#include <QtGui/qwsevent_qws.h> +#include "qwsprotocolitem_qws.h" + +QT_BEGIN_NAMESPACE + +QT_MODULE(Gui) + +#define QTE_PIPE "QtEmbedded-%1" + +class QRect; + +/********************************************************************* + * + * Functions to read/write commands on/from a socket + * + *********************************************************************/ +#ifndef QT_NO_QWS_MULTIPROCESS +void qws_write_command(QIODevice *socket, int type, char *simpleData, int simpleLen, char *rawData, int rawLen); +bool qws_read_command(QIODevice *socket, char *&simpleData, int &simpleLen, char *&rawData, int &rawLen, int &bytesRead); +#endif + +struct QWSCommand : QWSProtocolItem +{ + QWSCommand(int t, int len, char *ptr) : QWSProtocolItem(t,len,ptr) {} + + enum Type { + Unknown = 0, + Create, + Shutdown, + Region, + RegionMove, + RegionDestroy, + SetProperty, + AddProperty, + RemoveProperty, + GetProperty, + SetSelectionOwner, + ConvertSelection, + RequestFocus, + ChangeAltitude, + SetOpacity, + DefineCursor, + SelectCursor, + PositionCursor, + GrabMouse, + PlaySound, + QCopRegisterChannel, + QCopSend, + RegionName, + Identify, + GrabKeyboard, + RepaintRegion, + IMMouse, + IMUpdate, + IMResponse, + Embed, + Font, + ScreenTransform + }; + static QWSCommand *factory(int type); +}; + +const char *qws_getCommandTypeString( QWSCommand::Type tp ); + +#ifndef QT_NO_DEBUG +class QDebug; +QDebug &operator<<(QDebug &dbg, QWSCommand::Type tp); +#endif // QT_NO_DEBUG + +/********************************************************************* + * + * Commands + * + *********************************************************************/ + +struct QWSIdentifyCommand : public QWSCommand +{ + QWSIdentifyCommand() : + QWSCommand(QWSCommand::Identify, + sizeof(simpleData), reinterpret_cast<char *>(&simpleData)) + { + simpleData.idLen = 0; + simpleData.idLock = -1; + } + + void setData(const char *d, int len, bool allocateMem) { + QWSCommand::setData(d, len, allocateMem); + if ( simpleData.idLen > MAX_COMMAND_SIZE ) + { + qWarning( "Identify command - name length %d - too big!", simpleData.idLen ); + simpleData.idLen = MAX_COMMAND_SIZE; + } + if ( simpleData.idLen * int(sizeof(QChar)) > len ) + { + qWarning( "Identify command - name length %d - buffer size %d - buffer overrun!", simpleData.idLen, len ); + } + else + { + id = QString(reinterpret_cast<const QChar*>(d), simpleData.idLen); + } + } + + void setId(const QString& i, int lock) + { + id = i; + simpleData.idLen = id.length(); + simpleData.idLock = lock; + setData(reinterpret_cast<const char*>(id.unicode()), simpleData.idLen*2, true); + } + + struct SimpleData { + int idLen; + int idLock; + } simpleData; + QString id; +}; + +struct QWSCreateCommand : public QWSCommand +{ + QWSCreateCommand(int n = 1) : + QWSCommand(QWSCommand::Create, sizeof(count), + reinterpret_cast<char *>(&count)), count(n) {} + int count; +}; + +struct QWSRegionNameCommand : public QWSCommand +{ + QWSRegionNameCommand() : + QWSCommand(QWSCommand::RegionName, + sizeof(simpleData), reinterpret_cast<char *>(&simpleData)) {} + + void setData(const char *d, int len, bool allocateMem) { + QWSCommand::setData(d, len, allocateMem); + if ( simpleData.nameLen > MAX_COMMAND_SIZE ) + { + qWarning( "region name command - name length too big!" ); + simpleData.nameLen = MAX_COMMAND_SIZE; + } + if ( simpleData.captionLen > MAX_COMMAND_SIZE ) + { + qWarning( "region name command - caption length too big!" ); + simpleData.captionLen = MAX_COMMAND_SIZE; + } + if ( simpleData.nameLen + simpleData.captionLen > len ) + { + qWarning( "region name command - name length %d - caption length %d - buffer size %d - buffer overrun!", + simpleData.nameLen, simpleData.captionLen, len ); + + } + else + { + name = QString(reinterpret_cast<const QChar*>(d), simpleData.nameLen/2); + d += simpleData.nameLen; + caption = QString(reinterpret_cast<const QChar*>(d), simpleData.captionLen/2); + } + } + + void setName(const QString& n, const QString &c) + { + name = n; + caption = c; + int l = simpleData.nameLen = name.length()*2; + l += simpleData.captionLen = caption.length()*2; + char *d = new char[l]; + memcpy(d, name.unicode(), simpleData.nameLen); + memcpy(d+simpleData.nameLen, caption.unicode(), simpleData.captionLen); + setData(d, l, true); + delete[] d; + } + + struct SimpleData { + int windowid; + int nameLen; + int captionLen; + } simpleData; + QString name; + QString caption; +}; + +struct QWSRegionCommand : public QWSCommand +{ + QWSRegionCommand() : + QWSCommand(QWSCommand::Region, sizeof(simpleData), + reinterpret_cast<char*>(&simpleData)) {} + + void setData(const char *d, int len, bool allocateMem = true) { + QWSCommand::setData(d, len, allocateMem); + + if( simpleData.nrectangles * int(sizeof(QRect)) + simpleData.surfacekeylength * int(sizeof(QChar)) + simpleData.surfacedatalength * int(sizeof(char)) > len ) + { + qWarning( "region command - rectangle count %d - surface key length %d - region data size %d - buffer size %d - buffer overrun!", + simpleData.nrectangles, simpleData.surfacekeylength, simpleData.surfacedatalength, len ); + } + else + { + char *ptr = rawDataPtr; + + region.setRects(reinterpret_cast<QRect*>(ptr), simpleData.nrectangles); + ptr += simpleData.nrectangles * sizeof(QRect); + + surfaceKey = QString(reinterpret_cast<QChar*>(ptr), + simpleData.surfacekeylength); + ptr += simpleData.surfacekeylength * sizeof(QChar); + + surfaceData = QByteArray(ptr, simpleData.surfacedatalength); + } + } + + void setData(int id, const QString &key, const QByteArray &data, + const QRegion ®) + { + surfaceKey = key; + surfaceData = data; + region = reg; + + const QVector<QRect> rects = reg.rects(); + + simpleData.windowid = id; + simpleData.surfacekeylength = key.size(); + simpleData.surfacedatalength = data.size(); + simpleData.nrectangles = rects.count(); + + QVarLengthArray<char, 256> buffer; + buffer.append(reinterpret_cast<const char*>(rects.constData()), + rects.count() * sizeof(QRect)); + buffer.append(reinterpret_cast<const char*>(key.constData()), + key.size() * sizeof(QChar)); + buffer.append(data, data.size()); + + QWSCommand::setData(buffer.constData(), buffer.size(), true); + } + + /* XXX this will pad out in a compiler dependent way, + should move nrectangles to before windowtype, and + add reserved bytes. + Symptom will be valgrind reported uninitialized memory usage + */ + struct SimpleData { + int windowid; + int surfacekeylength; + int surfacedatalength; + int nrectangles; + } simpleData; + + QString surfaceKey; + QByteArray surfaceData; + QRegion region; +}; + +struct QWSSetOpacityCommand : public QWSCommand +{ + QWSSetOpacityCommand() : + QWSCommand(QWSCommand::SetOpacity, sizeof(simpleData), + reinterpret_cast<char*>(&simpleData)) {} + + struct SimpleData { + int windowid; + uchar opacity; + } simpleData; +}; + +struct QWSRegionMoveCommand : public QWSCommand +{ + QWSRegionMoveCommand() : + QWSCommand(QWSCommand::RegionMove, sizeof(simpleData), + reinterpret_cast<char*>(&simpleData)) {} + + struct SimpleData { + int windowid; + int dx; + int dy; + } simpleData; + +}; + +struct QWSRegionDestroyCommand : public QWSCommand +{ + QWSRegionDestroyCommand() : + QWSCommand(QWSCommand::RegionDestroy, sizeof(simpleData), + reinterpret_cast<char*>(&simpleData)) {} + + struct SimpleData { + int windowid; + } simpleData; + +}; + +struct QWSRequestFocusCommand : public QWSCommand +{ + QWSRequestFocusCommand() : + QWSCommand(QWSCommand::RequestFocus, sizeof(simpleData), reinterpret_cast<char*>(&simpleData)) {} + + struct SimpleData { + int windowid; + int flag; + } simpleData; +}; + +struct QWSChangeAltitudeCommand : public QWSCommand +{ + QWSChangeAltitudeCommand() : + QWSCommand(QWSCommand::ChangeAltitude, sizeof(simpleData), reinterpret_cast<char*>(&simpleData)) {} + + enum Altitude { + Lower = -1, + Raise = 0, + StaysOnTop = 1 + }; + + struct SimpleData { + int windowid; + Altitude altitude; + bool fixed; + } simpleData; + +}; + + +struct QWSAddPropertyCommand : public QWSCommand +{ + QWSAddPropertyCommand() : + QWSCommand(QWSCommand::AddProperty, sizeof(simpleData), reinterpret_cast<char*>(&simpleData)) {} + + struct SimpleData { + int windowid, property; + } simpleData; + +}; + +struct QWSSetPropertyCommand : public QWSCommand +{ + QWSSetPropertyCommand() : + QWSCommand(QWSCommand::SetProperty, sizeof(simpleData), + reinterpret_cast<char*>(&simpleData)) { data = 0; } + + void setData(const char *d, int len, bool allocateMem = true) { + QWSCommand::setData(d, len, allocateMem); + data = rawDataPtr; + } + + struct SimpleData { + int windowid, property, mode; + } simpleData; + + char *data; +}; + +struct QWSRepaintRegionCommand : public QWSCommand +{ + QWSRepaintRegionCommand() : + QWSCommand(QWSCommand::RepaintRegion, sizeof(simpleData), + reinterpret_cast<char*>(&simpleData)) {} + + void setData(const char *d, int len, bool allocateMem = true) { + QWSCommand::setData(d, len, allocateMem); + + if( simpleData.nrectangles * int(sizeof(QRect)) > len ) + { + qWarning( "repaint region command - region rectangle count %d - buffer size %d - buffer overrun", + simpleData.nrectangles, len ); + + simpleData.nrectangles = len / sizeof(QRect); + } + rectangles = reinterpret_cast<QRect *>(rawDataPtr); + } + + struct SimpleData { + int windowid; + int windowFlags; + bool opaque; + int nrectangles; + } simpleData; + + QRect * rectangles; + +}; + +struct QWSRemovePropertyCommand : public QWSCommand +{ + QWSRemovePropertyCommand() : + QWSCommand(QWSCommand::RemoveProperty, sizeof(simpleData), reinterpret_cast<char*>(&simpleData)) {} + + struct SimpleData { + int windowid, property; + } simpleData; + +}; + +struct QWSGetPropertyCommand : public QWSCommand +{ + QWSGetPropertyCommand() : + QWSCommand(QWSCommand::GetProperty, sizeof(simpleData), reinterpret_cast<char*>(&simpleData)) {} + + struct SimpleData { + int windowid, property; + } simpleData; + +}; + +struct QWSSetSelectionOwnerCommand : public QWSCommand +{ + QWSSetSelectionOwnerCommand() : + QWSCommand(QWSCommand::SetSelectionOwner, + sizeof(simpleData), reinterpret_cast<char*>(&simpleData)) {} + + struct SimpleData { + int windowid; + int hour, minute, sec, ms; // time + } simpleData; + +}; + +struct QWSConvertSelectionCommand : public QWSCommand +{ + QWSConvertSelectionCommand() : + QWSCommand(QWSCommand::ConvertSelection, + sizeof(simpleData), reinterpret_cast<char*>(&simpleData)) {} + + struct SimpleData { + int requestor; // requestor window of the selection + int selection; // property on requestor into which the selection should be stored + int mimeTypes; // property ion requestor in which the mimetypes, in which the selection may be, are stored + } simpleData; + +}; + +struct QWSDefineCursorCommand : public QWSCommand +{ + QWSDefineCursorCommand() : + QWSCommand(QWSCommand::DefineCursor, + sizeof(simpleData), reinterpret_cast<char *>(&simpleData)) {} + + void setData(const char *d, int len, bool allocateMem = true) { + QWSCommand::setData(d, len, allocateMem); + data = reinterpret_cast<unsigned char *>(rawDataPtr); + if (simpleData.height * ((simpleData.width+7) / 8) > len) { + qWarning("define cursor command - width %d height %d- buffer size %d - buffer overrun", + simpleData.width, simpleData.height, len ); + simpleData.width = simpleData.height = 0; + } + } + + struct SimpleData { + int width; + int height; + int hotX; + int hotY; + int id; + } simpleData; + + unsigned char *data; +}; + +struct QWSSelectCursorCommand : public QWSCommand +{ + QWSSelectCursorCommand() : + QWSCommand(QWSCommand::SelectCursor, + sizeof(simpleData), reinterpret_cast<char *>(&simpleData)) {} + + struct SimpleData { + int windowid; + int id; + } simpleData; +}; + +struct QWSPositionCursorCommand : public QWSCommand +{ + QWSPositionCursorCommand() : + QWSCommand(QWSCommand::PositionCursor, + sizeof(simpleData), reinterpret_cast<char *>(&simpleData)) {} + + struct SimpleData { + int newX; + int newY; + } simpleData; +}; + +struct QWSGrabMouseCommand : public QWSCommand +{ + QWSGrabMouseCommand() : + QWSCommand(QWSCommand::GrabMouse, + sizeof(simpleData), reinterpret_cast<char *>(&simpleData)) {} + + struct SimpleData { + int windowid; + bool grab; // grab or ungrab? + } simpleData; +}; + +struct QWSGrabKeyboardCommand : public QWSCommand +{ + QWSGrabKeyboardCommand() : + QWSCommand(QWSCommand::GrabKeyboard, + sizeof(simpleData), reinterpret_cast<char *>(&simpleData)) {} + + struct SimpleData { + int windowid; + bool grab; // grab or ungrab? + } simpleData; +}; + +#ifndef QT_NO_SOUND +struct QWSPlaySoundCommand : public QWSCommand +{ + QWSPlaySoundCommand() : + QWSCommand(QWSCommand::PlaySound, + sizeof(simpleData), reinterpret_cast<char *>(&simpleData)) {} + + void setData(const char *d, int len, bool allocateMem) { + QWSCommand::setData(d, len, allocateMem); + filename = QString(reinterpret_cast<QChar*>(rawDataPtr),len/2); + } + void setFileName(const QString& n) + { + setData(reinterpret_cast<const char*>(n.unicode()), n.length()*2, true); + } + + struct SimpleData { + int windowid; + } simpleData; + QString filename; +}; +#endif + + +#ifndef QT_NO_COP +struct QWSQCopRegisterChannelCommand : public QWSCommand +{ + QWSQCopRegisterChannelCommand() : + QWSCommand(QWSCommand::QCopRegisterChannel, + sizeof(simpleData), reinterpret_cast<char *>(&simpleData)) {} + + void setData(const char *d, int len, bool allocateMem) { + QWSCommand::setData(d, len, allocateMem); + if ( simpleData.chLen > MAX_COMMAND_SIZE ) + { + qWarning( "Command channel name too large!" ); + simpleData.chLen = MAX_COMMAND_SIZE; + } + if( simpleData.chLen * int(sizeof(QChar)) > len ) + { + qWarning( "register qcop channel command - channel name length %d - buffer size %d - buffer overrun!", simpleData.chLen, len ); + } + else + { + channel = QString(reinterpret_cast<const QChar*>(d), simpleData.chLen); + } + } + + void setChannel(const QString& n) + { + channel = n; + simpleData.chLen = channel.length(); + setData(reinterpret_cast<const char*>(channel.unicode()), simpleData.chLen*2, true); + } + + struct SimpleData { + int chLen; + } simpleData; + QString channel; +}; + +struct QWSQCopSendCommand : public QWSCommand +{ + QWSQCopSendCommand() : + QWSCommand(QWSCommand::QCopSend, + sizeof(simpleData), reinterpret_cast<char *>(&simpleData)) {} + + void setData(const char *d, int len, bool allocateMem) { + QWSCommand::setData(d, len, allocateMem); + + if( simpleData.clen * int(sizeof(QChar)) + simpleData.mlen * int(sizeof(QChar)) + simpleData.dlen * int(sizeof(char)) > len ) + { + qWarning( "qcop send command - channel name length %d - message name length %d - data size %d - buffer size %d - buffer overrun!", + simpleData.clen, simpleData.mlen, simpleData.dlen, len ); + } + else + { + const QChar *cd = reinterpret_cast<const QChar*>(d); + channel = QString(cd,simpleData.clen); cd += simpleData.clen; + message = QString(cd,simpleData.mlen); + d += simpleData.clen*sizeof(QChar) + simpleData.mlen*sizeof(QChar); + data = QByteArray(d, simpleData.dlen); + } + } + + void setMessage(const QString &c, const QString &m, + const QByteArray &data) + { + this->channel = c; + this->message = m; + this->data = data; + simpleData.clen = c.length(); + simpleData.mlen = m.length(); + simpleData.dlen = data.size(); + int l = simpleData.clen*sizeof(QChar); + l += simpleData.mlen*sizeof(QChar); + l += simpleData.dlen; + char *tmp = new char[l]; + char *d = tmp; + memcpy(d, c.unicode(), simpleData.clen*sizeof(QChar)); + d += simpleData.clen*sizeof(QChar); + memcpy(d, m.unicode(), simpleData.mlen*sizeof(QChar)); + d += simpleData.mlen*sizeof(QChar); + memcpy(d, data.data(), simpleData.dlen); + QWSCommand::setData(tmp, l, false); + deleteRaw = true; + } + + struct SimpleData { + int clen; + int mlen; + int dlen; + } simpleData; + QString channel; + QString message; + QByteArray data; +}; + +#endif + + +#ifndef QT_NO_QWS_INPUTMETHODS + +struct QWSIMMouseCommand : public QWSCommand +{ + QWSIMMouseCommand() : + QWSCommand(QWSCommand::IMMouse, + sizeof(simpleData), reinterpret_cast<char *>(&simpleData)) {} + + struct SimpleData { + int windowid; + int state; + int index; + } simpleData; +}; + + +struct QWSIMResponseCommand : public QWSCommand +{ + QWSIMResponseCommand() : + QWSCommand(QWSCommand::IMResponse, + sizeof(simpleData), reinterpret_cast<char *>(&simpleData)) {} + + void setData(const char *d, int len, bool allocateMem) { + QWSCommand::setData(d, len, allocateMem); + + QByteArray tmp = QByteArray::fromRawData(d, len); + QDataStream s(tmp); + s >> result; + } + + void setResult(const QVariant & v) + { + QByteArray tmp; + QDataStream s(&tmp, QIODevice::WriteOnly); + s << v; + setData(tmp.data(), tmp.size(), true); + } + + struct SimpleData { + int windowid; + int property; + } simpleData; + + QVariant result; +}; + +struct QWSIMUpdateCommand: public QWSCommand +{ + QWSIMUpdateCommand() : + QWSCommand(QWSCommand::IMUpdate, + sizeof(simpleData), reinterpret_cast<char *>(&simpleData)) {} + + struct SimpleData { + int windowid; + int type; + int widgetid; + } simpleData; +}; + +#endif + +#ifndef QT_NO_QWSEMBEDWIDGET +struct QWSEmbedCommand : public QWSCommand +{ + QWSEmbedCommand() : QWSCommand(QWSCommand::Embed, + sizeof(simpleData), + reinterpret_cast<char*>(&simpleData)) + {} + + void setData(const char *d, int len, bool allocateMem = true) + { + QWSCommand::setData(d, len, allocateMem); + + if( simpleData.rects * int(sizeof(QRect)) > len ) + { + qWarning( "embed command - region rectangle count %d - buffer size %d - buffer overrun!", + simpleData.rects, len ); + } + else + { + region.setRects(reinterpret_cast<QRect*>(rawDataPtr), + simpleData.rects); + } + } + + void setData(WId embedder, WId embedded, QWSEmbedEvent::Type type, + const QRegion reg = QRegion()) + { + simpleData.embedder = embedder; + simpleData.embedded = embedded; + simpleData.type = type; + + region = reg; + const QVector<QRect> rects = reg.rects(); + simpleData.rects = rects.count(); + + QWSCommand::setData(reinterpret_cast<const char*>(rects.constData()), + rects.count() * sizeof(QRect)); + } + + struct { + WId embedder; + WId embedded; + QWSEmbedEvent::Type type; + int rects; + } simpleData; + + QRegion region; +}; +#endif // QT_NO_QWSEMBEDWIDGET + +struct QWSFontCommand : public QWSCommand +{ + enum CommandType { + StartedUsingFont, + StoppedUsingFont + }; + + QWSFontCommand() : + QWSCommand(QWSCommand::Font, + sizeof(simpleData), reinterpret_cast<char *>(&simpleData)) {} + + void setData(const char *d, int len, bool allocateMem) { + QWSCommand::setData(d, len, allocateMem); + + fontName = QByteArray(d, len); + } + + void setFontName(const QByteArray &name) + { + setData(name.constData(), name.size(), true); + } + + struct SimpleData { + int type; + } simpleData; + + QByteArray fontName; +}; + +struct QWSScreenTransformCommand : public QWSCommand +{ + QWSScreenTransformCommand() : + QWSCommand(QWSCommand::ScreenTransform, + sizeof(simpleData), reinterpret_cast<char *>(&simpleData)) {} + + void setTransformation(int screen, int transformation) + { + simpleData.screen = screen; + simpleData.transformation = transformation; + } + + struct SimpleData { + int screen; + int transformation; + } simpleData; +}; + +QT_END_NAMESPACE + +#endif // QWSCOMMAND_QWS_P_H diff --git a/src/gui/embedded/qwscursor_qws.cpp b/src/gui/embedded/qwscursor_qws.cpp new file mode 100644 index 0000000..3a5bd2c --- /dev/null +++ b/src/gui/embedded/qwscursor_qws.cpp @@ -0,0 +1,654 @@ +/**************************************************************************** +** +** 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 "qcursor.h" +#include "qbitmap.h" +#include "qscreen_qws.h" +#include "qapplication.h" +#include "qwindowsystem_qws.h" +#include "qwindowsystem_p.h" +#include "qwscursor_qws.h" + +QT_BEGIN_NAMESPACE + +#ifndef QT_NO_QWS_CURSOR +static QWSCursor *systemCursorTable[Qt::LastCursor+1]; +static bool systemCursorTableInit = false; + +// 16 x 16 +static const uchar cur_arrow_bits[] = { + 0x07, 0x00, 0x39, 0x00, 0xc1, 0x01, 0x02, 0x0e, 0x02, 0x10, 0x02, 0x08, + 0x04, 0x04, 0x04, 0x02, 0x04, 0x04, 0x88, 0x08, 0x48, 0x11, 0x28, 0x22, + 0x10, 0x44, 0x00, 0x28, 0x00, 0x10, 0x00, 0x00 }; +static const uchar mcur_arrow_bits[] = { + 0x07, 0x00, 0x3f, 0x00, 0xff, 0x01, 0xfe, 0x0f, 0xfe, 0x1f, 0xfe, 0x0f, + 0xfc, 0x07, 0xfc, 0x03, 0xfc, 0x07, 0xf8, 0x0f, 0x78, 0x1f, 0x38, 0x3e, + 0x10, 0x7c, 0x00, 0x38, 0x00, 0x10, 0x00, 0x00 }; + +static const unsigned char cur_up_arrow_bits[] = { + 0x80, 0x00, 0x40, 0x01, 0x40, 0x01, 0x20, 0x02, 0x20, 0x02, 0x10, 0x04, + 0x10, 0x04, 0x08, 0x08, 0x78, 0x0f, 0x40, 0x01, 0x40, 0x01, 0x40, 0x01, + 0x40, 0x01, 0x40, 0x01, 0x40, 0x01, 0xc0, 0x01}; +static const unsigned char mcur_up_arrow_bits[] = { + 0x80, 0x00, 0xc0, 0x01, 0xc0, 0x01, 0xe0, 0x03, 0xe0, 0x03, 0xf0, 0x07, + 0xf0, 0x07, 0xf8, 0x0f, 0xf8, 0x0f, 0xc0, 0x01, 0xc0, 0x01, 0xc0, 0x01, + 0xc0, 0x01, 0xc0, 0x01, 0xc0, 0x01, 0xc0, 0x01}; + +static const unsigned char cur_cross_bits[] = { + 0xc0, 0x01, 0x40, 0x01, 0x40, 0x01, 0x40, 0x01, 0x40, 0x01, 0x40, 0x01, + 0x7f, 0x7f, 0x01, 0x40, 0x7f, 0x7f, 0x40, 0x01, 0x40, 0x01, 0x40, 0x01, + 0x40, 0x01, 0x40, 0x01, 0xc0, 0x01, 0x00, 0x00}; +static const unsigned char mcur_cross_bits[] = { + 0xc0, 0x01, 0xc0, 0x01, 0xc0, 0x01, 0xc0, 0x01, 0xc0, 0x01, 0xc0, 0x01, + 0xff, 0x7f, 0xff, 0x7f, 0xff, 0x7f, 0xc0, 0x01, 0xc0, 0x01, 0xc0, 0x01, + 0xc0, 0x01, 0xc0, 0x01, 0xc0, 0x01, 0x00, 0x00}; + +static const uchar cur_ibeam_bits[] = { + 0x00, 0x00, 0xe0, 0x03, 0x80, 0x00, 0x80, 0x00, 0x80, 0x00, 0x80, 0x00, + 0x80, 0x00, 0x80, 0x00, 0x80, 0x00, 0x80, 0x00, 0x80, 0x00, 0x80, 0x00, + 0x80, 0x00, 0xe0, 0x03, 0x00, 0x00, 0x00, 0x00 }; +static const uchar mcur_ibeam_bits[] = { + 0xf0, 0x07, 0xf0, 0x07, 0xf0, 0x07, 0xc0, 0x01, 0xc0, 0x01, 0xc0, 0x01, + 0xc0, 0x01, 0xc0, 0x01, 0xc0, 0x01, 0xc0, 0x01, 0xc0, 0x01, 0xc0, 0x01, + 0xf0, 0x07, 0xf0, 0x07, 0xf0, 0x07, 0x00, 0x00 }; + +static const uchar cur_ver_bits[] = { + 0x00, 0x00, 0x00, 0x00, 0x80, 0x01, 0xc0, 0x03, 0xe0, 0x07, 0xf0, 0x0f, + 0x80, 0x01, 0x80, 0x01, 0x80, 0x01, 0x80, 0x01, 0x80, 0x01, 0xf0, 0x0f, + 0xe0, 0x07, 0xc0, 0x03, 0x80, 0x01, 0x00, 0x00 }; +static const uchar mcur_ver_bits[] = { + 0x00, 0x00, 0x80, 0x03, 0xc0, 0x07, 0xe0, 0x0f, 0xf0, 0x1f, 0xf8, 0x3f, + 0xfc, 0x7f, 0xc0, 0x07, 0xc0, 0x07, 0xc0, 0x07, 0xfc, 0x7f, 0xf8, 0x3f, + 0xf0, 0x1f, 0xe0, 0x0f, 0xc0, 0x07, 0x80, 0x03 }; + +static const uchar cur_hor_bits[] = { + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x20, 0x08, 0x30, 0x18, + 0x38, 0x38, 0xfc, 0x7f, 0xfc, 0x7f, 0x38, 0x38, 0x30, 0x18, 0x20, 0x08, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; +static const uchar mcur_hor_bits[] = { + 0x00, 0x00, 0x00, 0x00, 0x40, 0x04, 0x60, 0x0c, 0x70, 0x1c, 0x78, 0x3c, + 0xfc, 0x7f, 0xfe, 0xff, 0xfe, 0xff, 0xfe, 0xff, 0xfc, 0x7f, 0x78, 0x3c, + 0x70, 0x1c, 0x60, 0x0c, 0x40, 0x04, 0x00, 0x00 }; +static const uchar cur_bdiag_bits[] = { + 0x00, 0x00, 0x00, 0x00, 0x00, 0x3f, 0x00, 0x3e, 0x00, 0x3c, 0x00, 0x3e, + 0x00, 0x37, 0x88, 0x23, 0xd8, 0x01, 0xf8, 0x00, 0x78, 0x00, 0xf8, 0x00, + 0xf8, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; +static const uchar mcur_bdiag_bits[] = { + 0x00, 0x00, 0xc0, 0x7f, 0x80, 0x7f, 0x00, 0x7f, 0x00, 0x7e, 0x04, 0x7f, + 0x8c, 0x7f, 0xdc, 0x77, 0xfc, 0x63, 0xfc, 0x41, 0xfc, 0x00, 0xfc, 0x01, + 0xfc, 0x03, 0xfc, 0x07, 0x00, 0x00, 0x00, 0x00 }; +static const uchar cur_fdiag_bits[] = { + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xf8, 0x01, 0xf8, 0x00, 0x78, 0x00, + 0xf8, 0x00, 0xd8, 0x01, 0x88, 0x23, 0x00, 0x37, 0x00, 0x3e, 0x00, 0x3c, + 0x00, 0x3e, 0x00, 0x3f, 0x00, 0x00, 0x00, 0x00 }; +static const uchar mcur_fdiag_bits[] = { + 0x00, 0x00, 0x00, 0x00, 0xfc, 0x07, 0xfc, 0x03, 0xfc, 0x01, 0xfc, 0x00, + 0xfc, 0x41, 0xfc, 0x63, 0xdc, 0x77, 0x8c, 0x7f, 0x04, 0x7f, 0x00, 0x7e, + 0x00, 0x7f, 0x80, 0x7f, 0xc0, 0x7f, 0x00, 0x00 }; +static const uchar cur_blank_bits[] = { + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; + +// 20 x 20 +static const uchar forbidden_bits[] = { + 0x00,0x00,0x00,0x80,0x1f,0x00,0xe0,0x7f,0x00,0xf0,0xf0,0x00,0x38,0xc0,0x01, + 0x7c,0x80,0x03,0xec,0x00,0x03,0xce,0x01,0x07,0x86,0x03,0x06,0x06,0x07,0x06, + 0x06,0x0e,0x06,0x06,0x1c,0x06,0x0e,0x38,0x07,0x0c,0x70,0x03,0x1c,0xe0,0x03, + 0x38,0xc0,0x01,0xf0,0xe0,0x00,0xe0,0x7f,0x00,0x80,0x1f,0x00,0x00,0x00,0x00 }; + +static const uchar forbiddenm_bits[] = { + 0x80,0x1f,0x00,0xe0,0x7f,0x00,0xf0,0xff,0x00,0xf8,0xff,0x01,0xfc,0xf0,0x03, + 0xfe,0xc0,0x07,0xfe,0x81,0x07,0xff,0x83,0x0f,0xcf,0x07,0x0f,0x8f,0x0f,0x0f, + 0x0f,0x1f,0x0f,0x0f,0x3e,0x0f,0x1f,0xfc,0x0f,0x1e,0xf8,0x07,0x3e,0xf0,0x07, + 0xfc,0xe0,0x03,0xf8,0xff,0x01,0xf0,0xff,0x00,0xe0,0x7f,0x00,0x80,0x1f,0x00}; + +// 32 x 32 +static const uchar wait_data_bits[] = { + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xfc, 0x7f, 0x00, + 0x00, 0x04, 0x40, 0x00, 0x00, 0xfc, 0x7f, 0x00, 0x00, 0x08, 0x20, 0x00, + 0x00, 0x08, 0x20, 0x00, 0x00, 0x08, 0x20, 0x00, 0x00, 0x08, 0x20, 0x00, + 0x00, 0x50, 0x15, 0x00, 0x00, 0xa0, 0x0a, 0x00, 0x00, 0x40, 0x05, 0x00, + 0x00, 0x80, 0x02, 0x00, 0x00, 0x40, 0x04, 0x00, 0x00, 0x20, 0x08, 0x00, + 0x00, 0x10, 0x10, 0x00, 0x00, 0x08, 0x21, 0x00, 0x00, 0x88, 0x22, 0x00, + 0x00, 0x48, 0x25, 0x00, 0x00, 0xa8, 0x2a, 0x00, 0x00, 0xfc, 0x7f, 0x00, + 0x00, 0x04, 0x40, 0x00, 0x00, 0xfc, 0x7f, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; +static const uchar wait_mask_bits[] = { + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xfc, 0x7f, 0x00, + 0x00, 0xfc, 0x7f, 0x00, 0x00, 0xfc, 0x7f, 0x00, 0x00, 0xf8, 0x3f, 0x00, + 0x00, 0xf8, 0x3f, 0x00, 0x00, 0xf8, 0x3f, 0x00, 0x00, 0xf8, 0x3f, 0x00, + 0x00, 0xf0, 0x1f, 0x00, 0x00, 0xe0, 0x0f, 0x00, 0x00, 0xc0, 0x07, 0x00, + 0x00, 0x80, 0x03, 0x00, 0x00, 0xc0, 0x07, 0x00, 0x00, 0xe0, 0x0f, 0x00, + 0x00, 0xf0, 0x1f, 0x00, 0x00, 0xf8, 0x3f, 0x00, 0x00, 0xf8, 0x3f, 0x00, + 0x00, 0xf8, 0x3f, 0x00, 0x00, 0xf8, 0x3f, 0x00, 0x00, 0xfc, 0x7f, 0x00, + 0x00, 0xfc, 0x7f, 0x00, 0x00, 0xfc, 0x7f, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; + +static const uchar hsplit_bits[] = { + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0x02, 0x00, 0x00, 0x40, 0x02, 0x00, + 0x00, 0x40, 0x02, 0x00, 0x00, 0x40, 0x02, 0x00, 0x00, 0x40, 0x02, 0x00, + 0x00, 0x41, 0x82, 0x00, 0x80, 0x41, 0x82, 0x01, 0xc0, 0x7f, 0xfe, 0x03, + 0x80, 0x41, 0x82, 0x01, 0x00, 0x41, 0x82, 0x00, 0x00, 0x40, 0x02, 0x00, + 0x00, 0x40, 0x02, 0x00, 0x00, 0x40, 0x02, 0x00, 0x00, 0x40, 0x02, 0x00, + 0x00, 0x40, 0x02, 0x00, 0x00, 0x40, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; +static const uchar hsplitm_bits[] = { + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0xe0, 0x07, 0x00, 0x00, 0xe0, 0x07, 0x00, 0x00, 0xe0, 0x07, 0x00, + 0x00, 0xe0, 0x07, 0x00, 0x00, 0xe2, 0x47, 0x00, 0x00, 0xe3, 0xc7, 0x00, + 0x80, 0xe3, 0xc7, 0x01, 0xc0, 0xff, 0xff, 0x03, 0xe0, 0xff, 0xff, 0x07, + 0xc0, 0xff, 0xff, 0x03, 0x80, 0xe3, 0xc7, 0x01, 0x00, 0xe3, 0xc7, 0x00, + 0x00, 0xe2, 0x47, 0x00, 0x00, 0xe0, 0x07, 0x00, 0x00, 0xe0, 0x07, 0x00, + 0x00, 0xe0, 0x07, 0x00, 0x00, 0xe0, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; +static const uchar vsplit_bits[] = { + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x80, 0x00, 0x00, 0x00, 0xc0, 0x01, 0x00, 0x00, 0xe0, 0x03, 0x00, + 0x00, 0x80, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, + 0x00, 0x80, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0xff, 0x7f, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0x7f, 0x00, + 0x00, 0x80, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, + 0x00, 0x80, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0xe0, 0x03, 0x00, + 0x00, 0xc0, 0x01, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; +static const uchar vsplitm_bits[] = { + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, + 0x00, 0xc0, 0x01, 0x00, 0x00, 0xe0, 0x03, 0x00, 0x00, 0xf0, 0x07, 0x00, + 0x00, 0xf8, 0x0f, 0x00, 0x00, 0xc0, 0x01, 0x00, 0x00, 0xc0, 0x01, 0x00, + 0x00, 0xc0, 0x01, 0x00, 0x80, 0xff, 0xff, 0x00, 0x80, 0xff, 0xff, 0x00, + 0x80, 0xff, 0xff, 0x00, 0x80, 0xff, 0xff, 0x00, 0x80, 0xff, 0xff, 0x00, + 0x80, 0xff, 0xff, 0x00, 0x00, 0xc0, 0x01, 0x00, 0x00, 0xc0, 0x01, 0x00, + 0x00, 0xc0, 0x01, 0x00, 0x00, 0xf8, 0x0f, 0x00, 0x00, 0xf0, 0x07, 0x00, + 0x00, 0xe0, 0x03, 0x00, 0x00, 0xc0, 0x01, 0x00, 0x00, 0x80, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; +static const uchar phand_bits[] = { + 0x00, 0x00, 0x00, 0x00, 0xfe, 0x01, 0x00, 0x00, 0x01, 0x02, 0x00, 0x00, + 0x7e, 0x04, 0x00, 0x00, 0x08, 0x08, 0x00, 0x00, 0x70, 0x08, 0x00, 0x00, + 0x08, 0x08, 0x00, 0x00, 0x70, 0x14, 0x00, 0x00, 0x08, 0x22, 0x00, 0x00, + 0x30, 0x41, 0x00, 0x00, 0xc0, 0x20, 0x00, 0x00, 0x40, 0x12, 0x00, 0x00, + 0x80, 0x08, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; +static const uchar phandm_bits[] = { + 0xfe, 0x01, 0x00, 0x00, 0xff, 0x03, 0x00, 0x00, 0xff, 0x07, 0x00, 0x00, + 0xff, 0x0f, 0x00, 0x00, 0xfe, 0x1f, 0x00, 0x00, 0xf8, 0x1f, 0x00, 0x00, + 0xfc, 0x1f, 0x00, 0x00, 0xf8, 0x3f, 0x00, 0x00, 0xfc, 0x7f, 0x00, 0x00, + 0xf8, 0xff, 0x00, 0x00, 0xf0, 0x7f, 0x00, 0x00, 0xe0, 0x3f, 0x00, 0x00, + 0xc0, 0x1f, 0x00, 0x00, 0x80, 0x0f, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, + 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; + +static const uchar size_all_data_bits[] = { + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x80, 0x00, 0x00, 0x00, 0xc0, 0x01, 0x00, 0x00, 0xe0, 0x03, 0x00, + 0x00, 0x80, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, + 0x00, 0x80, 0x00, 0x00, 0x00, 0x81, 0x40, 0x00, 0x80, 0x81, 0xc0, 0x00, + 0xc0, 0xff, 0xff, 0x01, 0x80, 0x81, 0xc0, 0x00, 0x00, 0x81, 0x40, 0x00, + 0x00, 0x80, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, + 0x00, 0x80, 0x00, 0x00, 0x00, 0xe0, 0x03, 0x00, 0x00, 0xc0, 0x01, 0x00, + 0x00, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; +static const uchar size_all_mask_bits[] = { + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, + 0x00, 0xc0, 0x01, 0x00, 0x00, 0xe0, 0x03, 0x00, 0x00, 0xf0, 0x07, 0x00, + 0x00, 0xf8, 0x0f, 0x00, 0x00, 0xc0, 0x01, 0x00, 0x00, 0xc2, 0x21, 0x00, + 0x00, 0xc3, 0x61, 0x00, 0x80, 0xc3, 0xe1, 0x00, 0xc0, 0xff, 0xff, 0x01, + 0xe0, 0xff, 0xff, 0x03, 0xc0, 0xff, 0xff, 0x01, 0x80, 0xc3, 0xe1, 0x00, + 0x00, 0xc3, 0x61, 0x00, 0x00, 0xc2, 0x21, 0x00, 0x00, 0xc0, 0x01, 0x00, + 0x00, 0xf8, 0x0f, 0x00, 0x00, 0xf0, 0x07, 0x00, 0x00, 0xe0, 0x03, 0x00, + 0x00, 0xc0, 0x01, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; + +static const uchar whatsthis_bits[] = { + 0x01, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x05, 0xf0, 0x07, 0x00, + 0x09, 0x18, 0x0e, 0x00, 0x11, 0x1c, 0x0e, 0x00, 0x21, 0x1c, 0x0e, 0x00, + 0x41, 0x1c, 0x0e, 0x00, 0x81, 0x1c, 0x0e, 0x00, 0x01, 0x01, 0x07, 0x00, + 0x01, 0x82, 0x03, 0x00, 0xc1, 0xc7, 0x01, 0x00, 0x49, 0xc0, 0x01, 0x00, + 0x95, 0xc0, 0x01, 0x00, 0x93, 0xc0, 0x01, 0x00, 0x21, 0x01, 0x00, 0x00, + 0x20, 0xc1, 0x01, 0x00, 0x40, 0xc2, 0x01, 0x00, 0x40, 0x02, 0x00, 0x00, + 0x80, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, }; +static const uchar whatsthism_bits[] = { + 0x01, 0x00, 0x00, 0x00, 0x03, 0xf0, 0x07, 0x00, 0x07, 0xf8, 0x0f, 0x00, + 0x0f, 0xfc, 0x1f, 0x00, 0x1f, 0x3e, 0x1f, 0x00, 0x3f, 0x3e, 0x1f, 0x00, + 0x7f, 0x3e, 0x1f, 0x00, 0xff, 0x3e, 0x1f, 0x00, 0xff, 0x9d, 0x0f, 0x00, + 0xff, 0xc3, 0x07, 0x00, 0xff, 0xe7, 0x03, 0x00, 0x7f, 0xe0, 0x03, 0x00, + 0xf7, 0xe0, 0x03, 0x00, 0xf3, 0xe0, 0x03, 0x00, 0xe1, 0xe1, 0x03, 0x00, + 0xe0, 0xe1, 0x03, 0x00, 0xc0, 0xe3, 0x03, 0x00, 0xc0, 0xe3, 0x03, 0x00, + 0x80, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, }; + +static const uchar busy_bits[] = { + 0x01, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, + 0x09, 0x00, 0x00, 0x00, 0x11, 0x00, 0x00, 0x00, 0x21, 0x00, 0x00, 0x00, + 0x41, 0xe0, 0xff, 0x00, 0x81, 0x20, 0x80, 0x00, 0x01, 0xe1, 0xff, 0x00, + 0x01, 0x42, 0x40, 0x00, 0xc1, 0x47, 0x40, 0x00, 0x49, 0x40, 0x55, 0x00, + 0x95, 0x80, 0x2a, 0x00, 0x93, 0x00, 0x15, 0x00, 0x21, 0x01, 0x0a, 0x00, + 0x20, 0x01, 0x11, 0x00, 0x40, 0x82, 0x20, 0x00, 0x40, 0x42, 0x44, 0x00, + 0x80, 0x41, 0x4a, 0x00, 0x00, 0x40, 0x55, 0x00, 0x00, 0xe0, 0xff, 0x00, + 0x00, 0x20, 0x80, 0x00, 0x00, 0xe0, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}; +static const uchar busym_bits[] = { + 0x01, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, + 0x0f, 0x00, 0x00, 0x00, 0x1f, 0x00, 0x00, 0x00, 0x3f, 0x00, 0x00, 0x00, + 0x7f, 0xe0, 0xff, 0x00, 0xff, 0xe0, 0xff, 0x00, 0xff, 0xe1, 0xff, 0x00, + 0xff, 0xc3, 0x7f, 0x00, 0xff, 0xc7, 0x7f, 0x00, 0x7f, 0xc0, 0x7f, 0x00, + 0xf7, 0x80, 0x3f, 0x00, 0xf3, 0x00, 0x1f, 0x00, 0xe1, 0x01, 0x0e, 0x00, + 0xe0, 0x01, 0x1f, 0x00, 0xc0, 0x83, 0x3f, 0x00, 0xc0, 0xc3, 0x7f, 0x00, + 0x80, 0xc1, 0x7f, 0x00, 0x00, 0xc0, 0x7f, 0x00, 0x00, 0xe0, 0xff, 0x00, + 0x00, 0xe0, 0xff, 0x00, 0x00, 0xe0, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}; + +// 16 x 16 +static const uchar openhand_bits[] = { + 0x80,0x01,0x58,0x0e,0x64,0x12,0x64,0x52,0x48,0xb2,0x48,0x92, + 0x16,0x90,0x19,0x80,0x11,0x40,0x02,0x40,0x04,0x40,0x04,0x20, + 0x08,0x20,0x10,0x10,0x20,0x10,0x00,0x00}; +static const uchar openhandm_bits[] = { + 0x80,0x01,0xd8,0x0f,0xfc,0x1f,0xfc,0x5f,0xf8,0xff,0xf8,0xff, + 0xfe,0xff,0xff,0xff,0xff,0x7f,0xfe,0x7f,0xfc,0x7f,0xfc,0x3f, + 0xf8,0x3f,0xf0,0x1f,0xe0,0x1f,0x00,0x00}; +static const uchar closedhand_bits[] = { + 0x00,0x00,0x00,0x00,0x00,0x00,0xb0,0x0d,0x48,0x32,0x08,0x50, + 0x10,0x40,0x18,0x40,0x04,0x40,0x04,0x20,0x08,0x20,0x10,0x10, + 0x20,0x10,0x20,0x10,0x00,0x00,0x00,0x00}; +static const uchar closedhandm_bits[] = { + 0x00,0x00,0x00,0x00,0x00,0x00,0xb0,0x0d,0xf8,0x3f,0xf8,0x7f, + 0xf0,0x7f,0xf8,0x7f,0xfc,0x7f,0xfc,0x3f,0xf8,0x3f,0xf0,0x1f, + 0xe0,0x1f,0xe0,0x1f,0x00,0x00,0x00,0x00}; + +#endif + +void QWSServerPrivate::initializeCursor() +{ + Q_Q(QWSServer); + // setup system cursors +#ifndef QT_NO_QWS_CURSOR +// qt_screen->initCursor(sharedram + ramlen,true); + + // default cursor + cursor = 0; + setCursor(QWSCursor::systemCursor(Qt::ArrowCursor)); +#endif + q->sendMouseEvent(QPoint(swidth/2, sheight/2), 0); +} + +void QWSServerPrivate::setCursor(QWSCursor *curs) +{ +#ifdef QT_NO_QWS_CURSOR + Q_UNUSED(curs); +#else + if (cursor == curs) + return; + + cursor = curs; + + if (!haveviscurs || !curs) + curs = QWSCursor::systemCursor(Qt::BlankCursor); + + if (qt_screencursor) { + qt_screencursor->set(curs->image(), + curs->hotSpot().x(), + curs->hotSpot().y()); + } +#endif +} + +#ifndef QT_NO_QWS_CURSOR +static void cleanupSystemCursorTable() +{ + for (int i = 0; i <= Qt::LastCursor; i++) + if (systemCursorTable[i]) { + delete systemCursorTable[i]; + systemCursorTable[i] = 0; + } +} +#endif + +void QWSCursor::createSystemCursor(int id) +{ +#ifdef QT_NO_QWS_CURSOR + Q_UNUSED(id); +#else + if (!systemCursorTableInit) { + for (int i = 0; i <= Qt::LastCursor; i++) + systemCursorTable[i] = 0; + qAddPostRoutine(cleanupSystemCursorTable); + systemCursorTableInit = true; + } + switch (id) { + // 16x16 cursors + case Qt::ArrowCursor: + systemCursorTable[Qt::ArrowCursor] = + new QWSCursor(cur_arrow_bits, mcur_arrow_bits, 16, 16, 0, 0); + break; + + case Qt::UpArrowCursor: + systemCursorTable[Qt::UpArrowCursor] = + new QWSCursor(cur_up_arrow_bits, mcur_up_arrow_bits, 16, 16, 7, 0); + break; + + case Qt::CrossCursor: + systemCursorTable[Qt::CrossCursor] = + new QWSCursor(cur_cross_bits, mcur_cross_bits, 16, 16, 7, 7); + break; + + case Qt::IBeamCursor: + systemCursorTable[Qt::IBeamCursor] = + new QWSCursor(cur_ibeam_bits, mcur_ibeam_bits, 16, 16, 7, 7); + break; + + case Qt::SizeVerCursor: + systemCursorTable[Qt::SizeVerCursor] = + new QWSCursor(cur_ver_bits, mcur_ver_bits, 16, 16, 7, 7); + break; + + case Qt::SizeHorCursor: + systemCursorTable[Qt::SizeHorCursor] = + new QWSCursor(cur_hor_bits, mcur_hor_bits, 16, 16, 7, 7); + break; + + case Qt::SizeBDiagCursor: + systemCursorTable[Qt::SizeBDiagCursor] = + new QWSCursor(cur_bdiag_bits, mcur_bdiag_bits, 16, 16, 7, 7); + break; + + case Qt::SizeFDiagCursor: + systemCursorTable[Qt::SizeFDiagCursor] = + new QWSCursor(cur_fdiag_bits, mcur_fdiag_bits, 16, 16, 7, 7); + break; + + case Qt::BlankCursor: + systemCursorTable[Qt::BlankCursor] = + new QWSCursor(0, 0, 0, 0, 0, 0); + break; + + // 20x20 cursors + case Qt::ForbiddenCursor: + systemCursorTable[Qt::ForbiddenCursor] = + new QWSCursor(forbidden_bits, forbiddenm_bits, 20, 20, 10, 10); + break; + + // 32x32 cursors + case Qt::WaitCursor: + systemCursorTable[Qt::WaitCursor] = + new QWSCursor(wait_data_bits, wait_mask_bits, 32, 32, 15, 15); + break; + + case Qt::SplitVCursor: + systemCursorTable[Qt::SplitVCursor] = + new QWSCursor(vsplit_bits, vsplitm_bits, 32, 32, 15, 15); + break; + + case Qt::SplitHCursor: + systemCursorTable[Qt::SplitHCursor] = + new QWSCursor(hsplit_bits, hsplitm_bits, 32, 32, 15, 15); + break; + + case Qt::SizeAllCursor: + systemCursorTable[Qt::SizeAllCursor] = + new QWSCursor(size_all_data_bits, size_all_mask_bits, 32, 32, 15, 15); + break; + + case Qt::PointingHandCursor: + systemCursorTable[Qt::PointingHandCursor] = + new QWSCursor(phand_bits, phandm_bits, 32, 32, 0, 0); + break; + + case Qt::WhatsThisCursor: + systemCursorTable[Qt::WhatsThisCursor] = + new QWSCursor(whatsthis_bits, whatsthism_bits, 32, 32, 0, 0); + break; + case Qt::BusyCursor: + systemCursorTable[Qt::BusyCursor] = + new QWSCursor(busy_bits, busym_bits, 32, 32, 0, 0); + break; + + case Qt::OpenHandCursor: + systemCursorTable[Qt::OpenHandCursor] = + new QWSCursor(openhand_bits, openhandm_bits, 16, 16, 8, 8); + break; + case Qt::ClosedHandCursor: + systemCursorTable[Qt::ClosedHandCursor] = + new QWSCursor(closedhand_bits, closedhandm_bits, 16, 16, 8, 8); + break; + default: + qWarning("Unknown system cursor %d", id); + } +#endif +} + +QWSCursor *QWSCursor::systemCursor(int id) +{ + QWSCursor *cursor = 0; +#ifdef QT_NO_QWS_CURSOR + Q_UNUSED(id); +#else + if (id >= 0 && id <= Qt::LastCursor) { + if (!systemCursorTable[id]) + createSystemCursor(id); + cursor = systemCursorTable[id]; + } + + if (cursor == 0) { + if (!systemCursorTable[Qt::ArrowCursor]) + createSystemCursor(Qt::ArrowCursor); + cursor = systemCursorTable[Qt::ArrowCursor]; + } +#endif + return cursor; +} + +void QWSCursor::set(const uchar *data, const uchar *mask, + int width, int height, int hx, int hy) +{ +#ifdef QT_NO_QWS_CURSOR + Q_UNUSED(data); + Q_UNUSED(mask); + Q_UNUSED(width); + Q_UNUSED(height); + Q_UNUSED(hx); + Q_UNUSED(hy); +#else + hot.setX(hx); + hot.setY(hy); + + cursor = QImage(width,height, QImage::Format_Indexed8); + + if (!width || !height || !data || !mask) + return; + + cursor.setNumColors(3); + cursor.setColor(0, 0xff000000); + cursor.setColor(1, 0xffffffff); + cursor.setColor(2, 0x00000000); + + int bytesPerLine = (width + 7) / 8; + int p = 0; + int d, m; + + int x = -1, w = 0; + + uchar *cursor_data = cursor.bits(); + int bpl = cursor.bytesPerLine(); + for (int i = 0; i < height; i++) + { + for (int j = 0; j < bytesPerLine; j++, data++, mask++) + { + for (int b = 0; b < 8 && j*8+b < width; b++) + { + d = *data & (1 << b); + m = *mask & (1 << b); + if (d && m) p = 0; + else if (!d && m) p = 1; + else p = 2; + cursor_data[j*8+b] = p; + + // calc region + if (x < 0 && m) + x = j*8+b; + else if (x >= 0 && !m) { + x = -1; + w = 0; + } + if (m) + w++; + } + } + if (x >= 0) { + x = -1; + w = 0; + } + cursor_data += bpl; + } + + if (qt_screencursor && qt_screencursor->supportsAlphaCursor()) + createDropShadow(5, 2); +#endif +} + +// now we're really silly +void QWSCursor::createDropShadow(int dropx, int dropy) +{ + //#### +#if 1 || defined(QT_NO_QWS_CURSOR) || defined(QT_NO_QWS_ALHPA_CURSOR) + Q_UNUSED(dropx); + Q_UNUSED(dropy); +#else + if (cursor.width() + dropx > 64 || cursor.height() + dropy > 64) + return; + + if (!cursor.hasAlphaBuffer()) { + cursor.setAlphaBuffer(true); + + const int nblur=4; + const int darkness=140; + + QImage drop(cursor.width()+dropx+nblur, cursor.height()+dropy+nblur, 8, 18); + drop.setColor(0, 0xff000000); // bg (black) + drop.setColor(1, 0xffffffff); // fg (white) + for (int i=0; i<16; i++) { + drop.setColor(2+i, (darkness*i/16)<<24); + } + drop.fill(2); // all trans + QImage drop2 = drop.copy(); + + int cp; + + // made solid shadow + for (int row = 0; row < cursor.height(); row++) { + for (int col = 0; col < cursor.width(); col++) { + cp = cursor.pixelIndex(col, row); + if (cp != 2) + drop.setPixel(col+dropx, row+dropy, 17); + } + } + + // blur shadow + for (int blur=0; blur<nblur; blur++) { + QImage& to((blur&1)?drop:drop2); + QImage& from((blur&1)?drop2:drop); + for (int row = 1; row < drop.height()-1; row++) { + for (int col = 1; col < drop.width()-1; col++) { + int t=0; + for (int dx=-1; dx<=1; dx++) { + for (int dy=-1; dy<=1; dy++) { + t += from.pixelIndex(col+dx,row+dy)-2; + } + } + to.setPixel(col,row,2+t/9); + } + } + } + + // copy cursor + for (int row = 0; row < cursor.height(); row++) { + for (int col = 0; col < cursor.width(); col++) { + cp = cursor.pixelIndex(col, row); + if (cp != 2) + drop.setPixel(col, row, cp); + } + } + + cursor = drop; + } +#endif +} + +QT_END_NAMESPACE diff --git a/src/gui/embedded/qwscursor_qws.h b/src/gui/embedded/qwscursor_qws.h new file mode 100644 index 0000000..6d19c31 --- /dev/null +++ b/src/gui/embedded/qwscursor_qws.h @@ -0,0 +1,83 @@ +/**************************************************************************** +** +** 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$ +** +****************************************************************************/ + +#ifndef QWSCURSOR_QWS_H +#define QWSCURSOR_QWS_H + +#include <QtGui/qimage.h> +#include <QtGui/qregion.h> + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +QT_MODULE(Gui) + +class QWSCursor +{ +public: + QWSCursor() {} + QWSCursor(const uchar *data, const uchar *mask, int width, int height, + int hotX, int hotY) + { set(data, mask, width, height, hotX, hotY); } + + void set(const uchar *data, const uchar *mask, + int width, int height, int hotX, int hotY); + + QPoint hotSpot() const { return hot; } + QImage &image() { return cursor; } + + static QWSCursor *systemCursor(int id); + +private: + static void createSystemCursor(int id); + void createDropShadow(int dropx, int dropy); + +private: + QPoint hot; + QImage cursor; +}; + +QT_END_NAMESPACE + +QT_END_HEADER + +#endif // QWSCURSOR_QWS_H diff --git a/src/gui/embedded/qwsdisplay_qws.h b/src/gui/embedded/qwsdisplay_qws.h new file mode 100644 index 0000000..455d46d --- /dev/null +++ b/src/gui/embedded/qwsdisplay_qws.h @@ -0,0 +1,185 @@ +/**************************************************************************** +** +** 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$ +** +****************************************************************************/ + +#ifndef QWSDISPLAY_QWS_H +#define QWSDISPLAY_QWS_H + +#include <QtCore/qobject.h> +#include <QtCore/qbytearray.h> +#include <QtGui/qregion.h> +#include <QtGui/qimage.h> +#include <QtGui/qwindowdefs.h> +#include <QtCore/qlist.h> + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +QT_MODULE(Gui) + +class QWSEvent; +class QWSMouseEvent; +class QWSQCopMessageEvent; +class QVariant; +class QLock; + +class QWSWindowInfo +{ + +public: + + int winid; + unsigned int clientid; + QString name; + +}; + +#define QT_QWS_PROPERTY_CONVERTSELECTION 999 +#define QT_QWS_PROPERTY_WINDOWNAME 998 +#define QT_QWS_PROPERTY_MARKEDTEXT 997 + +class QWSDisplay; +extern Q_GUI_EXPORT QWSDisplay *qt_fbdpy; + +class Q_GUI_EXPORT QWSDisplay +{ +public: + QWSDisplay(); + ~QWSDisplay(); + + static QWSDisplay* instance() { return qt_fbdpy; } + + bool eventPending() const; + QWSEvent *getEvent(); +// QWSRegionManager *regionManager() const; + + uchar* frameBuffer() const; + int width() const; + int height() const; + int depth() const; + int pixmapDepth() const; + bool supportsDepth(int) const; + + uchar *sharedRam() const; + int sharedRamSize() const; + +#ifndef QT_NO_QWS_PROPERTIES + void addProperty(int winId, int property); + void setProperty(int winId, int property, int mode, const QByteArray &data); + void setProperty(int winId, int property, int mode, const char * data); + void removeProperty(int winId, int property); + bool getProperty(int winId, int property, char *&data, int &len); +#endif // QT_NO_QWS_PROPERTIES + + QList<QWSWindowInfo> windowList(); + int windowAt(const QPoint &); + + void setIdentity(const QString &appName); + void nameRegion(int winId, const QString& n, const QString &c); + void requestRegion(int winId, const QString &surfacekey, + const QByteArray &surfaceData, + const QRegion ®ion); + void repaintRegion(int winId, int windowFlags, bool opaque, QRegion); + void moveRegion(int winId, int dx, int dy); + void destroyRegion(int winId); + void requestFocus(int winId, bool get); + void setAltitude(int winId, int altitude, bool fixed = false); + void setOpacity(int winId, int opacity); + int takeId(); + void setSelectionOwner(int winId, const QTime &time); + void convertSelection(int winId, int selectionProperty, const QString &mimeTypes); + void defineCursor(int id, const QBitmap &curs, const QBitmap &mask, + int hotX, int hotY); + void destroyCursor(int id); + void selectCursor(QWidget *w, unsigned int id); + void setCursorPosition(int x, int y); + void grabMouse(QWidget *w, bool grab); + void grabKeyboard(QWidget *w, bool grab); + void playSoundFile(const QString&); + void registerChannel(const QString &channel); + void sendMessage(const QString &channel, const QString &msg, + const QByteArray &data); + void flushCommands(); +#ifndef QT_NO_QWS_INPUTMETHODS + void sendIMUpdate(int type, int winId, int widgetid); + void resetIM(); + void sendIMResponse(int winId, int property, const QVariant &result); + void sendIMMouseEvent(int index, bool isPress); +#endif + QWSQCopMessageEvent* waitForQCopResponse(); + void sendFontCommand(int type, const QByteArray &fontName); + + void setWindowCaption(QWidget *w, const QString &); + + // Lock display for access only by this process + static bool initLock(const QString &filename, bool create = false); + static bool grabbed(); + static void grab(); + static void grab(bool write); + static void ungrab(); + + static void setTransformation(int transformation, int screenNo = -1); + static void setRawMouseEventFilter(void (*filter)(QWSMouseEvent *)); + +private: + friend int qt_fork_qapplication(); + friend void qt_app_reinit( const QString& newAppName ); + friend class QApplication; + friend class QCopChannel; + friend class QWSEmbedWidget; + friend class QWSEmbedWidgetPrivate; + class Data; + friend class Data; + Data *d; + + friend class QWSMemorySurface; + friend class QWSOnScreenSurface; + friend class QWSDirectPainterSurface; + int getPropertyLen; + char *getPropertyData; + static QLock *lock; +}; + +QT_END_NAMESPACE + +QT_END_HEADER + +#endif // QWSDISPLAY_QWS_H diff --git a/src/gui/embedded/qwsdisplay_qws_p.h b/src/gui/embedded/qwsdisplay_qws_p.h new file mode 100644 index 0000000..819b826 --- /dev/null +++ b/src/gui/embedded/qwsdisplay_qws_p.h @@ -0,0 +1,161 @@ +/**************************************************************************** +** +** 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$ +** +****************************************************************************/ + +#ifndef QWSDISPLAY_QWS_P_H +#define QWSDISPLAY_QWS_P_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +#include "qwsdisplay_qws.h" +#include "qwssocket_qws.h" +#include "qwsevent_qws.h" +#include <private/qwssharedmemory_p.h> +#include "qwscommand_qws_p.h" +#include "qwslock_p.h" + +QT_BEGIN_NAMESPACE + +class QWSDisplay::Data +{ +public: + Data(QObject* parent, bool singleProcess = false); + ~Data(); + + void flush(); + + bool queueNotEmpty(); + QWSEvent *dequeue(); + QWSEvent *peek(); + + bool directServerConnection(); + void fillQueue(); +#ifndef QT_NO_QWS_MULTIPROCESS + void connectToPipe(); + void waitForConnection(); + void waitForPropertyReply(); + void waitForRegionAck(int winId); + void waitForRegionEvents(int winId, bool ungrabDisplay); + bool hasPendingRegionEvents() const; +#endif + void waitForCreation(); +#ifndef QT_NO_COP + void waitForQCopResponse(); +#endif + void init(); + void reinit( const QString& newAppName ); + void create(int n = 1); + + void flushCommands(); + void sendCommand(QWSCommand & cmd); + void sendSynchronousCommand(QWSCommand & cmd); + + QWSEvent *readMore(); + + int takeId(); + + void setMouseFilter(void (*filter)(QWSMouseEvent*)); + + //####public data members + +// QWSRegionManager *rgnMan; + uchar *sharedRam; +#ifndef QT_NO_QWS_MULTIPROCESS + QWSSharedMemory shm; +#endif + int sharedRamSize; + +#ifndef QT_NO_QWS_MULTIPROCESS + static QWSLock *clientLock; + + static bool lockClient(QWSLock::LockType, int timeout = -1); + static void unlockClient(QWSLock::LockType); + static bool waitClient(QWSLock::LockType, int timeout = -1); + static QWSLock* getClientLock(); +#endif // QT_NO_QWS_MULTIPROCESS + +private: +#ifndef QT_NO_QWS_MULTIPROCESS + QWSSocket *csocket; +#endif + QList<QWSEvent*> queue; + +#if 0 + void debugQueue() { + for (int i = 0; i < queue.size(); ++i) { + QWSEvent *e = queue.at(i); + qDebug( " ev %d type %d sl %d rl %d", i, e->type, e->simpleLen, e->rawLen); + } + } +#endif + + QWSConnectedEvent* connected_event; + QWSMouseEvent* mouse_event; + int region_events_count; + int mouse_state; + int mouse_winid; + QPoint region_offset; + int region_offset_window; +#ifndef QT_NO_COP + QWSQCopMessageEvent *qcop_response; +#endif + QWSEvent* current_event; + QList<int> unused_identifiers; +#ifdef QAPPLICATION_EXTRA_DEBUG + int mouse_event_count; +#endif + void (*mouseFilter)(QWSMouseEvent *); + + enum { VariableEvent=-1 }; + +}; + +QT_END_NAMESPACE + +#endif // QWSDISPLAY_QWS_P_H diff --git a/src/gui/embedded/qwsembedwidget.cpp b/src/gui/embedded/qwsembedwidget.cpp new file mode 100644 index 0000000..a449c65 --- /dev/null +++ b/src/gui/embedded/qwsembedwidget.cpp @@ -0,0 +1,227 @@ +/**************************************************************************** +** +** 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 "qwsembedwidget.h" + +#ifndef QT_NO_QWSEMBEDWIDGET + +#include <qwsdisplay_qws.h> +#include <private/qwidget_p.h> +#include <private/qwsdisplay_qws_p.h> +#include <private/qwscommand_qws_p.h> + +QT_BEGIN_NAMESPACE + +// TODO: +// Must remove window decorations from the embedded window +// Focus In/Out, Keyboard/Mouse... +// +// BUG: what if my parent change parent? + +class QWSEmbedWidgetPrivate : public QWidgetPrivate +{ + Q_DECLARE_PUBLIC(QWSEmbedWidget); + +public: + QWSEmbedWidgetPrivate(int winId); + void updateWindow(); + void resize(const QSize &size); + + QWidget *window; + WId windowId; + WId embeddedId; +}; + +QWSEmbedWidgetPrivate::QWSEmbedWidgetPrivate(int winId) + : window(0), windowId(0), embeddedId(winId) +{ +} + +void QWSEmbedWidgetPrivate::updateWindow() +{ + Q_Q(QWSEmbedWidget); + + QWidget *win = q->window(); + if (win == window) + return; + + if (window) { + window->removeEventFilter(q); + QWSEmbedCommand command; + command.setData(windowId, embeddedId, QWSEmbedEvent::StopEmbed); + QWSDisplay::instance()->d->sendCommand(command); + } + + window = win; + if (!window) + return; + windowId = window->winId(); + + QWSEmbedCommand command; + command.setData(windowId, embeddedId, QWSEmbedEvent::StartEmbed); + QWSDisplay::instance()->d->sendCommand(command); + window->installEventFilter(q); + q->installEventFilter(q); +} + +void QWSEmbedWidgetPrivate::resize(const QSize &size) +{ + if (!window) + return; + + Q_Q(QWSEmbedWidget); + + QWSEmbedCommand command; + command.setData(windowId, embeddedId, QWSEmbedEvent::Region, + QRect(q->mapToGlobal(QPoint(0, 0)), size)); + QWSDisplay::instance()->d->sendCommand(command); +} + +/*! + \class QWSEmbedWidget + \since 4.2 + \ingroup qws + \ingroup advanced + + \brief The QWSEmbedWidget class enables embedded top-level widgets + in Qt for Embedded Linux. + + Note that this class is only available in \l{Qt for Embedded Linux}. + + QWSEmbedWidget inherits QWidget and acts as any other widget, but + in addition it is capable of embedding another top-level widget. + + An example of use is when painting directly onto the screen using + the QDirectPainter class. Then the reserved region can be embedded + into an instance of the QWSEmbedWidget class, providing for + example event handling and size policies for the reserved region. + + All that is required to embed a top-level widget is its window ID. + + \sa {Qt for Embedded Linux Architecture} +*/ + +/*! + Constructs a widget with the given \a parent, embedding the widget + identified by the given window \a id. +*/ +QWSEmbedWidget::QWSEmbedWidget(WId id, QWidget *parent) + : QWidget(*new QWSEmbedWidgetPrivate(id), parent, 0) +{ + Q_D(QWSEmbedWidget); + d->updateWindow(); +} + +/*! + Destroys this widget. +*/ +QWSEmbedWidget::~QWSEmbedWidget() +{ + Q_D(QWSEmbedWidget); + if (!d->window) + return; + + QWSEmbedCommand command; + command.setData(d->windowId, d->embeddedId, QWSEmbedEvent::StopEmbed); + QWSDisplay::instance()->d->sendCommand(command); +} + +/*! + \reimp +*/ +bool QWSEmbedWidget::eventFilter(QObject *object, QEvent *event) +{ + Q_D(QWSEmbedWidget); + if (object == d->window && event->type() == QEvent::Move) + resizeEvent(0); + else if (object == this && event->type() == QEvent::Hide) + d->resize(QSize()); + return QWidget::eventFilter(object, event); +} + +/*! + \reimp +*/ +void QWSEmbedWidget::changeEvent(QEvent *event) +{ + Q_D(QWSEmbedWidget); + if (event->type() == QEvent::ParentChange) + d->updateWindow(); +} + +/*! + \reimp +*/ +void QWSEmbedWidget::resizeEvent(QResizeEvent*) +{ + Q_D(QWSEmbedWidget); + d->resize(rect().size()); +} + +/*! + \reimp +*/ +void QWSEmbedWidget::moveEvent(QMoveEvent*) +{ + resizeEvent(0); +} + +/*! + \reimp +*/ +void QWSEmbedWidget::hideEvent(QHideEvent*) +{ + Q_D(QWSEmbedWidget); + d->resize(QSize()); +} + +/*! + \reimp +*/ +void QWSEmbedWidget::showEvent(QShowEvent*) +{ + Q_D(QWSEmbedWidget); + d->resize(rect().size()); +} + +QT_END_NAMESPACE + +#endif // QT_NO_QWSEMBEDWIDGET diff --git a/src/gui/embedded/qwsembedwidget.h b/src/gui/embedded/qwsembedwidget.h new file mode 100644 index 0000000..6badf76 --- /dev/null +++ b/src/gui/embedded/qwsembedwidget.h @@ -0,0 +1,82 @@ +/**************************************************************************** +** +** 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$ +** +****************************************************************************/ + +#ifndef QWSEMBEDWIDGET_H +#define QWSEMBEDWIDGET_H + +#include <QtGui/qwidget.h> + +#ifndef QT_NO_QWSEMBEDWIDGET + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +QT_MODULE(Gui) + +class QWSEmbedWidgetPrivate; + +class Q_GUI_EXPORT QWSEmbedWidget : public QWidget +{ + Q_OBJECT + +public: + QWSEmbedWidget(WId winId, QWidget *parent = 0); + ~QWSEmbedWidget(); + +protected: + bool eventFilter(QObject *object, QEvent *event); + void changeEvent(QEvent *event); + void resizeEvent(QResizeEvent *event); + void moveEvent(QMoveEvent *event); + void hideEvent(QHideEvent *event); + void showEvent(QShowEvent *event); + +private: + Q_DECLARE_PRIVATE(QWSEmbedWidget) +}; + +QT_END_NAMESPACE + +QT_END_HEADER + +#endif // QT_NO_QWSEMBEDWIDGET +#endif // QWSEMBEDWIDGET_H diff --git a/src/gui/embedded/qwsevent_qws.cpp b/src/gui/embedded/qwsevent_qws.cpp new file mode 100644 index 0000000..e23eacd --- /dev/null +++ b/src/gui/embedded/qwsevent_qws.cpp @@ -0,0 +1,216 @@ +/**************************************************************************** +** +** 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 "qwsevent_qws.h" + +QT_BEGIN_NAMESPACE + +QWSEvent *QWSEvent::factory(int type) +{ + QWSEvent *event = 0; + switch (type) { + case QWSEvent::Connected: + event = new QWSConnectedEvent; + break; + case QWSEvent::MaxWindowRect: + event = new QWSMaxWindowRectEvent; + break; + case QWSEvent::Mouse: + event = new QWSMouseEvent; + break; + case QWSEvent::Focus: + event = new QWSFocusEvent; + break; + case QWSEvent::Key: + event = new QWSKeyEvent; + break; + case QWSEvent::Region: + event = new QWSRegionEvent; + break; + case QWSEvent::Creation: + event = new QWSCreationEvent; + break; +#ifndef QT_NO_QWS_PROPERTIES + case QWSEvent::PropertyNotify: + event = new QWSPropertyNotifyEvent; + break; + case QWSEvent::PropertyReply: + event = new QWSPropertyReplyEvent; + break; +#endif // QT_NO_QWS_PROPERTIES + case QWSEvent::SelectionClear: + event = new QWSSelectionClearEvent; + break; + case QWSEvent::SelectionRequest: + event = new QWSSelectionRequestEvent; + break; + case QWSEvent::SelectionNotify: + event = new QWSSelectionNotifyEvent; + break; +#ifndef QT_NO_COP + case QWSEvent::QCopMessage: + event = new QWSQCopMessageEvent; + break; +#endif + case QWSEvent::WindowOperation: + event = new QWSWindowOperationEvent; + break; + +#ifndef QT_NO_QWS_INPUTMETHODS + case QWSEvent::IMEvent: + event = new QWSIMEvent; + break; + case QWSEvent::IMQuery: + event = new QWSIMQueryEvent; + break; + case QWSEvent::IMInit: + event = new QWSIMInitEvent; + break; +#endif +#ifndef QT_NO_QWSEMBEDWIDGET + case QWSEvent::Embed: + event = new QWSEmbedEvent; + break; +#endif + case QWSEvent::Font: + event = new QWSFontEvent; + break; + case QWSEvent::ScreenTransformation: + event = new QWSScreenTransformationEvent; + break; + default: + qCritical("QWSEvent::factory() : Unknown event type %08x!", type); + } + return event; +} + +/*! + \class QWSEvent + \ingroup qws + + \brief The QWSEvent class encapsulates an event in Qt for Embedded Linux. + + When running a \l{Qt for Embedded Linux} application, it either runs as a + server or connects to an existing server. All system generated + events are passed to the server application which then propagates + the event to the appropriate client. + + 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, and returns its ID to the server upon + request. Finally, the server forwards the event, encapsulated by + an instance of the QWSEvent class, to the appropriate client. + + \image qt-embedded-client.png + + The server communicates with the client applications over the UNIX + domain socket. You can retrieve direct access to all the events a + client receives from the server, by reimplementing QApplication's + \l {QApplication::}{qwsEventFilter()} function. + + QWSEvent provides the \l Type enum specifying the origin of the + event. Internally, each type is represented by a QWSEvent + subclass, e.g., \c QWSKeyEvent. + + \sa QWSServer, QWSClient, {Qt for Embedded Linux Architecture} +*/ + +/*! + \enum QWSEvent::Type + + This enum describes the origin of the event. + + \value NoEvent No event has occurred. + \value Connected An application has connected to the server. + \value Mouse A mouse button is pressed or released, or the mouse cursor is moved. + See also \l{Qt for Embedded Linux Pointer Handling}. + \value Focus A window has lost or received focus. + \value Key A key is pressed or released. See also \l{Qt for Embedded Linux Character Input}. + \value Region A region has changed. + \value Creation The server has created an ID, typically for a window. + \value PropertyNotify A property has changed. + \value PropertyReply The server is responding to a request for a property's value. + \value SelectionClear A selection is deleted. + \value SelectionRequest The server has queried for a selection. + \value SelectionNotify A new selection has been created. + \value MaxWindowRect The server has changed the maximum window for an application. + \value QCopMessage A new Qt Cop message has appeared. See also QCopChannel + \value WindowOperation A window operation, e.g. resizing, has occurred. + \value IMEvent An input method has been used to enter text for languages with + non-Latin alphabets. See also QWSInputMethod. + \value IMQuery An input method query for a specified property has occurred. + See also QWSInputMethod. + \value NEvent The number of events has changed. + \value Embed An event used internally to implement embedded windows. See also + QWSEmbedWidget. + \value ScreenTransformation An event used internally to notify the client processes + that the screen has changed for example, rotation, etc. + \omitvalue Font + \omitvalue IMInit +*/ + +/*! + \fn QWSMouseEvent *QWSEvent::asMouse() + \internal +*/ + +/*! + \fn int QWSEvent::window() + \internal +*/ + +/*! + \fn int QWSEvent::window() const + \internal +*/ + +/*! + \fn QWSEvent *QWSEvent::factory(int type) + \internal +*/ + +/*! + \fn QWSEvent::QWSEvent( int t, int len, char * ptr) + \internal +*/ + +QT_END_NAMESPACE diff --git a/src/gui/embedded/qwsevent_qws.h b/src/gui/embedded/qwsevent_qws.h new file mode 100644 index 0000000..f231db2 --- /dev/null +++ b/src/gui/embedded/qwsevent_qws.h @@ -0,0 +1,459 @@ +/**************************************************************************** +** +** 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$ +** +****************************************************************************/ + +#ifndef QWSEVENT_QWS_H +#define QWSEVENT_QWS_H + +#include <QtGui/qwsutils_qws.h> +#include <QtGui/qwsprotocolitem_qws.h> +#include <QtCore/qrect.h> +#include <QtGui/qregion.h> +#include <QtCore/qvector.h> + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +QT_MODULE(Gui) + +struct QWSMouseEvent; + +struct QWSEvent : QWSProtocolItem { + + QWSEvent(int t, int len, char *ptr) : QWSProtocolItem(t,len,ptr) {} + + + + enum Type { + NoEvent, + Connected, + Mouse, + Focus, + Key, + Region, + Creation, + PropertyNotify, + PropertyReply, + SelectionClear, + SelectionRequest, + SelectionNotify, + MaxWindowRect, + QCopMessage, + WindowOperation, + IMEvent, + IMQuery, + IMInit, + Embed, + Font, + ScreenTransformation, + NEvent + }; + + QWSMouseEvent *asMouse() + { return type == Mouse ? reinterpret_cast<QWSMouseEvent*>(this) : 0; } + int window() { return *(reinterpret_cast<int*>(simpleDataPtr)); } + int window() const { return *(reinterpret_cast<int*>(simpleDataPtr)); } + static QWSEvent *factory(int type); +}; + + +//All events must start with windowID + +struct QWSConnectedEvent : QWSEvent { + QWSConnectedEvent() + : QWSEvent(QWSEvent::Connected, sizeof(simpleData), + reinterpret_cast<char*>(&simpleData)) {} + + void setData(const char *d, int len, bool allocateMem = true) { + QWSEvent::setData(d, len, allocateMem); + display = reinterpret_cast<char*>(rawDataPtr); + } + + struct SimpleData { + int window; + int len; + int clientId; + int servershmid; + } simpleData; + + char *display; +}; + +struct QWSMaxWindowRectEvent : QWSEvent { + QWSMaxWindowRectEvent() + : QWSEvent(MaxWindowRect, sizeof(simpleData), reinterpret_cast<char*>(&simpleData)) { } + struct SimpleData { + int window; + QRect rect; + } simpleData; +}; + +struct QWSMouseEvent : QWSEvent { + QWSMouseEvent() + : QWSEvent(QWSEvent::Mouse, sizeof(simpleData), + reinterpret_cast<char*>(&simpleData)) {} + struct SimpleData { + int window; + int x_root, y_root, state, delta; + int time; // milliseconds + } simpleData; +}; + +struct QWSFocusEvent : QWSEvent { + QWSFocusEvent() + : QWSEvent(QWSEvent::Focus, sizeof(simpleData), reinterpret_cast<char*>(&simpleData)) + { memset(reinterpret_cast<char*>(&simpleData),0,sizeof(simpleData)); } + struct SimpleData { + int window; + uint get_focus:1; + } simpleData; +}; + +struct QWSKeyEvent: QWSEvent { + QWSKeyEvent() + : QWSEvent(QWSEvent::Key, sizeof(simpleData), + reinterpret_cast<char*>(&simpleData)) {} + struct SimpleData { + int window; + uint keycode; + Qt::KeyboardModifiers modifiers; + ushort unicode; + uint is_press:1; + uint is_auto_repeat:1; + } simpleData; +}; + + +struct QWSCreationEvent : QWSEvent { + QWSCreationEvent() + : QWSEvent(QWSEvent::Creation, sizeof(simpleData), + reinterpret_cast<char*>(&simpleData)) {} + struct SimpleData { + int objectid; + int count; + } simpleData; +}; + +#ifndef QT_NO_QWS_PROPERTIES +struct QWSPropertyNotifyEvent : QWSEvent { + QWSPropertyNotifyEvent() + : QWSEvent(QWSEvent::PropertyNotify, sizeof(simpleData), + reinterpret_cast<char*>(&simpleData)) {} + enum State { + PropertyNewValue, + PropertyDeleted + }; + struct SimpleData { + int window; + int property; + int state; + } simpleData; +}; +#endif + +struct QWSSelectionClearEvent : QWSEvent { + QWSSelectionClearEvent() + : QWSEvent(QWSEvent::SelectionClear, sizeof(simpleData), + reinterpret_cast<char*>(&simpleData)) {} + struct SimpleData { + int window; + } simpleData; +}; + +struct QWSSelectionRequestEvent : QWSEvent { + QWSSelectionRequestEvent() + : QWSEvent(QWSEvent::SelectionRequest, sizeof(simpleData), + reinterpret_cast<char*>(&simpleData)) {} + struct SimpleData { + int window; + int requestor; // window which wants the selection + int property; // property on requestor into which the selection should be stored, normally QWSProperty::PropSelection + int mimeTypes; // Value is stored in the property mimeType on the requestor window. This value may contain + // multiple mimeTypes separated by ;; where the order reflects the priority + } simpleData; +}; + +struct QWSSelectionNotifyEvent : QWSEvent { + QWSSelectionNotifyEvent() + : QWSEvent(QWSEvent::SelectionNotify, sizeof(simpleData), + reinterpret_cast<char*>(&simpleData)) {} + struct SimpleData { + int window; + int requestor; // the window which wanted the selection and to which this event is sent + int property; // property of requestor in which the data of the selection is stored + int mimeType; // a property on the requestor in which the mime type in which the selection is, is stored + } simpleData; +}; + +//complex events: + +struct QWSRegionEvent : QWSEvent { + QWSRegionEvent() + : QWSEvent(QWSEvent::Region, sizeof(simpleData), + reinterpret_cast<char*>(&simpleData)) + { memset(reinterpret_cast<char*>(&simpleData),0,sizeof(simpleData)); } + + void setData(const char *d, int len, bool allocateMem = true) { + QWSEvent::setData(d, len, allocateMem); + rectangles = reinterpret_cast<QRect*>(rawDataPtr); + } + + void setData(int winId, const QRegion ®ion, uint type) { + const QVector<QRect> rects = region.rects(); + setData(reinterpret_cast<const char*>(rects.constData()), + rects.size() * sizeof(QRect)); + simpleData.window = winId; + simpleData.nrectangles = rects.size(); + simpleData.type = type; +#ifdef QT_QWS_CLIENTBLIT + simpleData.id = 0; +#endif + } + + enum Type {Allocation +#ifdef QT_QWS_CLIENTBLIT + , DirectPaint +#endif + }; + struct SimpleData { + int window; + int nrectangles; +#ifdef QT_QWS_CLIENTBLIT + int id; +#endif + uint type:8; + } simpleData; + + QRect *rectangles; +}; + +#ifndef QT_NO_QWSEMBEDWIDGET +struct QWSEmbedEvent : QWSEvent +{ + QWSEmbedEvent() : QWSEvent(QWSEvent::Embed, sizeof(simpleData), + reinterpret_cast<char*>(&simpleData)) + {} + + enum Type { StartEmbed = 1, StopEmbed = 2, Region = 4 }; + + void setData(const char *d, int len, bool allocateMem = true) { + QWSEvent::setData(d, len, allocateMem); + region.setRects(reinterpret_cast<const QRect *>(rawDataPtr), + simpleData.nrectangles); + } + + void setData(int winId, Type type, const QRegion ® = QRegion()) { + simpleData.window = winId; + simpleData.nrectangles = reg.rects().size(); + simpleData.type = type; + region = reg; + const QVector<QRect> rects = reg.rects(); + QWSEvent::setData(reinterpret_cast<const char*>(rects.data()), + rects.size() * sizeof(QRect)); + } + + struct SimpleData { + int window; + int nrectangles; + Type type; + } simpleData; + + QRegion region; +}; +#endif // QT_NO_QWSEMBEDWIDGET + +#ifndef QT_NO_QWS_PROPERTIES +struct QWSPropertyReplyEvent : QWSEvent { + QWSPropertyReplyEvent() + : QWSEvent(QWSEvent::PropertyReply, sizeof(simpleData), + reinterpret_cast<char*>(&simpleData)) {} + + void setData(const char *d, int len, bool allocateMem = true) { + QWSEvent::setData(d, len, allocateMem); + data = reinterpret_cast<char*>(rawDataPtr); + } + + struct SimpleData { + int window; + int property; + int len; + } simpleData; + char *data; +}; +#endif //QT_NO_QWS_PROPERTIES + +#ifndef QT_NO_COP +struct QWSQCopMessageEvent : QWSEvent { + QWSQCopMessageEvent() + : QWSEvent(QWSEvent::QCopMessage, sizeof(simpleData), + reinterpret_cast<char*>(&simpleData)) + { memset(reinterpret_cast<char*>(&simpleData),0,sizeof(simpleData)); } + + void setData(const char *d, int len, bool allocateMem = true) { + QWSEvent::setData(d, len, allocateMem); + char* p = rawDataPtr; + channel = QByteArray(p, simpleData.lchannel); + p += simpleData.lchannel; + message = QByteArray(p, simpleData.lmessage); + p += simpleData.lmessage; + data = QByteArray(p, simpleData.ldata); + } + + void setDataDirect(const char *d, int len) { + QWSEvent::setData(d, len, false); + deleteRaw = true; + } + + struct SimpleData { + bool is_response; + int lchannel; + int lmessage; + int ldata; + } simpleData; + + QByteArray channel; + QByteArray message; + QByteArray data; +}; + +#endif + +struct QWSWindowOperationEvent : QWSEvent { + QWSWindowOperationEvent() + : QWSEvent(WindowOperation, sizeof(simpleData), reinterpret_cast<char*>(&simpleData)) { } + + enum Operation { Show, Hide, ShowMaximized, ShowNormal, ShowMinimized, Close }; + struct SimpleData { + int window; + Operation op; + } simpleData; +}; + +#ifndef QT_NO_QWS_INPUTMETHODS + + +struct QWSIMEvent : QWSEvent { + QWSIMEvent() + : QWSEvent(IMEvent, sizeof(simpleData), reinterpret_cast<char*>(&simpleData)) + { memset(reinterpret_cast<char*>(&simpleData),0,sizeof(simpleData)); } + + struct SimpleData { + int window; + int replaceFrom; + int replaceLength; + } simpleData; + + void setData(const char *d, int len, bool allocateMem = true) { + QWSEvent::setData(d, len, allocateMem); + streamingData = QByteArray::fromRawData(rawDataPtr, len); + } + QByteArray streamingData; +}; + + +struct QWSIMInitEvent : QWSEvent { + QWSIMInitEvent() + : QWSEvent(IMInit, sizeof(simpleData), reinterpret_cast<char*>(&simpleData)) + { memset(reinterpret_cast<char*>(&simpleData),0,sizeof(simpleData)); } + + struct SimpleData { + int window; + int existence; + } simpleData; + + void setData(const char *d, int len, bool allocateMem = true) { + QWSEvent::setData(d, len, allocateMem); + streamingData = QByteArray::fromRawData(rawDataPtr, len); + } + QByteArray streamingData; +}; + + +struct QWSIMQueryEvent : QWSEvent { + QWSIMQueryEvent() + : QWSEvent(QWSEvent::IMQuery, sizeof(simpleData), + reinterpret_cast<char*>(&simpleData)) {} + + struct SimpleData { + int window; + int property; + } simpleData; + +}; + +#endif + +struct QWSFontEvent : QWSEvent { + QWSFontEvent() + : QWSEvent(QWSEvent::Font, sizeof(simpleData), + reinterpret_cast<char*>(&simpleData)) {} + + enum EventType { + FontRemoved + }; + + void setData(const char *d, int len, bool allocateMem = true) { + QWSEvent::setData(d, len, allocateMem); + fontName = QByteArray::fromRawData(rawDataPtr, len); + } + + struct SimpleData { + uchar type; + } simpleData; + QByteArray fontName; +}; + +struct QWSScreenTransformationEvent : QWSEvent { + QWSScreenTransformationEvent() + : QWSEvent(QWSEvent::ScreenTransformation, sizeof(simpleData), + reinterpret_cast<char*>(&simpleData)) {} + + struct SimpleData { + int screen; + int transformation; + } simpleData; +}; + +QT_END_NAMESPACE + +QT_END_HEADER + +#endif // QWSEVENT_QWS_H diff --git a/src/gui/embedded/qwslock.cpp b/src/gui/embedded/qwslock.cpp new file mode 100644 index 0000000..5fb588c --- /dev/null +++ b/src/gui/embedded/qwslock.cpp @@ -0,0 +1,243 @@ +/**************************************************************************** +** +** 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 "qwslock_p.h" + +#ifndef QT_NO_QWS_MULTIPROCESS + +#include "qwssignalhandler_p.h" + +#include <qglobal.h> +#include <qdebug.h> +#include <stdio.h> +#include <errno.h> +#include <string.h> +#include <sys/types.h> +#include <sys/ipc.h> +#include <sys/sem.h> +#include <sys/time.h> +#include <time.h> +#ifdef Q_OS_LINUX +#include <linux/version.h> +#endif +#include <unistd.h> + +QT_BEGIN_NAMESPACE + +#ifdef QT_NO_SEMAPHORE +#error QWSLock currently requires semaphores +#endif + +#ifndef Q_OS_BSD4 +union semun { + int val; + struct semid_ds *buf; + unsigned short *array; + struct seminfo *__buf; +}; +#endif + +QWSLock::QWSLock() +{ + semId = semget(IPC_PRIVATE, 3, IPC_CREAT | 0666); + + if (semId == -1) { + perror("QWSLock::QWSLock"); + qFatal("Unable to create semaphore"); + } + QWSSignalHandler::instance()->addSemaphore(semId); + + semun semval; + semval.val = 1; + + if (semctl(semId, BackingStore, SETVAL, semval) == -1) { + perror("QWSLock::QWSLock"); + qFatal("Unable to initialize backingstore semaphore"); + } + lockCount[BackingStore] = 0; + + if (semctl(semId, Communication, SETVAL, semval) == -1) { + perror("QWSLock::QWSLock"); + qFatal("Unable to initialize communication semaphore"); + } + lockCount[Communication] = 0; + + semval.val = 0; + if (semctl(semId, RegionEvent, SETVAL, semval) == -1) { + perror("QWSLock::QWSLock"); + qFatal("Unable to initialize region event semaphore"); + } +} + +QWSLock::QWSLock(int id) +{ + semId = id; + QWSSignalHandler::instance()->addSemaphore(semId); + lockCount[0] = lockCount[1] = 0; +} + +QWSLock::~QWSLock() +{ + if (semId == -1) + return; + QWSSignalHandler::instance()->removeSemaphore(semId); +} + +static bool forceLock(int semId, int semNum, int) +{ + int ret; + do { + sembuf sops = { semNum, -1, 0 }; + + // As the BackingStore lock is a mutex, and only one process may own + // the lock, it's safe to use SEM_UNDO. On the other hand, the + // Communication lock is locked by the client but unlocked by the + // server and therefore can't use SEM_UNDO. + if (semNum == QWSLock::BackingStore) + sops.sem_flg |= SEM_UNDO; + + ret = semop(semId, &sops, 1); + if (ret == -1 && errno != EINTR) + qDebug("QWSLock::lock: %s", strerror(errno)); + } while (ret == -1 && errno == EINTR); + + return (ret != -1); +} + +static bool up(int semId, int semNum) +{ + int ret; + do { + sembuf sops = { semNum, 1, 0 }; + ret = semop(semId, &sops, 1); + if (ret == -1 && errno != EINTR) + qDebug("QWSLock::up: %s", strerror(errno)); + } while (ret == -1 && errno == EINTR); + + return (ret != -1); +} + +static bool down(int semId, int semNum) +{ + int ret; + do { + sembuf sops = { semNum, -1, 0 }; + ret = semop(semId, &sops, 1); + if (ret == -1 && errno != EINTR) + qDebug("QWSLock::down: %s", strerror(errno)); + } while (ret == -1 && errno == EINTR); + + return (ret != -1); +} + +static int getValue(int semId, int semNum) +{ + int ret; + do { + ret = semctl(semId, semNum, GETVAL, 0); + if (ret == -1 && errno != EINTR) + qDebug("QWSLock::getValue: %s", strerror(errno)); + } while (ret == -1 && errno == EINTR); + + return ret; +} + +bool QWSLock::lock(LockType type, int timeout) +{ + if (type == RegionEvent) + return up(semId, RegionEvent); + + if (hasLock(type)) { + ++lockCount[type]; + return true; + } + + if (!forceLock(semId, type, timeout)) + return false; + ++lockCount[type]; + return true; +} + +bool QWSLock::hasLock(LockType type) +{ + if (type == RegionEvent) + return (getValue(semId, RegionEvent) == 0); + + return (lockCount[type] > 0); +} + +void QWSLock::unlock(LockType type) +{ + if (type == RegionEvent) { + down(semId, RegionEvent); + return; + } + + if (hasLock(type)) { + --lockCount[type]; + if (hasLock(type)) + return; + } + + const int semNum = type; + int ret; + do { + sembuf sops = {semNum, 1, 0}; + if (semNum == QWSLock::BackingStore) + sops.sem_flg |= SEM_UNDO; + + ret = semop(semId, &sops, 1); + if (ret == -1 && errno != EINTR) + qDebug("QWSLock::unlock: %s", strerror(errno)); + } while (ret == -1 && errno == EINTR); +} + +bool QWSLock::wait(LockType type, int timeout) +{ + bool ok = forceLock(semId, type, timeout); + if (ok) + unlock(type); + return ok; +} + +QT_END_NAMESPACE + +#endif // QT_NO_QWS_MULTIPROCESS diff --git a/src/gui/embedded/qwslock_p.h b/src/gui/embedded/qwslock_p.h new file mode 100644 index 0000000..4dc8732 --- /dev/null +++ b/src/gui/embedded/qwslock_p.h @@ -0,0 +1,85 @@ +/**************************************************************************** +** +** 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$ +** +****************************************************************************/ + +#ifndef QWSLOCK_P_H +#define QWSLOCK_P_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. This header file may +// change from version to version without notice, or even be +// removed. +// +// We mean it. +// + +#include <qglobal.h> + +QT_BEGIN_NAMESPACE + +#ifndef QT_NO_QWS_MULTIPROCESS + +class QWSLock +{ +public: + enum LockType { BackingStore, Communication, RegionEvent }; + + QWSLock(); + QWSLock(int lockId); + ~QWSLock(); + + bool lock(LockType type, int timeout = -1); + void unlock(LockType type); + bool wait(LockType type, int timeout = -1); + bool hasLock(LockType type); + int id() const { return semId; } + +private: + int semId; + int lockCount[2]; +}; + + +QT_END_NAMESPACE +#endif // QT_NO_QWS_MULTIPROCESS +#endif // QWSLOCK_P_H diff --git a/src/gui/embedded/qwsmanager_p.h b/src/gui/embedded/qwsmanager_p.h new file mode 100644 index 0000000..a891a75 --- /dev/null +++ b/src/gui/embedded/qwsmanager_p.h @@ -0,0 +1,122 @@ +/**************************************************************************** +** +** 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$ +** +****************************************************************************/ + +#ifndef QWSMANAGER_P_H +#define QWSMANAGER_P_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists for the convenience +// of the QLibrary class. This header file may change from +// version to version without notice, or even be removed. +// +// We mean it. +// + +#include "QtGui/qregion.h" +#include "QtGui/qdecoration_qws.h" + +#ifndef QT_NO_QWS_MANAGER + +#include "QtCore/qhash.h" + +QT_BEGIN_NAMESPACE + +class QWidget; +class QMenu; + +class QWSManagerPrivate : public QObjectPrivate +{ + Q_DECLARE_PUBLIC(QWSManager) +public: + QWSManagerPrivate(); + + int activeRegion; + QWidget *managed; + QMenu *popup; + + enum MenuAction { + NormalizeAction, + TitleAction, + BottomRightAction, + MinimizeAction, + MaximizeAction, + CloseAction, + LastMenuAction + }; + QAction *menuActions[LastMenuAction]; + + static QWidget *active; + static QPoint mousePos; + + // Region caching to avoid getting a regiontype's + // QRegion for each mouse move event + int previousRegionType; + bool previousRegionRepainted; // Hover/Press handled + bool entireDecorationNeedsRepaint; + struct RegionCaching { + int regionType; + QRegion region; + Qt::WindowFlags windowFlags; + QRect windowGeometry; + } cached_region; + + bool newCachedRegion(const QPoint &pos); + int cachedRegionAt() + { return cached_region.regionType; } + + void dirtyRegion(int decorationRegion, + QDecoration::DecorationState state, + const QRegion &clip = QRegion()); + void clearDirtyRegions(); + + QList<int> dirtyRegions; + QList<QDecoration::DecorationState> dirtyStates; + QRegion dirtyClip; +}; + +#endif // QT_NO_QWS_MANAGER + +QT_END_NAMESPACE + +#endif // QWSMANAGER_P_H diff --git a/src/gui/embedded/qwsmanager_qws.cpp b/src/gui/embedded/qwsmanager_qws.cpp new file mode 100644 index 0000000..96abf3f --- /dev/null +++ b/src/gui/embedded/qwsmanager_qws.cpp @@ -0,0 +1,535 @@ +/**************************************************************************** +** +** 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 "qwsmanager_qws.h" + +#ifndef QT_NO_QWS_MANAGER + +#include "qdrawutil.h" +#include "qapplication.h" +#include "qstyle.h" +#include "qwidget.h" +#include "qmenu.h" +#include "qpainter.h" +#include "private/qpainter_p.h" +#include "qregion.h" +#include "qevent.h" +#include "qcursor.h" +#include "qwsdisplay_qws.h" +#include "qdesktopwidget.h" + +#include <private/qapplication_p.h> +#include <private/qwidget_p.h> +#include <private/qbackingstore_p.h> +#include <private/qwindowsurface_qws_p.h> +#include "qdecorationfactory_qws.h" + +#include "qlayout.h" + +#include "qwsmanager_p.h" + +#include <qdebug.h> + +QT_BEGIN_NAMESPACE + +QWidget *QWSManagerPrivate::active = 0; +QPoint QWSManagerPrivate::mousePos; + + +QWSManagerPrivate::QWSManagerPrivate() + : QObjectPrivate(), activeRegion(QDecoration::None), managed(0), popup(0), + previousRegionType(0), previousRegionRepainted(false), entireDecorationNeedsRepaint(false) +{ + cached_region.regionType = 0; +} + +QRegion &QWSManager::cachedRegion() +{ + return d_func()->cached_region.region; +} + +/*! + \class QWSManager + \ingroup qws + \internal +*/ + +/*! + +*/ +QWSManager::QWSManager(QWidget *w) + : QObject(*new QWSManagerPrivate, (QObject*)0) +{ + d_func()->managed = w; + +} + +QWSManager::~QWSManager() +{ + Q_D(QWSManager); +#ifndef QT_NO_MENU + if (d->popup) + delete d->popup; +#endif + if (d->managed == QWSManagerPrivate::active) + QWSManagerPrivate::active = 0; +} + +QWidget *QWSManager::widget() +{ + Q_D(QWSManager); + return d->managed; +} + +QWidget *QWSManager::grabbedMouse() +{ + return QWSManagerPrivate::active; +} + +QRegion QWSManager::region() +{ + Q_D(QWSManager); + return QApplication::qwsDecoration().region(d->managed, d->managed->geometry()); +} + +bool QWSManager::event(QEvent *e) +{ + if (QObject::event(e)) + return true; + + switch (e->type()) { + case QEvent::MouseMove: + mouseMoveEvent((QMouseEvent*)e); + break; + + case QEvent::MouseButtonPress: + mousePressEvent((QMouseEvent*)e); + break; + + case QEvent::MouseButtonRelease: + mouseReleaseEvent((QMouseEvent*)e); + break; + + case QEvent::MouseButtonDblClick: + mouseDoubleClickEvent((QMouseEvent*)e); + break; + + case QEvent::Paint: + paintEvent((QPaintEvent*)e); + break; + + default: + return false; + break; + } + + return true; +} + +void QWSManager::mousePressEvent(QMouseEvent *e) +{ + Q_D(QWSManager); + d->mousePos = e->globalPos(); + d->activeRegion = QApplication::qwsDecoration().regionAt(d->managed, d->mousePos); + if(d->cached_region.regionType) + d->previousRegionRepainted |= repaintRegion(d->cached_region.regionType, QDecoration::Pressed); + + if (d->activeRegion == QDecoration::Menu) { + QPoint pos = (QApplication::layoutDirection() == Qt::LeftToRight + ? d->managed->geometry().topLeft() + : d->managed->geometry().topRight()); + menu(pos); + } + if (d->activeRegion != QDecoration::None && + d->activeRegion != QDecoration::Menu) { + d->active = d->managed; + d->managed->grabMouse(); + } + if (d->activeRegion != QDecoration::None && + d->activeRegion != QDecoration::Close && + d->activeRegion != QDecoration::Minimize && + d->activeRegion != QDecoration::Menu) { + d->managed->raise(); + } + + if (e->button() == Qt::RightButton) { + menu(e->globalPos()); + } +} + +void QWSManager::mouseReleaseEvent(QMouseEvent *e) +{ + Q_D(QWSManager); + d->managed->releaseMouse(); + if (d->cached_region.regionType && d->previousRegionRepainted && QApplication::mouseButtons() == 0) { + bool doesHover = repaintRegion(d->cached_region.regionType, QDecoration::Hover); + if (!doesHover) { + repaintRegion(d->cached_region.regionType, QDecoration::Normal); + d->previousRegionRepainted = false; + } + } + + if (e->button() == Qt::LeftButton) { + //handleMove(); + int itm = QApplication::qwsDecoration().regionAt(d->managed, e->globalPos()); + int activatedItem = d->activeRegion; + d->activeRegion = QDecoration::None; + d->active = 0; + if (activatedItem == itm) + QApplication::qwsDecoration().regionClicked(d->managed, itm); + } else if (d->activeRegion == QDecoration::None) { + d->active = 0; + } +} + +void QWSManager::mouseDoubleClickEvent(QMouseEvent *e) +{ + Q_D(QWSManager); + if (e->button() == Qt::LeftButton) + QApplication::qwsDecoration().regionDoubleClicked(d->managed, + QApplication::qwsDecoration().regionAt(d->managed, e->globalPos())); +} + +static inline Qt::CursorShape regionToShape(int region) +{ + if (region == QDecoration::None) + return Qt::ArrowCursor; + + static const struct { + int region; + Qt::CursorShape shape; + } r2s[] = { + { QDecoration::TopLeft, Qt::SizeFDiagCursor }, + { QDecoration::Top, Qt::SizeVerCursor}, + { QDecoration::TopRight, Qt::SizeBDiagCursor}, + { QDecoration::Left, Qt::SizeHorCursor}, + { QDecoration::Right, Qt::SizeHorCursor}, + { QDecoration::BottomLeft, Qt::SizeBDiagCursor}, + { QDecoration::Bottom, Qt::SizeVerCursor}, + { QDecoration::BottomRight, Qt::SizeFDiagCursor}, + { QDecoration::None, Qt::ArrowCursor} + }; + + int i = 0; + while (region != r2s[i].region && r2s[i].region) + ++i; + return r2s[i].shape; +} + +void QWSManager::mouseMoveEvent(QMouseEvent *e) +{ + Q_D(QWSManager); + if (d->newCachedRegion(e->globalPos())) { + if(d->previousRegionType && d->previousRegionRepainted) + repaintRegion(d->previousRegionType, QDecoration::Normal); + if(d->cached_region.regionType) { + d->previousRegionRepainted = repaintRegion(d->cached_region.regionType, QDecoration::Hover); + } + } + + +#ifndef QT_NO_CURSOR + QWSDisplay *qwsd = QApplication::desktop()->qwsDisplay(); + qwsd->selectCursor(d->managed, regionToShape(d->cachedRegionAt())); +#endif //QT_NO_CURSOR + + if (d->activeRegion) + handleMove(e->globalPos()); +} + +void QWSManager::handleMove(QPoint g) +{ + Q_D(QWSManager); + + // don't allow dragging to where the user probably cannot click! + QApplicationPrivate *ap = QApplicationPrivate::instance(); + const QRect maxWindowRect = ap->maxWindowRect(qt_screen); + if (maxWindowRect.isValid()) { + if (g.x() < maxWindowRect.x()) + g.setX(maxWindowRect.x()); + if (g.y() < maxWindowRect.y()) + g.setY(maxWindowRect.y()); + if (g.x() > maxWindowRect.right()) + g.setX(maxWindowRect.right()); + if (g.y() > maxWindowRect.bottom()) + g.setY(maxWindowRect.bottom()); + } + + if (g == d->mousePos) + return; + + if ( d->managed->isMaximized() ) + return; + + int x = d->managed->geometry().x(); + int y = d->managed->geometry().y(); + int w = d->managed->width(); + int h = d->managed->height(); + + QRect geom(d->managed->geometry()); + + QPoint delta = g - d->mousePos; + d->mousePos = g; + + if (d->activeRegion == QDecoration::Title) { + geom = QRect(x + delta.x(), y + delta.y(), w, h); + } else { + bool keepTop = true; + bool keepLeft = true; + switch (d->activeRegion) { + case QDecoration::Top: + geom.setTop(geom.top() + delta.y()); + keepTop = false; + break; + case QDecoration::Bottom: + geom.setBottom(geom.bottom() + delta.y()); + keepTop = true; + break; + case QDecoration::Left: + geom.setLeft(geom.left() + delta.x()); + keepLeft = false; + break; + case QDecoration::Right: + geom.setRight(geom.right() + delta.x()); + keepLeft = true; + break; + case QDecoration::TopRight: + geom.setTopRight(geom.topRight() + delta); + keepLeft = true; + keepTop = false; + break; + case QDecoration::TopLeft: + geom.setTopLeft(geom.topLeft() + delta); + keepLeft = false; + keepTop = false; + break; + case QDecoration::BottomLeft: + geom.setBottomLeft(geom.bottomLeft() + delta); + keepLeft = false; + keepTop = true; + break; + case QDecoration::BottomRight: + geom.setBottomRight(geom.bottomRight() + delta); + keepLeft = true; + keepTop = true; + break; + default: + return; + } + + QSize newSize = QLayout::closestAcceptableSize(d->managed, geom.size()); + + int dx = newSize.width() - geom.width(); + int dy = newSize.height() - geom.height(); + + if (keepTop) { + geom.setBottom(geom.bottom() + dy); + d->mousePos.ry() += dy; + } else { + geom.setTop(geom.top() - dy); + d->mousePos.ry() -= dy; + } + if (keepLeft) { + geom.setRight(geom.right() + dx); + d->mousePos.rx() += dx; + } else { + geom.setLeft(geom.left() - dx); + d->mousePos.rx() -= dx; + } + } + if (geom != d->managed->geometry()) { + QApplication::sendPostedEvents(); + d->managed->setGeometry(geom); + } +} + +void QWSManager::paintEvent(QPaintEvent *) +{ + Q_D(QWSManager); + d->dirtyRegion(QDecoration::All, QDecoration::Normal); +} + +void QWSManagerPrivate::dirtyRegion(int decorationRegion, + QDecoration::DecorationState state, + const QRegion &clip) +{ + QTLWExtra *topextra = managed->d_func()->extra->topextra; + QWidgetBackingStore *bs = topextra->backingStore; + const bool pendingUpdateRequest = bs->isDirty(); + + if (decorationRegion == QDecoration::All) { + if (clip.isEmpty()) + entireDecorationNeedsRepaint = true; + dirtyRegions.clear(); + dirtyStates.clear(); + } + int i = dirtyRegions.indexOf(decorationRegion); + if (i >= 0) { + dirtyRegions.removeAt(i); + dirtyStates.removeAt(i); + } + + dirtyRegions.append(decorationRegion); + dirtyStates.append(state); + if (!entireDecorationNeedsRepaint) + dirtyClip += clip; + + if (!pendingUpdateRequest) + QApplication::postEvent(managed, new QEvent(QEvent::UpdateRequest), Qt::LowEventPriority); +} + +void QWSManagerPrivate::clearDirtyRegions() +{ + dirtyRegions.clear(); + dirtyStates.clear(); + dirtyClip = QRegion(); + entireDecorationNeedsRepaint = false; +} + +bool QWSManager::repaintRegion(int decorationRegion, QDecoration::DecorationState state) +{ + Q_D(QWSManager); + + d->dirtyRegion(decorationRegion, state); + return true; +} + +void QWSManager::menu(const QPoint &pos) +{ +#ifdef QT_NO_MENU + Q_UNUSED(pos); +#else + Q_D(QWSManager); + if (d->popup) + delete d->popup; + + // Basic window operation menu + d->popup = new QMenu(); + QApplication::qwsDecoration().buildSysMenu(d->managed, d->popup); + connect(d->popup, SIGNAL(triggered(QAction*)), SLOT(menuTriggered(QAction*))); + + d->popup->popup(pos); + d->activeRegion = QDecoration::None; +#endif // QT_NO_MENU +} + +void QWSManager::menuTriggered(QAction *action) +{ +#ifdef QT_NO_MENU + Q_UNUSED(action); +#else + Q_D(QWSManager); + QApplication::qwsDecoration().menuTriggered(d->managed, action); + d->popup->deleteLater(); + d->popup = 0; +#endif +} + +void QWSManager::startMove() +{ + Q_D(QWSManager); + d->mousePos = QCursor::pos(); + d->activeRegion = QDecoration::Title; + d->active = d->managed; + d->managed->grabMouse(); +} + +void QWSManager::startResize() +{ + Q_D(QWSManager); + d->activeRegion = QDecoration::BottomRight; + d->active = d->managed; + d->managed->grabMouse(); +} + +void QWSManager::maximize() +{ + Q_D(QWSManager); + // find out how much space the decoration needs + const int screen = QApplication::desktop()->screenNumber(d->managed); + const QRect desk = QApplication::desktop()->availableGeometry(screen); + QRect dummy(0, 0, 1, 1); + QRect nr; + QRegion r = QApplication::qwsDecoration().region(d->managed, dummy); + if (r.isEmpty()) { + nr = desk; + } else { + r += dummy; // make sure we get the full window region in case of 0 width borders + QRect rect = r.boundingRect(); + nr = QRect(desk.x()-rect.x(), desk.y()-rect.y(), + desk.width() - (rect.width()==1 ? 0 : rect.width()-1), // ==1 -> dummy + desk.height() - (rect.height()==1 ? 0 : rect.height()-1)); + } + d->managed->setGeometry(nr); +} + +bool QWSManagerPrivate::newCachedRegion(const QPoint &pos) +{ + // Check if anything has changed that would affect the region caching + if (managed->windowFlags() == cached_region.windowFlags + && managed->geometry() == cached_region.windowGeometry + && cached_region.region.contains(pos)) + return false; + + // Update the cached region + int reg = QApplication::qwsDecoration().regionAt(managed, pos); + if (QWidget::mouseGrabber()) + reg = QDecoration::None; + + previousRegionType = cached_region.regionType; + cached_region.regionType = reg; + cached_region.region = QApplication::qwsDecoration().region(managed, managed->geometry(), + reg); + // Make room for borders around the widget, even if the decoration doesn't have a frame. + if (reg && !(reg & int(QDecoration::Borders))) { + cached_region.region -= QApplication::qwsDecoration().region(managed, managed->geometry(), QDecoration::Borders); + } + cached_region.windowFlags = managed->windowFlags(); + cached_region.windowGeometry = managed->geometry(); +// QRect rec = cached_region.region.boundingRect(); +// qDebug("Updated cached region: 0x%04x (%d, %d) (%d, %d, %d, %d)", +// reg, pos.x(), pos.y(), rec.x(), rec.y(), rec.right(), rec.bottom()); + return true; +} + +QT_END_NAMESPACE + +#endif //QT_NO_QWS_MANAGER diff --git a/src/gui/embedded/qwsmanager_qws.h b/src/gui/embedded/qwsmanager_qws.h new file mode 100644 index 0000000..d342ae0 --- /dev/null +++ b/src/gui/embedded/qwsmanager_qws.h @@ -0,0 +1,122 @@ +/**************************************************************************** +** +** 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$ +** +****************************************************************************/ + +#ifndef QWSMANAGER_QWS_H +#define QWSMANAGER_QWS_H + +#include <QtGui/qpixmap.h> +#include <QtCore/qobject.h> +#include <QtGui/qdecoration_qws.h> +#include <QtGui/qevent.h> + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +QT_MODULE(Gui) + +#ifndef QT_NO_QWS_MANAGER + +class QAction; +class QPixmap; +class QWidget; +class QPopupMenu; +class QRegion; +class QMouseEvent; +class QWSManagerPrivate; + +class Q_GUI_EXPORT QWSManager : public QObject +{ + Q_OBJECT + Q_DECLARE_PRIVATE(QWSManager) +public: + explicit QWSManager(QWidget *); + ~QWSManager(); + + static QDecoration *newDefaultDecoration(); + + QWidget *widget(); + static QWidget *grabbedMouse(); + void maximize(); + void startMove(); + void startResize(); + + QRegion region(); + QRegion &cachedRegion(); + +protected Q_SLOTS: + void menuTriggered(QAction *action); + +protected: + void handleMove(QPoint g); + + virtual bool event(QEvent *e); + virtual void mouseMoveEvent(QMouseEvent *); + virtual void mousePressEvent(QMouseEvent *); + virtual void mouseReleaseEvent(QMouseEvent *); + virtual void mouseDoubleClickEvent(QMouseEvent *); + virtual void paintEvent(QPaintEvent *); + bool repaintRegion(int region, QDecoration::DecorationState state); + + void menu(const QPoint &); + +private: + friend class QWidget; + friend class QETWidget; + friend class QWidgetPrivate; + friend class QApplication; + friend class QApplicationPrivate; + friend class QWidgetBackingStore; + friend class QWSWindowSurface; + friend class QGLDrawable; +}; + +QT_BEGIN_INCLUDE_NAMESPACE +#include <QtGui/qdecorationdefault_qws.h> +QT_END_INCLUDE_NAMESPACE + +#endif // QT_NO_QWS_MANAGER + +QT_END_NAMESPACE + +QT_END_HEADER + +#endif // QWSMANAGER_QWS_H diff --git a/src/gui/embedded/qwsproperty_qws.cpp b/src/gui/embedded/qwsproperty_qws.cpp new file mode 100644 index 0000000..1c44506 --- /dev/null +++ b/src/gui/embedded/qwsproperty_qws.cpp @@ -0,0 +1,145 @@ +/**************************************************************************** +** +** 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 "qwsproperty_qws.h" + +#ifndef QT_NO_QWS_PROPERTIES +#include "qwscommand_qws_p.h" +#include "qwindowsystem_qws.h" +#include "qhash.h" +#include "qalgorithms.h" +#include "qbytearray.h" + +#include <stdio.h> + +QT_BEGIN_NAMESPACE + +class QWSPropertyManager::Data { +public: + QByteArray find(int winId, int property) + { + return properties.value(winId).value(property); + } + + typedef QHash<int, QHash<int, QByteArray> > PropertyHash; + PropertyHash properties; +}; + +/********************************************************************* + * + * Class: QWSPropertyManager + * + *********************************************************************/ + +QWSPropertyManager::QWSPropertyManager() +{ + d = new Data; +} + +QWSPropertyManager::~QWSPropertyManager() +{ + delete d; +} + +bool QWSPropertyManager::setProperty(int winId, int property, int mode, const char *data, int len) +{ + QHash<int, QByteArray> props = d->properties.value(winId); + QHash<int, QByteArray>::iterator it = props.find(property); + if (it == props.end()) + return false; + + switch (mode) { + case PropReplace: + d->properties[winId][property] = QByteArray(data, len); + break; + case PropAppend: + d->properties[winId][property].append(data); + break; + case PropPrepend: + d->properties[winId][property].prepend(data); + break; + } + return true; +} + +bool QWSPropertyManager::hasProperty(int winId, int property) +{ + return d->properties.value(winId).contains(property); +} + +bool QWSPropertyManager::removeProperty(int winId, int property) +{ + QWSPropertyManager::Data::PropertyHash::iterator it = d->properties.find(winId); + if (it == d->properties.end()) + return false; + return d->properties[winId].remove( property ); +} + +bool QWSPropertyManager::addProperty(int winId, int property) +{ + if( !d->properties[winId].contains(property) ) + d->properties[winId][property] = QByteArray(); // only add if it doesn't exist + return true; +} + +bool QWSPropertyManager::getProperty(int winId, int property, const char *&data, int &len) +{ + QHash<int, QByteArray> props = d->properties.value(winId); + QHash<int, QByteArray>::iterator it = props.find(property); + if (it == props.end()) { + data = 0; + len = -1; + return false; + } + data = it.value().constData(); + len = it.value().length(); + + return true; +} + +bool QWSPropertyManager::removeProperties(int winId) +{ + return d->properties.remove(winId); +} + +QT_END_NAMESPACE + +#endif //QT_NO_QWS_PROPERTIES diff --git a/src/gui/embedded/qwsproperty_qws.h b/src/gui/embedded/qwsproperty_qws.h new file mode 100644 index 0000000..27f0c65 --- /dev/null +++ b/src/gui/embedded/qwsproperty_qws.h @@ -0,0 +1,96 @@ +/**************************************************************************** +** +** 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$ +** +****************************************************************************/ + +#ifndef QWSPROPERTY_QWS_H +#define QWSPROPERTY_QWS_H + +#include <QtCore/qglobal.h> + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +QT_MODULE(Gui) + +/********************************************************************* + * + * Class: QWSPropertyManager + * + *********************************************************************/ + +#ifndef QT_NO_QWS_PROPERTIES + +class QWSPropertyManager +{ +public: + enum Mode { + PropReplace = 0, + PropPrepend, + PropAppend + }; + + // pre-defined properties + enum Atom { + PropSelection = 0 + }; + + QWSPropertyManager(); + ~QWSPropertyManager(); + + bool setProperty(int winId, int property, int mode, const char *data, int len); + bool hasProperty(int winId, int property); + bool removeProperty(int winId, int property); + bool addProperty(int winId, int property); + bool getProperty(int winId, int property, const char *&data, int &len); + bool removeProperties(int winId); + +private: + class Data; + Data* d; +}; + +#endif // QT_NO_QWS_PROPERTIES + +QT_END_NAMESPACE + +QT_END_HEADER + +#endif // QWSPROPERTY_QWS_H diff --git a/src/gui/embedded/qwsprotocolitem_qws.h b/src/gui/embedded/qwsprotocolitem_qws.h new file mode 100644 index 0000000..71f70dd --- /dev/null +++ b/src/gui/embedded/qwsprotocolitem_qws.h @@ -0,0 +1,100 @@ +/**************************************************************************** +** +** 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$ +** +****************************************************************************/ + +#ifndef QWSPROTOCOLITEM_QWS_H +#define QWSPROTOCOLITEM_QWS_H + +/********************************************************************* + * + * QWSCommand base class - only use derived classes from that + * + *********************************************************************/ + +#include <QtCore/qglobal.h> + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +QT_MODULE(Gui) + +class QIODevice; + +struct QWSProtocolItem +{ + // ctor - dtor + QWSProtocolItem(int t, int len, char *ptr) : type(t), + simpleLen(len), rawLen(-1), deleteRaw(false), simpleDataPtr(ptr), + rawDataPtr(0), bytesRead(0) { } + virtual ~QWSProtocolItem(); + + // data + int type; + int simpleLen; + int rawLen; + bool deleteRaw; + + // functions +#ifndef QT_NO_QWS_MULTIPROCESS + void write(QIODevice *s); + bool read(QIODevice *s); +#endif + void copyFrom(const QWSProtocolItem *item); + + virtual void setData(const char *data, int len, bool allocateMem = true); + + char *simpleDataPtr; + char *rawDataPtr; + // temp variables + int bytesRead; +}; + +// This should probably be a method on QWSProtocolItem, but this way avoids +// changing the API of this apparently public header +// size = (int)type + (int)rawLenSize + simpleLen + rawLen +#define QWS_PROTOCOL_ITEM_SIZE( item ) \ + (2 * sizeof(int)) + ((item).simpleDataPtr ? (item).simpleLen : 0) + ((item).rawDataPtr ? (item).rawLen : 0) + +QT_END_NAMESPACE + +QT_END_HEADER + +#endif // QWSPROTOCOLITEM_QWS_H diff --git a/src/gui/embedded/qwssharedmemory.cpp b/src/gui/embedded/qwssharedmemory.cpp new file mode 100644 index 0000000..8afbe9b --- /dev/null +++ b/src/gui/embedded/qwssharedmemory.cpp @@ -0,0 +1,185 @@ +/**************************************************************************** +** +** 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 "qwssharedmemory_p.h" + +#if !defined(QT_NO_QWS_MULTIPROCESS) + +#include <sys/shm.h> + +QT_BEGIN_NAMESPACE + +QWSSharedMemory::QWSSharedMemory() + : shmBase(0), shmSize(0), character(0), shmId(-1), key(-1) +{ +} + + +QWSSharedMemory::~QWSSharedMemory() +{ + detach(); +} + +/* + man page says: + On Linux, it is possible to attach a shared memory segment even if it + is already marked to be deleted. However, POSIX.1-2001 does not spec- + ify this behaviour and many other implementations do not support it. +*/ + +bool QWSSharedMemory::create(int size) +{ + if (shmId != -1) + detach(); + shmId = shmget(IPC_PRIVATE, size, IPC_CREAT|0600); + + if (shmId == -1) { +#ifdef QT_SHM_DEBUG + perror("QWSSharedMemory::create allocating shared memory"); + qWarning("Error allocating shared memory of size %d", size); +#endif + return false; + } + shmBase = shmat(shmId,0,0); + shmctl(shmId, IPC_RMID, 0); + if (shmBase == (void*)-1) { +#ifdef QT_SHM_DEBUG + perror("QWSSharedMemory::create attaching to shared memory"); + qWarning("Error attaching to shared memory id %d", shmId); +#endif + shmBase = 0; + return false; + } + return true; +} + +bool QWSSharedMemory::attach(int id) +{ + if (shmId == id) + return id != -1; + if (shmId != -1) + detach(); + + shmBase = shmat(id,0,0); + if (shmBase == (void*)-1) { +#ifdef QT_SHM_DEBUG + perror("QWSSharedMemory::attach attaching to shared memory"); + qWarning("Error attaching to shared memory 0x%x of size %d", + id, size()); +#endif + shmBase = 0; + return false; + } + shmId = id; + return true; +} + + +void QWSSharedMemory::detach () +{ + if (!shmBase) + return; + shmdt (shmBase); + shmBase = 0; + shmSize = 0; + shmId = -1; +} + +void QWSSharedMemory::setPermissions (mode_t mode) +{ + struct shmid_ds shm; + shmctl (shmId, IPC_STAT, &shm); + shm.shm_perm.mode = mode; + shmctl (shmId, IPC_SET, &shm); +} + +int QWSSharedMemory::size () const +{ + struct shmid_ds shm; + shmctl (shmId, IPC_STAT, &shm); + return shm.shm_segsz; +} + + +// old API + + + +QWSSharedMemory::QWSSharedMemory (int size, const QString &filename, char c) +{ + shmSize = size; + shmFile = filename; + shmBase = 0; + shmId = -1; + character = c; + key = ftok (shmFile.toLatin1().constData(), c); +} + + + +bool QWSSharedMemory::create () +{ + shmId = shmget (key, shmSize, IPC_CREAT | 0666); + return (shmId != -1); +} + +void QWSSharedMemory::destroy () +{ + if (shmId != -1) + shmctl(shmId, IPC_RMID, 0); +} + +bool QWSSharedMemory::attach () +{ + if (shmId == -1) + shmId = shmget (key, shmSize, 0); + + shmBase = shmat (shmId, 0, 0); + if ((long)shmBase == -1) + shmBase = 0; + + return (long)shmBase != 0; +} + + +QT_END_NAMESPACE + +#endif // QT_NO_QWS_MULTIPROCESS diff --git a/src/gui/embedded/qwssharedmemory_p.h b/src/gui/embedded/qwssharedmemory_p.h new file mode 100644 index 0000000..6b58605 --- /dev/null +++ b/src/gui/embedded/qwssharedmemory_p.h @@ -0,0 +1,105 @@ +/**************************************************************************** +** +** 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$ +** +****************************************************************************/ + +#ifndef QWSSHAREDMEMORY_P_H +#define QWSSHAREDMEMORY_P_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +#include "qplatformdefs.h" +#include "QtCore/qstring.h" + +QT_BEGIN_NAMESPACE + +#if !defined(QT_NO_QWS_MULTIPROCESS) + +class QWSSharedMemory { +public: + + QWSSharedMemory(); + ~QWSSharedMemory(); + + void setPermissions(mode_t mode); + int size() const; + void *address() { return shmBase; }; + + int id() const { return shmId; } + + void detach(); + + bool create(int size); + bool attach(int id); + + //bool create(int size, const QString &filename, char c = 'Q'); + //bool attach(const QString &filename, char c = 'Q'); +// old API + + QWSSharedMemory(int, const QString &, char c = 'Q'); + void * base() { return address(); }; + + bool create(); + void destroy(); + + bool attach(); + +private: + void *shmBase; + int shmSize; + QString shmFile; + char character; + int shmId; + key_t key; +}; + +#endif // QT_NO_QWS_MULTIPROCESS + +QT_END_NAMESPACE + +#endif // QWSSHAREDMEMORY_P_H diff --git a/src/gui/embedded/qwssignalhandler.cpp b/src/gui/embedded/qwssignalhandler.cpp new file mode 100644 index 0000000..0946fb6 --- /dev/null +++ b/src/gui/embedded/qwssignalhandler.cpp @@ -0,0 +1,134 @@ +/**************************************************************************** +** +** 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 "qwssignalhandler_p.h" + +#ifndef QT_NO_QWS_SIGNALHANDLER + +#include <sys/types.h> +#include <sys/ipc.h> +#include <sys/sem.h> +#include <signal.h> + +QT_BEGIN_NAMESPACE + +#ifndef Q_OS_BSD4 +union semun { + int val; + struct semid_ds *buf; + unsigned short *array; + struct seminfo *__buf; +}; +#endif + + +class QWSSignalHandlerPrivate : public QWSSignalHandler +{ +public: + QWSSignalHandlerPrivate() : QWSSignalHandler() {} +}; + + +Q_GLOBAL_STATIC(QWSSignalHandlerPrivate, signalHandlerInstance); + + +QWSSignalHandler* QWSSignalHandler::instance() +{ + return signalHandlerInstance(); +} + +QWSSignalHandler::QWSSignalHandler() +{ + const int signums[] = { SIGHUP, SIGINT, SIGQUIT, SIGILL, SIGABRT, SIGFPE, + SIGSEGV, SIGTERM, SIGBUS }; + const int n = sizeof(signums)/sizeof(int); + + for (int i = 0; i < n; ++i) { + const int signum = signums[i]; + qt_sighandler_t old = signal(signum, handleSignal); + if (old == SIG_IGN) // don't remove shm and semaphores when ignored + signal(signum, old); + else + oldHandlers[signum] = (old == SIG_ERR ? SIG_DFL : old); + } +} + +QWSSignalHandler::~QWSSignalHandler() +{ +#ifndef QT_NO_QWS_MULTIPROCESS + while (!semaphores.isEmpty()) + removeSemaphore(semaphores.last()); +#endif +} + +#ifndef QT_NO_QWS_MULTIPROCESS +void QWSSignalHandler::removeSemaphore(int semno) +{ + const int index = semaphores.lastIndexOf(semno); + if (index != -1) { + semun semval; + semval.val = 0; + semctl(semaphores.at(index), 0, IPC_RMID, semval); + semaphores.remove(index); + } +} +#endif // QT_NO_QWS_MULTIPROCESS + +void QWSSignalHandler::handleSignal(int signum) +{ + QWSSignalHandler *h = instance(); + + signal(signum, h->oldHandlers[signum]); + +#ifndef QT_NO_QWS_MULTIPROCESS + semun semval; + semval.val = 0; + for (int i = 0; i < h->semaphores.size(); ++i) + semctl(h->semaphores.at(i), 0, IPC_RMID, semval); +#endif + + h->objects.clear(); + raise(signum); +} + +QT_END_NAMESPACE + +#endif // QT_QWS_NO_SIGNALHANDLER diff --git a/src/gui/embedded/qwssignalhandler_p.h b/src/gui/embedded/qwssignalhandler_p.h new file mode 100644 index 0000000..f2228e1 --- /dev/null +++ b/src/gui/embedded/qwssignalhandler_p.h @@ -0,0 +1,99 @@ +/**************************************************************************** +** +** 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$ +** +****************************************************************************/ + +#ifndef QWSSIGNALHANDLER_P_H +#define QWSSIGNALHANDLER_P_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists for the convenience +// of the QLibrary class. This header file may change from +// version to version without notice, or even be removed. +// +// We mean it. +// + +#include <QtCore/qglobal.h> + +#ifndef QT_NO_QWS_SIGNALHANDLER + +#include <QtCore/qmap.h> +#include <QtCore/qvector.h> +#include <QtCore/qobjectcleanuphandler.h> + +QT_BEGIN_NAMESPACE + +typedef void (*qt_sighandler_t)(int); + +class QWSSignalHandlerPrivate; + +class Q_GUI_EXPORT QWSSignalHandler +{ +public: + static QWSSignalHandler* instance(); + + ~QWSSignalHandler(); + +#ifndef QT_NO_QWS_MULTIPROCESS + inline void addSemaphore(int semno) { semaphores.append(semno); } + void removeSemaphore(int semno); +#endif + inline void addObject(QObject *object) { (void)objects.add(object); } + +private: + QWSSignalHandler(); + static void handleSignal(int signal); + QMap<int, qt_sighandler_t> oldHandlers; +#ifndef QT_NO_QWS_MULTIPROCESS + QVector<int> semaphores; +#endif + QObjectCleanupHandler objects; + + friend class QWSSignalHandlerPrivate; +}; + +QT_END_NAMESPACE + +#endif // QT_NO_QWS_SIGNALHANDLER + +#endif // QWSSIGNALHANDLER_P_H diff --git a/src/gui/embedded/qwssocket_qws.cpp b/src/gui/embedded/qwssocket_qws.cpp new file mode 100644 index 0000000..bebd98e --- /dev/null +++ b/src/gui/embedded/qwssocket_qws.cpp @@ -0,0 +1,280 @@ +/**************************************************************************** +** +** 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 "qplatformdefs.h" +#include "qwssocket_qws.h" + +#ifndef QT_NO_QWS_MULTIPROCESS + +#include <fcntl.h> +#include <netdb.h> +#include <errno.h> +#include <stdio.h> +#include <sys/file.h> +#include <sys/time.h> +#include <sys/un.h> + +#ifdef __MIPSEL__ +# ifndef SOCK_DGRAM +# define SOCK_DGRAM 1 +# endif +# ifndef SOCK_STREAM +# define SOCK_STREAM 2 +# endif +#endif + +#if defined(Q_OS_SOLARIS) || defined (QT_LINUXBASE) +// uff-da apparently Solaris doesn't have the SUN_LEN macro, here is +// an implementation of it... +# ifndef SUN_LEN +# define SUN_LEN(su) \ + sizeof(*(su)) - sizeof((su)->sun_path) + strlen((su)->sun_path) +# endif + +// nor the POSIX names of UNIX domain sockets *sigh* +# ifndef AF_LOCAL +# define AF_LOCAL AF_UNIX +# endif +# ifndef PF_LOCAL +# define PF_LOCAL PF_UNIX +# endif +#endif // Q_OS_SOLARIS || QT_LINUXBASE + +QT_BEGIN_NAMESPACE + +/*********************************************************************** + * + * QWSSocket + * + **********************************************************************/ +QWSSocket::QWSSocket(QObject *parent) + : QWS_SOCK_BASE(parent) +{ +#ifndef QT_NO_SXE + QObject::connect( this, SIGNAL(stateChanged(SocketState)), + this, SLOT(forwardStateChange(SocketState))); +#endif +} + +QWSSocket::~QWSSocket() +{ +} + +#ifndef QT_NO_SXE +QString QWSSocket::errorString() +{ + switch (QUnixSocket::error()) { + case NoError: + return QString(); + case InvalidPath: + case NonexistentPath: + return QLatin1String("Bad path"); // NO_TR + default: + return QLatin1String("Bad socket"); // NO TR + } +} + +void QWSSocket::forwardStateChange(QUnixSocket::SocketState st ) +{ + switch ( st ) + { + case ConnectedState: + emit connected(); + break; + case ClosingState: + break; + case UnconnectedState: + emit disconnected(); + break; + default: + // nothing + break; + } + if ( QUnixSocket::error() != NoError ) + emit error((QAbstractSocket::SocketError)0); +} +#endif + +bool QWSSocket::connectToLocalFile(const QString &file) +{ +#ifndef QT_NO_SXE + bool result = QUnixSocket::connect( file.toLocal8Bit() ); + if ( !result ) + { + perror( "QWSSocketAuth::connectToLocalFile could not connect:" ); + emit error(QAbstractSocket::ConnectionRefusedError); + return false; + } + return true; +#else + // create socket + int s = ::socket(PF_LOCAL, SOCK_STREAM, 0); + + // connect to socket + struct sockaddr_un a; + memset(&a, 0, sizeof(a)); + a.sun_family = PF_LOCAL; + strncpy(a.sun_path, file.toLocal8Bit().constData(), sizeof(a.sun_path) - 1); + int r = ::connect(s, (struct sockaddr*)&a, SUN_LEN(&a)); + if (r == 0) { + setSocketDescriptor(s); + } else { + perror("QWSSocket::connectToLocalFile could not connect:"); + ::close(s); + emit error(ConnectionRefusedError); + return false; + } +#endif + return true; +} + + +/*********************************************************************** + * + * QWSServerSocket + * + **********************************************************************/ +QWSServerSocket::QWSServerSocket(const QString& file, QObject *parent) +#ifndef QT_NO_SXE + : QUnixSocketServer(parent) +#else + : QTcpServer(parent) +#endif +{ + init(file); +} + +void QWSServerSocket::init(const QString &file) +{ +#ifndef QT_NO_SXE + QByteArray fn = file.toLocal8Bit(); + bool result = QUnixSocketServer::listen( fn ); + if ( !result ) + { + QUnixSocketServer::ServerError err = serverError(); + switch ( err ) + { + case InvalidPath: + qWarning("QWSServerSocket:: invalid path %s", qPrintable(file)); + break; + case ResourceError: + case BindError: + case ListenError: + qWarning("QWSServerSocket:: could not listen on path %s", qPrintable(file)); + break; + default: + break; + } + } +#else + int backlog = 16; //##### + +// create socket + int s = ::socket(PF_LOCAL, SOCK_STREAM, 0); + if (s == -1) { + perror("QWSServerSocket::init"); + qWarning("QWSServerSocket: unable to create socket."); + return; + } + + QByteArray fn = file.toLocal8Bit(); + unlink(fn.constData()); // doesn't have to succeed + + // bind socket + struct sockaddr_un a; + memset(&a, 0, sizeof(a)); + a.sun_family = PF_LOCAL; + strncpy(a.sun_path, fn.constData(), sizeof(a.sun_path) - 1); + int r = ::bind(s, (struct sockaddr*)&a, SUN_LEN(&a)); + if (r < 0) { + perror("QWSServerSocket::init"); + qWarning("QWSServerSocket: could not bind to file %s", fn.constData()); + ::close(s); + return; + } + + if (chmod(fn.constData(), 0600) < 0) { + perror("QWSServerSocket::init"); + qWarning("Could not set permissions of %s", fn.constData()); + ::close(s); + return; + } + + // listen + if (::listen(s, backlog) == 0) { + if (!setSocketDescriptor(s)) + qWarning( "QWSServerSocket could not set descriptor %d : %s", s, errorString().toLatin1().constData()); + } else { + perror("QWSServerSocket::init"); + qWarning("QWSServerSocket: could not listen to file %s", fn.constData()); + ::close(s); + } +#endif +} + +QWSServerSocket::~QWSServerSocket() +{ +} + +#ifndef QT_NO_SXE + +void QWSServerSocket::incomingConnection(int socketDescriptor) +{ + inboundConnections.append( socketDescriptor ); + emit newConnection(); +} + + +QWSSocket *QWSServerSocket::nextPendingConnection() +{ + QMutexLocker locker( &ssmx ); + if ( inboundConnections.count() == 0 ) + return 0; + QWSSocket *s = new QWSSocket(); + s->setSocketDescriptor( inboundConnections.takeFirst() ); + return s; +} + +#endif // QT_NO_SXE + +QT_END_NAMESPACE + +#endif //QT_NO_QWS_MULTIPROCESS diff --git a/src/gui/embedded/qwssocket_qws.h b/src/gui/embedded/qwssocket_qws.h new file mode 100644 index 0000000..6b86d7c --- /dev/null +++ b/src/gui/embedded/qwssocket_qws.h @@ -0,0 +1,120 @@ +/**************************************************************************** +** +** 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$ +** +****************************************************************************/ + +#ifndef QWSSOCKET_QWS_H +#define QWSSOCKET_QWS_H + +#include <QtCore/qconfig.h> +#include <QtGui/qwsutils_qws.h> + +#ifndef QT_NO_QWS_MULTIPROCESS + +#ifndef QT_NO_SXE +#include <QtCore/qmutex.h> +#include <QtGui/private/qunixsocketserver_p.h> +#include <QtGui/private/qunixsocket_p.h> +#else +#include <QtNetwork/qtcpsocket.h> +#include <QtNetwork/qtcpserver.h> +#endif + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +QT_MODULE(Gui) + + +class QWSSocket : public QWS_SOCK_BASE +{ + Q_OBJECT +public: + explicit QWSSocket(QObject *parent=0); + ~QWSSocket(); + + bool connectToLocalFile(const QString &file); + +#ifndef QT_NO_SXE + QString errorString(); +Q_SIGNALS: + void connected(); + void disconnected(); + void error(QAbstractSocket::SocketError); +private Q_SLOTS: + void forwardStateChange(SocketState); +#endif + +private: + Q_DISABLE_COPY(QWSSocket) +}; + + +class QWSServerSocket : public QWS_SOCK_SERVER_BASE +{ + Q_OBJECT +public: + QWSServerSocket(const QString& file, QObject *parent=0); + ~QWSServerSocket(); + +#ifndef QT_NO_SXE + QWSSocket *nextPendingConnection(); +Q_SIGNALS: + void newConnection(); +protected: + void incomingConnection(int socketDescriptor); +private: + QMutex ssmx; + QList<int> inboundConnections; +#endif + +private: + Q_DISABLE_COPY(QWSServerSocket) + + void init(const QString &file); +}; + +QT_END_NAMESPACE + +QT_END_HEADER + +#endif // QT_NO_QWS_MULTIPROCESS + +#endif // QWSSOCKET_QWS_H diff --git a/src/gui/embedded/qwsutils_qws.h b/src/gui/embedded/qwsutils_qws.h new file mode 100644 index 0000000..3aa96d1 --- /dev/null +++ b/src/gui/embedded/qwsutils_qws.h @@ -0,0 +1,98 @@ +/**************************************************************************** +** +** 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$ +** +****************************************************************************/ + +#ifndef QWSUTILS_QWS_H +#define QWSUTILS_QWS_H + +#include <QtCore/QIODevice> + +#ifndef QT_NO_SXE +#define QWS_SOCK_BASE QUnixSocket +#define QWS_SOCK_SERVER_BASE QUnixSocketServer +class QUnixSocket; +class QUnixSocketServer; +#else +#define QWS_SOCK_BASE QTcpSocket +#define QWS_SOCK_SERVER_BASE QTcpServer +class QTcpSocket; +class QTcpServer; +#endif +class QWSSocket; +class QWSServerSocket; + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +QT_MODULE(Gui) + +/******************************************************************** + * + * Convenient socket functions + * + ********************************************************************/ +#ifndef QT_NO_QWS_MULTIPROCESS +inline int qws_read_uint(QIODevice *socket) +{ + if (!socket || socket->bytesAvailable() < (int)sizeof(int)) + return -1; + + int i; + socket->read(reinterpret_cast<char*>(&i), sizeof(i)); + + return i; +} + +inline void qws_write_uint(QIODevice *socket, int i) +{ + if (!socket) + return; + + socket->write(reinterpret_cast<char*>(&i), sizeof(i)); +} + +#endif // QT_NO_QWS_MULTIPROCESS + +QT_END_NAMESPACE + +QT_END_HEADER + +#endif // QWSUTILS_QWS_H |