tools/qvfb/qvfbx11view.cpp
changeset 0 1918ee327afb
child 4 3b1da2848fc7
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/tools/qvfb/qvfbx11view.cpp	Mon Jan 11 14:00:40 2010 +0000
@@ -0,0 +1,389 @@
+/****************************************************************************
+**
+** 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 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 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 "qvfbx11view.h"
+#include "x11keyfaker.h"
+#include <qevent.h>
+#include <QX11Info>
+#include <QTimer>
+#include <QProcess>
+#include <QDebug>
+#include <QUuid>
+#include <QDataStream>
+#include <QTemporaryFile>
+#include <X11/Xlib.h>
+
+QT_BEGIN_NAMESPACE
+
+QVFbX11View::QVFbX11View
+	(int id, int w, int h, int d, Rotation r, QWidget *parent)
+    : QVFbAbstractView(parent)
+{
+    this->id = id;
+    this->w = w;
+    this->h = h;
+    this->d = d;
+    this->rotation = r;
+    this->gr = 1.0;
+    this->gg = 1.0;
+    this->gb = 1.0;
+    this->touchscreen = false;
+    this->lcd = false;
+    this->keyFaker = 0;
+    this->xnest = 0;
+    this->serverAuthFile = 0;
+    this->shutdown = false;
+
+    // Try to find Xephyr, as it is better than Xnest in many ways.
+    if (QFile::exists("/usr/bin/Xephyr"))
+        xserver = "/usr/bin/Xephyr";
+    else if (QFile::exists("/usr/local/bin/Xephyr"))
+        xserver = "/usr/local/bin/Xephyr";
+    else if (QFile::exists("/usr/X11R6/bin/Xephyr"))
+        xserver = "/usr/X11R6/bin/Xephyr";
+    else
+        xserver = "Xnest";
+}
+
+QVFbX11View::~QVFbX11View()
+{
+    shutdown = true;
+    if (xnest) {
+	xnest->terminate();
+	xnestStopped();
+    }
+}
+
+int QVFbX11View::displayId() const
+{
+    return id;
+}
+
+int QVFbX11View::displayWidth() const
+{
+    return ( (int)rotation & 0x01 ) ? h : w;
+}
+
+int QVFbX11View::displayHeight() const
+{
+    return ( (int)rotation & 0x01 ) ? w : h;
+}
+
+int QVFbX11View::displayDepth() const
+{
+    return d;
+}
+
+QVFbX11View::Rotation QVFbX11View::displayRotation() const
+{
+    return rotation;
+}
+
+void QVFbX11View::skinKeyPressEvent(int code, const QString&, bool)
+{
+    if (keyFaker)
+	keyFaker->sendKeyEvent(code, true);
+}
+
+void QVFbX11View::skinKeyReleaseEvent(int code, const QString&, bool)
+{
+    if (keyFaker)
+	keyFaker->sendKeyEvent(code, false);
+}
+
+void QVFbX11View::setGamma(double gr, double gg, double gb)
+{
+    // We remember the values, but don't do anything with them.
+    this->gr = gr;
+    this->gg = gg;
+    this->gb = gb;
+}
+
+double QVFbX11View::gammaRed() const
+{
+    return gr;
+}
+
+double QVFbX11View::gammaGreen() const
+{
+    return gg;
+}
+
+double QVFbX11View::gammaBlue() const
+{
+    return gb;
+}
+
+void QVFbX11View::getGamma(int, QRgb& rgb)
+{
+    rgb = qRgb(255, 255, 255);
+}
+
+bool QVFbX11View::touchScreenEmulation() const
+{
+    return touchscreen;
+}
+
+bool QVFbX11View::lcdScreenEmulation() const
+{
+    return lcd;
+}
+
+int QVFbX11View::rate()
+{
+    // We don't support refresh rates, so return a default value.
+    return 30;
+}
+
+bool QVFbX11View::animating() const
+{
+    // We don't support animation.
+    return false;
+}
+
+QImage QVFbX11View::image() const
+{
+    // We don't support image capture.
+    return QImage();
+}
+
+void QVFbX11View::setRate(int)
+{
+    // We don't support rate adjustments.
+}
+
+
+double QVFbX11View::zoomH() const
+{
+    // Zoom is not possible with Xnest.
+    return 1.0;
+}
+
+double QVFbX11View::zoomV() const
+{
+    // Zoom is not possible with Xnest.
+    return 1.0;
+}
+
+QSize QVFbX11View::sizeHint() const
+{
+    return QSize(w, h);
+}
+
+void QVFbX11View::setTouchscreenEmulation( bool flag )
+{
+    touchscreen = flag;
+}
+
+void QVFbX11View::setLcdScreenEmulation( bool flag )
+{
+    lcd = flag;
+}
+
+void QVFbX11View::setZoom( double, double )
+{
+    // Zoom is not possible with Xnest.
+}
+
+void QVFbX11View::setRotation( Rotation )
+{
+    // Rotation is not possible with Xnest.
+}
+
+void QVFbX11View::startAnimation( const QString& )
+{
+    // Animation is not supported.
+}
+
+void QVFbX11View::stopAnimation()
+{
+    // Animation is not supported.
+}
+
+// Generate a 16-byte magic cookie string.
+static QString generateMagicCookie()
+{
+    static const char hexchars[] = "0123456789abcdef";
+    QUuid uuid = QUuid::createUuid();
+    QByteArray ba;
+    QDataStream stream(&ba, QIODevice::WriteOnly);
+    stream << uuid;
+    QString value;
+    foreach ( char ch, ba ) {
+	value += QChar( hexchars[(ch >> 4) & 0x0F] );
+	value += QChar( hexchars[ch & 0x0F] );
+    }
+    return value;
+}
+
+void QVFbX11View::showEvent(QShowEvent *e)
+{
+    if (!xnest)
+	startXnest();
+    QWidget::showEvent(e);
+}
+
+void QVFbX11View::keyPressEvent(QKeyEvent *e)
+{
+    if (keyFaker)
+	keyFaker->sendKeyEvent(e->key(), true);
+    QWidget::keyPressEvent(e);
+}
+
+void QVFbX11View::keyReleaseEvent(QKeyEvent *e)
+{
+    if (keyFaker)
+	keyFaker->sendKeyEvent(e->key(), false);
+    QWidget::keyReleaseEvent(e);
+}
+
+void QVFbX11View::startXnest()
+{
+    // Add authentication credentials to the XAUTHORITY file.
+    QString cookie = generateMagicCookie();
+    QStringList xauthargs;
+    xauthargs += "add";
+    xauthargs += ":" + QString::number(displayId());
+    xauthargs += "MIT-MAGIC-COOKIE-1";
+    xauthargs += cookie;
+    if (QProcess::execute("xauth", xauthargs) != 0)
+	qWarning() << "xauth: failed to add Xnest client authentication credentials";
+
+    // Write the credentials to another authentication file for the server.
+    serverAuthFile = new QTemporaryFile(this);
+    QString authFilename;
+    if (serverAuthFile->open()) {
+	authFilename = serverAuthFile->fileName();
+	serverAuthFile->close();
+	xauthargs.clear();
+	xauthargs += "-f";
+	xauthargs += authFilename;
+	xauthargs += "add";
+	xauthargs += ":" + QString::number(displayId());
+	xauthargs += "MIT-MAGIC-COOKIE-1";
+	xauthargs += cookie;
+	if (QProcess::execute("xauth", xauthargs) != 0)
+	    qWarning() << "xauth: failed to add Xnest server authentication credentials";
+    }
+
+    // Create a raw X11 window to act as the Xnest's root window.
+    // We cannot use winId() directly because qvfb is already
+    // selecting for events that Xnest wants to select for.
+    WId root = XCreateSimpleWindow
+	(QX11Info::display(), winId(), 0, 0, w, h, 0,
+	 BlackPixel(QX11Info::display(), QX11Info::appScreen()),
+	 BlackPixel(QX11Info::display(), QX11Info::appScreen()));
+    XMapWindow(QX11Info::display(), root);
+
+    // Warn the user if the visual number looks wrong.  Xnest expects
+    // its root window to be on the default visual.
+    if (QX11Info::appVisual() != DefaultVisual(QX11Info::display(), QX11Info::appScreen())) {
+	qWarning() << "*** Qt is not using the default visual.  Xnest may fail "
+		      "with a BadMatch error.";
+	qWarning() << "*** If it fails, then restart qvfb with \" -visual"
+		   << DefaultVisual(QX11Info::display(), QX11Info::appScreen())
+			    ->visualid << "\"";
+    }
+
+    // Make sure the root window is in the X server before Xnest starts.
+    XSync(QX11Info::display(), False);
+
+    // Start the Xnest process.
+    xnest = new QProcess(this);
+    connect(xnest, SIGNAL(error(QProcess::ProcessError)),
+	    this, SLOT(xnestStopped()));
+    connect(xnest, SIGNAL(finished(int,QProcess::ExitStatus)),
+	    this, SLOT(xnestStopped()));
+    QStringList args;
+    args += "-auth";
+    args += authFilename;
+    if (!xserver.contains("Xephyr")) {
+        args += "-geometry";
+        args += QString::number(w) + "x" + QString::number(h) + "+0+0";
+        args += "-depth";
+        args += QString::number(d);
+    }
+    args += "-br";	    // Start Xnest with a black background.
+    args += "-parent";
+    args += "0x" + QString::number(root, 16);
+    args += ":" + QString::number(displayId());
+    xnest->setProcessChannelMode(QProcess::ForwardedChannels);
+    xnest->start(xserver, args);
+    //qDebug() << args;
+
+    QTimer::singleShot(200, this, SLOT(startKeyFaker()));
+}
+
+void QVFbX11View::xnestStopped()
+{
+    if (!shutdown) {
+	if (xnest && xnest->error() == QProcess::FailedToStart)
+	    qWarning() << xserver << "could not be started";
+	else
+	    qWarning() << xserver << "stopped unexpectedly";
+    }
+    if (keyFaker) {
+	delete keyFaker;
+	keyFaker = 0;
+    }
+    if (xnest) {
+	xnest->deleteLater();
+	xnest = 0;
+
+	QStringList xauthargs;
+	xauthargs += "remove";
+	xauthargs += ":" + QString::number(displayId());
+	if (QProcess::execute("xauth", xauthargs) != 0)
+	    qWarning() << "xauth: failed to remove Xnest authentication credentials";
+    }
+    if (serverAuthFile) {
+	delete serverAuthFile;
+	serverAuthFile = 0;
+    }
+}
+
+void QVFbX11View::startKeyFaker()
+{
+    if (!keyFaker && xnest)
+	keyFaker = new X11KeyFaker(":" + QString::number(displayId()), this);
+}
+
+QT_END_NAMESPACE