/****************************************************************************
**
** 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