tools/qvfb/qvfbshmem.cpp
changeset 0 1918ee327afb
child 4 3b1da2848fc7
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/tools/qvfb/qvfbshmem.cpp	Mon Jan 11 14:00:40 2010 +0000
@@ -0,0 +1,314 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (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 Technology Preview License Agreement accompanying
+** this package.
+**
+** 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.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qlock_p.h"
+
+#include "qvfbshmem.h"
+#include "qvfbhdr.h"
+
+#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 = QT_VFB_DATADIR(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;
+
+    qws_dataDir(displayid);
+
+    QString oldPipe = "/tmp/qtembedded-" + username + "/" + QString("QtEmbedded-%1").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());
+        }
+    }
+
+    displayPipe = QTE_PIPE_QVFB(displayid);
+
+    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;
+
+    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