| /**************************************************************************** |
| ** |
| ** Copyright (C) 2011 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$ |
| ** GNU Lesser General Public License Usage |
| ** 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. |
| ** |
| ** 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. |
| ** |
| ** Other Usage |
| ** Alternatively, this file may be used in accordance with the terms and |
| ** conditions contained in a signed written agreement between you and Nokia. |
| ** |
| ** |
| ** |
| ** |
| ** |
| ** $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 |