/*
* vncserver.cpp
*
* Created on: 24.01.2013
* Author: sr
*/
#include <QApplication>
#include <QProcess>
#include <QDesktopWidget>
#include "vncserver.h"
#include "../util/util.h"
VncServer* VncServer::me = NULL;
/***************************************************************************//**
* @brief VncServer::instance
* @return
*/
VncServer* VncServer::instance()
{
if (me == NULL)
me = new VncServer();
return me;
}
/***************************************************************************//**
* @brief makePassword
* @param len
* @return
*/
static QString makePassword(int len = 10)
{
char pass[len];
for (int i = 0; i < len; ++i)
pass[i] = 43 + qrand() % 80;
return QString::fromUtf8(pass, len);
}
/***************************************************************************//**
* @brief Ugly hack to get an el-cheapo platform independent sleep
*/
struct Sleeper : public QThread {
static void msleep(unsigned long msecs) { QThread::msleep(msecs); }
};
/***************************************************************************//**
* @brief VncServer::VncServer
*/
VncServer::VncServer() : _process(NULL), _port(0), _timerId(0) {}
/***************************************************************************//**
* @brief VncServer::~VncServer
*/
VncServer::~VncServer() {}
/***************************************************************************//**
* @brief VncServer::start
*/
void VncServer::start()
{
// Keep things clean
if (_process != NULL) {
disconnect(_process, SIGNAL(error(QProcess::ProcessError)), this, SLOT(onError(QProcess::ProcessError)));
disconnect(_process, SIGNAL(finished(int)), this, SLOT(onFinished(int)));
}
this->stop();
// Generate passwords
_rwpass = makePassword();
_ropass = makePassword();
// Create new password file
QDir path = Util::settingsDir();
QString pwfile(path.absoluteFilePath("vncpass"));
QFile pwhandle(pwfile);
if (pwhandle.exists())
pwhandle.remove();
if (!pwhandle.open(QIODevice::WriteOnly)) {
qDebug() << "Could not open " << pwfile << " for writing";
emit started(0, _ropass, _rwpass);
return;
}
pwhandle.setPermissions(QFile::ReadOwner | QFile::WriteOwner);
pwhandle.write(_rwpass.toUtf8().constData());
pwhandle.write("\n");
pwhandle.write(_ropass.toUtf8().constData());
pwhandle.write("\n");
pwhandle.close();
// Create new process
_process = new QProcess(this);
connect(_process, SIGNAL(readyReadStandardOutput()), this, SLOT(onStdOut()));
connect(_process, SIGNAL(readyReadStandardError()), this, SLOT(onStdErr()));
connect(_process, SIGNAL(error(QProcess::ProcessError)), this, SLOT(onError(QProcess::ProcessError)));
connect(_process, SIGNAL(finished(int)), this, SLOT(onFinished(int)));
_timerId = startTimer(4000);
QStringList args;
args << "-forever";
args << "-display" << ":0";
args << "-passwdfile" << (QString("rm:" + pwfile));
args << "-shared";
args << "-repeat";
args << "-autoport" << QString::number(54112);
// Get rect of primary screen
const QDesktopWidget desktop;
const QRect primaryRect = desktop.screenGeometry();
// Tell x11vnc to just use primary screen
args << "-clip";
QString rect;
rect = QString::number(primaryRect.width())
+ "x" + QString::number(primaryRect.height())
+ "+" + QString::number(primaryRect.x())
+ "+" + QString::number(primaryRect.y());
args << rect;
qDebug() << "Arguments are: " << args;
_process->start("x11vnc", args, QIODevice::ReadOnly);
}
/***************************************************************************//**
* @brief VncServer::stop
*/
void VncServer::stop()
{
if (_timerId != 0) {
killTimer(_timerId);
_timerId = 0;
}
if (_process == NULL)
return;
qDebug("Stopping old VNC server.");
disconnect(_process, SIGNAL(readyReadStandardOutput()), this, SLOT(onStdOut()));
disconnect(_process, SIGNAL(readyReadStandardError()), this, SLOT(onStdErr()));
QProcess *process = _process;
_process = NULL;
_port = 0;
process->terminate();
for (int i = 0; i < 10 && process->state() != QProcess::NotRunning; ++i)
Sleeper::msleep(10);
if (process->state() == QProcess::Running)
process->kill();
for (int i = 0; i < 10 && process->state() != QProcess::NotRunning; ++i)
Sleeper::msleep(10);
process->deleteLater();
}
/*
* Overrides
*/
/***************************************************************************//**
* @brief Timer event, currently only used to assume VNC server setup failed
* after 3 seconds...
* @param event
*/
void VncServer::timerEvent(QTimerEvent *event)
{
// Error timeout (3s), tell server that vnc setup failed
this->stop();
emit started(0, _ropass, _rwpass);
}
/*
* Slots
*/
/***************************************************************************//**
* @brief VncServer::onStdOut
*/
void VncServer::onStdOut()
{
if (_process == NULL) {
qDebug("VncServer::onStdOut() called in bad state.");
return;
}
QByteArray data(_process->readAllStandardOutput());
qDebug() << "x11vnc: " << data;
if (_port <= 0) {
const int pos = data.indexOf("PORT=", 0);
if (pos != -1) {
_port = atoi(data.constData() + pos + 5);
qDebug() << "Got VNC port " << _port << ", ro " << _ropass << ", rw " << _rwpass;
emit started(_port, _ropass, _rwpass);
// Kill error timer, but only if port seemed valid
if (_timerId != 0 && _port > 0) {
killTimer(_timerId);
_timerId = 0;
}
}
}
}
/***************************************************************************//**
* @brief VncServer::onStdErr
*/
void VncServer::onStdErr()
{
if (_process == NULL) {
qDebug("VncServer::onStdErr() called in bad state.");
return;
}
QByteArray data(_process->readAllStandardError());
}
/***************************************************************************//**
* @brief VncServer::onError
* @param error
*/
void VncServer::onError(QProcess::ProcessError error)
{
this->stop();
emit started(0, _ropass, _rwpass);
}
/***************************************************************************//**
* @brief VncServer::onFinished
* @param exitCode
*/
void VncServer::onFinished(int exitCode)
{
this->stop();
emit started(0, _ropass, _rwpass);
}