diff options
Diffstat (limited to 'tools/qvfb/qvfbshmem.cpp')
-rw-r--r-- | tools/qvfb/qvfbshmem.cpp | 314 |
1 files changed, 314 insertions, 0 deletions
diff --git a/tools/qvfb/qvfbshmem.cpp b/tools/qvfb/qvfbshmem.cpp new file mode 100644 index 0000000..d07cd0f --- /dev/null +++ b/tools/qvfb/qvfbshmem.cpp @@ -0,0 +1,314 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the tools applications 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" + +#include "qvfbshmem.h" +#include "qvfbhdr.h" + +#define QTE_PIPE "QtEmbedded-%1" + +#include <QFile> +#include <QTimer> + +#include <stdlib.h> +#include <unistd.h> +#include <sys/ipc.h> +#include <sys/types.h> +#include <sys/shm.h> +#include <sys/stat.h> +#include <sys/sem.h> +#include <sys/mman.h> +#include <fcntl.h> +#include <errno.h> +#include <math.h> + +QT_BEGIN_NAMESPACE + +#ifdef Q_WS_QWS +#error qvfb must be compiled with the Qt for X11 package +#endif + +// Get the name of the directory where Qt for Embedded Linux temporary data should +// live. +static QString qws_dataDir(int qws_display_id) +{ + QByteArray dataDir = QString("/tmp/qtembedded-%1").arg(qws_display_id).toLocal8Bit(); + if (mkdir(dataDir, 0700)) { + if (errno != EEXIST) { + qFatal("Cannot create Qt for Embedded Linux data directory: %s", dataDir.constData()); + } + } + + struct stat buf; + if (lstat(dataDir, &buf)) + qFatal("stat failed for Qt for Embedded Linux data directory: %s", dataDir.constData()); + + if (!S_ISDIR(buf.st_mode)) + qFatal("%s is not a directory", dataDir.constData()); + if (buf.st_uid != getuid()) + qFatal("Qt for Embedded Linux data directory is not owned by user %uh", getuid()); + + if ((buf.st_mode & 0677) != 0600) + qFatal("Qt for Embedded Linux data directory has incorrect permissions: %s", dataDir.constData()); + dataDir += "/"; + + return QString(dataDir); +} + + +static QString displayPipe; +static QString displayPiped; +class DisplayLock +{ +public: + DisplayLock() : qlock(0) { + if (QFile::exists(displayPiped)) { + qlock = new QLock(displayPipe, 'd', false); + qlock->lock(QLock::Read); + } + } + ~DisplayLock() { + if (qlock) { + qlock->unlock(); + delete qlock; + qlock = 0; + } + } +private: + QLock *qlock; +}; + +QShMemViewProtocol::QShMemViewProtocol(int displayid, const QSize &s, + int d, QObject *parent) + : QVFbViewProtocol(displayid, parent), hdr(0), dataCache(0), lockId(-1), + windowId(0) +{ + int w = s.width(); + int h = s.height(); + + QString username = "unknown"; + const char *logname = getenv("LOGNAME"); + if ( logname ) + username = logname; + + QString oldPipe = "/tmp/qtembedded-" + username + "/" + QString(QTE_PIPE).arg(displayid); + int oldPipeSemkey = ftok(oldPipe.toLatin1().constData(), 'd'); + if (oldPipeSemkey != -1) { + int oldPipeLockId = semget(oldPipeSemkey, 0, 0); + if (oldPipeLockId >= 0){ + sembuf sops; + sops.sem_num = 0; + sops.sem_op = 1; + sops.sem_flg = SEM_UNDO; + int rv; + do { + rv = semop(lockId,&sops,1); + } while (rv == -1 && errno == EINTR); + + perror("QShMemViewProtocol::QShMemViewProtocol"); + qFatal("Cannot create lock file as an old version of QVFb has " + "opened %s. Close other QVFb and try again", + oldPipe.toLatin1().constData()); + } + } + + kh = new QVFbKeyPipeProtocol(displayid); + /* should really depend on receiving qt version, but how can + one tell? */ + mh = new QVFbMousePipe(displayid); + + QString mousePipe = mh->pipeName(); + + key_t key = ftok(mousePipe.toLatin1().constData(), 'b'); + + int bpl; + if (d < 8) + bpl = (w * d + 7) / 8; + else + bpl = w * ((d + 7) / 8); + + displaySize = bpl * h; + + unsigned char *data; + uint data_offset_value = sizeof(QVFbHeader); + + int dataSize = bpl * h + data_offset_value; + shmId = shmget(key, dataSize, IPC_CREAT | 0666); + if (shmId != -1) + data = (unsigned char *)shmat(shmId, 0, 0); + else { + struct shmid_ds shm; + shmctl(shmId, IPC_RMID, &shm); + shmId = shmget(key, dataSize, IPC_CREAT | 0666); + if (shmId == -1) { + perror("QShMemViewProtocol::QShMemViewProtocol"); + qFatal("Cannot get shared memory 0x%08x", key); + } + data = (unsigned char *)shmat(shmId, 0, 0); + } + + if ((long)data == -1) { + delete kh; + delete mh; + perror("QShMemViewProtocol::QShMemViewProtocol"); + qFatal("Cannot attach to shared memory %d",shmId); + } + dataCache = (unsigned char *)malloc(displaySize); + memset(dataCache, 0, displaySize); + memset(data+sizeof(QVFbHeader), 0, displaySize); + + hdr = (QVFbHeader *)data; + hdr->width = w; + hdr->height = h; + hdr->depth = d; + hdr->linestep = bpl; + hdr->dataoffset = data_offset_value; + hdr->update = QRect(); + hdr->dirty = 0; + hdr->numcols = 0; + hdr->viewerVersion = QT_VERSION; + hdr->brightness = 255; + hdr->windowId = 0; + + displayPipe = qws_dataDir(displayid) + QString(QTE_PIPE).arg(displayid); + + displayPiped = displayPipe + 'd'; + + + mRefreshTimer = new QTimer(this); + connect(mRefreshTimer, SIGNAL(timeout()), this, SLOT(flushChanges())); +} + +QShMemViewProtocol::~QShMemViewProtocol() +{ + struct shmid_ds shm; + shmdt( (char*)hdr ); + shmctl( shmId, IPC_RMID, &shm ); + free(dataCache); + delete kh; + delete mh; +} + +int QShMemViewProtocol::width() const +{ + return hdr->width; +} + +int QShMemViewProtocol::height() const +{ + return hdr->height; +} + +int QShMemViewProtocol::depth() const +{ + return hdr->depth; +} + +int QShMemViewProtocol::linestep() const +{ + return hdr->linestep; +} + +int QShMemViewProtocol::numcols() const +{ + return hdr->numcols; +} + +QVector<QRgb> QShMemViewProtocol::clut() const +{ + QVector<QRgb> vector(hdr->numcols); + for (int i=0; i < hdr->numcols; ++i) + vector[i]=hdr->clut[i]; + + return vector; +} + +unsigned char *QShMemViewProtocol::data() const +{ + return dataCache; + //return ((unsigned char *)hdr)+hdr->dataoffset; +} + +int QShMemViewProtocol::brightness() const +{ + return hdr->brightness; +} + +void QShMemViewProtocol::flushChanges() +{ + QRect r; + if (hdr->dirty) { + DisplayLock(); + + hdr->dirty = false; + r = hdr->update; + hdr->update = QRect(); + + if (hdr->windowId != windowId) { + windowId = hdr->windowId; + emit displayEmbedRequested(hdr->windowId); + } else if (!hdr->windowId) { + // copy the memory area, for now, be inefficient. + memcpy(dataCache, ((char *)hdr) + hdr->dataoffset, displaySize); + } + } + emit displayDataChanged(r); +} + +void QShMemViewProtocol::setRate(int interval) +{ + if (interval > 0) + return mRefreshTimer->start(1000/interval); + else + mRefreshTimer->stop(); +} + +int QShMemViewProtocol::rate() const +{ + int i = mRefreshTimer->interval(); + if (i > 0) + return 1000/i; + else + return 0; +} + +QT_END_NAMESPACE |