summaryrefslogtreecommitdiffstats
path: root/src/server/mainwindow/mainwindow.cpp
diff options
context:
space:
mode:
authorsr2013-02-04 19:50:31 +0100
committersr2013-02-04 19:50:31 +0100
commit1a5709501f94014d41987b956338bb6424b9f90c (patch)
treed3b93fe8dc406bca56aff147ef5cc4cbf9ed6be0 /src/server/mainwindow/mainwindow.cpp
parentTest (diff)
downloadpvs2-1a5709501f94014d41987b956338bb6424b9f90c.tar.gz
pvs2-1a5709501f94014d41987b956338bb6424b9f90c.tar.xz
pvs2-1a5709501f94014d41987b956338bb6424b9f90c.zip
Initial commit
Diffstat (limited to 'src/server/mainwindow/mainwindow.cpp')
-rw-r--r--src/server/mainwindow/mainwindow.cpp633
1 files changed, 633 insertions, 0 deletions
diff --git a/src/server/mainwindow/mainwindow.cpp b/src/server/mainwindow/mainwindow.cpp
new file mode 100644
index 0000000..8c57b91
--- /dev/null
+++ b/src/server/mainwindow/mainwindow.cpp
@@ -0,0 +1,633 @@
+/*
+ # Copyright (c) 2009 - OpenSLX Project, Computer Center University of Freiburg
+ #
+ # This program is free software distributed under the GPL version 2.
+ # See http://openslx.org/COPYING
+ #
+ # If you have any feedback please consult http://openslx.org/feedback and
+ # send your suggestions, praise, or complaints to feedback@openslx.org
+ #
+ # General information about OpenSLX can be found at http://openslx.org/
+ # -----------------------------------------------------------------------------
+ # mainWindow.cpp
+ This is the Main class for the pvsManager. The GUI is constructed here.
+ # -----------------------------------------------------------------------------
+ */
+// Self
+#include "mainwindow.h"
+// QT stuff
+#include <QtGui>
+#include <QFileDialog>
+// Other custom UI elements
+#include "../clicklabel/clicklabel.h"
+#include "../sessionnamewindow/sessionnamewindow.h"
+#include "../connectionframe/connectionframe.h"
+// Network
+#include "../net/listenserver.h"
+#include "../net/client.h"
+#include "../net/discoverylistener.h"
+#include "../../shared/networkmessage.h"
+// Others
+#include "../../shared/settings.h"
+#include "../util/util.h"
+#include "../util/global.h"
+// Auto-generated ui class
+#include "ui_mainwindow.h"
+
+MainWindow::MainWindow(QWidget* parent) :
+ QMainWindow(parent), ui(new Ui::MainWindow), _tbIconSize(24), _tbArea(Qt::LeftToolBarArea),
+ _buttonBlockTime(0)
+
+{
+ _sessionNameWindow = new SessionNameWindow(this);
+ ui->setupUi(this);
+
+ Global::setSessionName("1234");
+
+ //conWin = new ConnectionWindow(ui->widget);
+ //ui->VconWinLayout->addWidget(conWin);
+ //conList = new ConnectionList(ui->ClWidget);
+ //ui->ClientGLayout->addWidget(conList);
+
+ // Show only session name label, not textbox
+ _sessionNameLabel = new ClickLabel(this);
+ ui->mainLayout->addWidget(_sessionNameLabel);
+
+ ui->frmRoom->setStyleSheet("background-color:#444");
+
+ // toolbar and actions in pvsmgr
+ ui->action_Exit->setStatusTip(tr("Exit"));
+ ui->action_Lock->setStatusTip(tr("Lock or Unlock all Clients"));
+
+ // Close button in tool bar
+ connect(ui->action_Exit, SIGNAL(triggered()), this, SLOT(onButtonExit()));
+ connect(ui->action_BroadcastScreen, SIGNAL(triggered()), this, SLOT(onButtonBroadcast()));
+ connect(ui->action_Lock, SIGNAL(toggled(bool)), this, SLOT(onButtonLock(bool)));
+ // Clicking the session name label shows the edit field for it
+ connect(_sessionNameLabel, SIGNAL(clicked()), this, SLOT(onSessionNameClick()));
+ // Listen to updates to the session name through the session name window
+ connect(_sessionNameWindow, SIGNAL(updateSessionName()), this,
+ SLOT(onSessionNameUpdate()));
+
+ setAttribute(Qt::WA_QuitOnClose);
+ setUnifiedTitleAndToolBarOnMac(true);
+ this->showMaximized(); // show the Mainwindow maximized
+
+ _tilesX = 9;
+ _tilesY = 7;
+ _tileWidth = 10;
+ _tileHeight = 10;
+
+ _timerId = 0;
+ _timerTimeout = 0;
+
+ _listenServer = new ListenServer(CLIENT_PORT);
+ connect(_listenServer, SIGNAL(newClient(Client*)), this, SLOT(onClientConnected(Client*)));
+ _discoveryListener = new DiscoveryListener();
+
+ // Finally
+ this->onSessionNameUpdate();
+ _timerId = startTimer(10);
+ _timerTimeout = 8;
+}
+
+MainWindow::~MainWindow()
+{
+ _sessionNameLabel->deleteLater();
+ delete ui;
+}
+
+void MainWindow::placeFrameInFreeSlot(ConnectionFrame* frame)
+{
+ // Get occupied cell of each frame and store status in an array
+ const int elems = _tilesX * _tilesY;
+ int currentIndex = 0;
+ bool grid[elems];
+ memset(grid, 0, sizeof(bool) * elems); // set everything to false
+ for (QList<ConnectionFrame*>::iterator it(_clientFrames.begin()); it != _clientFrames.end(); ++it)
+ {
+ const QPoint &oldpos = (*it)->frameGeometry().topLeft();
+ const int tx = oldpos.x() / _tileWidth;
+ const int ty = oldpos.y() / _tileHeight;
+ const int index = tx + ty * _tilesX;
+ if (frame == *it)
+ {
+ // Current frame is the frame to place, remember its old index
+ currentIndex = index;
+ continue;
+ }
+ if (index < 0 || index >= elems)
+ {
+ // Current frame is out of bounds, place in top left corner
+ qDebug("Move A");
+ (*it)->move(0, 0);
+ grid[0] = true;
+ continue;
+ }
+ // Just mark cell as occupied in array
+ grid[index] = true;
+ }
+ // Safety
+ if (currentIndex < 0 || currentIndex >= elems)
+ currentIndex = 0;
+ // Now place in next free cell, start looking at current position of frame
+ for (int i = 0; i < elems; ++i)
+ {
+ const int index = (i + currentIndex) % elems;
+ if (grid[index])
+ continue;
+ const int x = (index % _tilesX) * _tileWidth;
+ const int y = (index / _tilesX) * _tileHeight;
+ //qDebug() << "Index=" << index << " i=" << i << " cI=" << currentIndex << " elems=" << elems << " x=" << x << " y=" << y;
+ qDebug("Move B");
+ frame->move(x, y);
+ break;
+ }
+}
+
+ConnectionFrame* MainWindow::createFrame()
+{
+ // Allocate and resize
+ ConnectionFrame *cf = new ConnectionFrame(ui->frmRoom, _tileWidth, _tileHeight);
+ _clientFrames.append(cf);
+ cf->show();
+ connect(cf, SIGNAL(frameMoved(ConnectionFrame *)), this, SLOT(onPlaceFrame(ConnectionFrame *)));
+ connect(cf, SIGNAL(clicked(ConnectionFrame *)), this, SLOT(onFrameClicked(ConnectionFrame *)));
+ return cf;
+}
+
+bool MainWindow::loadPosition(QSettings& settings, const QString& id, int& x, int& y)
+{
+ settings.beginGroup("client_position");
+ const QVariant retval(settings.value(id));
+ settings.endGroup();
+ if (retval.type() != QVariant::Point)
+ return false;
+ const QPoint point(retval.toPoint());
+ x = point.x();
+ y = point.y();
+ return true;
+}
+
+void MainWindow::savePosition(ConnectionFrame *cf)
+{
+ Client *client = cf->client();
+ if (client == NULL)
+ return;
+ QPoint pos(cf->pos().x() / _tileWidth, cf->pos().y() / _tileHeight);
+ USER_SETTINGS(settings);
+ settings.beginGroup("client_position");
+ settings.setValue(client->computerId(), pos);
+ settings.endGroup();
+}
+
+/*
+ * Overridden methods
+ */
+
+void MainWindow::closeEvent(QCloseEvent* e)
+{
+ int ret = QMessageBox::question(this, "Test", "Beenden?", 0, 1, 2);
+ if (ret == 1)
+ QApplication::exit(0);
+ else
+ e->ignore();
+}
+
+void MainWindow::changeEvent(QEvent* e)
+{
+ QMainWindow::changeEvent(e);
+}
+
+void MainWindow::resizeEvent(QResizeEvent* e)
+{
+ QMainWindow::resizeEvent(e);
+ if (_timerId == 0)
+ {
+ _timerId = startTimer(5);
+ }
+ _timerTimeout = 8;
+}
+
+void MainWindow::mouseReleaseEvent(QMouseEvent* e)
+{
+ // Try to figure out if the user clicked inside the "room" frame
+ // No idea if there is a more elegant way to do this without
+ // sub-classing that frame and overriding its mouseReleaseEvent
+ const QPoint pos(ui->frmRoom->mapFrom(this, e->pos()));
+ if (pos.x() < 0 || pos.y() < 0)
+ return;
+ const QSize frame(ui->frmRoom->size());
+ if (frame.width() > pos.x() && frame.height() > pos.y())
+ {
+ for (QList<ConnectionFrame*>::iterator it = _clientFrames.begin(); it != _clientFrames.end(); ++it)
+ {
+ (**it).setSelection(false);
+ }
+ }
+}
+
+void MainWindow::timerEvent(QTimerEvent* event)
+{
+ if (event->timerId() == _timerId)
+ {
+ _timerTimeout -= 1;
+ if (_timerTimeout == 1)
+ {
+ // Move toolbar if necessary
+ const int iconSize = this->size().width() / 30 + 14;
+ const int barSpace = ui->toolBar->actions().size() * (iconSize + 8);
+ const Qt::ToolBarArea area =
+ (barSpace < this->size().height() ? Qt::LeftToolBarArea : Qt::TopToolBarArea);
+ if (_tbIconSize != iconSize)
+ {
+ _tbIconSize = iconSize;
+ ui->toolBar->setIconSize(QSize(iconSize, iconSize));
+ }
+ if (_tbArea != area)
+ {
+ _tbArea = area;
+ this->addToolBar(area, ui->toolBar);
+ }
+ return;
+ }
+ // Kill timer when done
+ if (_timerTimeout <= 0)
+ {
+ killTimer(_timerId);
+ _timerId = 0;
+ // Resize all connection windows
+ if (ui->frmRoom->size().width() < 100 || ui->frmRoom->size().height() < 100 || _tilesX <= 0 || _tilesY <= 0)
+ return;
+ const int nw = ui->frmRoom->size().width() / _tilesX;
+ const int nh = ui->frmRoom->size().height() / _tilesY;
+ for (QList<ConnectionFrame*>::iterator it = _clientFrames.begin(); it != _clientFrames.end(); ++it)
+ {
+ const QPoint &oldpos = (*it)->frameGeometry().topLeft();
+ qDebug("Move C");
+ (*it)->move((oldpos.x() / _tileWidth) * nw, (oldpos.y() / _tileHeight) * nh);
+ (*it)->setSize(nw, nh);
+ }
+ _tileWidth = nw;
+ _tileHeight = nh;
+ }
+ }
+ else
+ {
+ // Unknown timer ID, kill
+ killTimer(event->timerId());
+ }
+}
+
+/*
+ * Slots
+ */
+
+void MainWindow::onPlaceFrame(ConnectionFrame* frame)
+{
+ if (_tilesX <= 0 || _tilesY <= 0)
+ return;
+ const QPoint &p = frame->frameGeometry().center();
+ const QSize &s = ui->frmRoom->geometry().size();
+ int x = (p.x() / _tileWidth) * _tileWidth;
+ int y = (p.y() / _tileHeight) * _tileHeight;
+ if (x < 0)
+ x = 0;
+ else if (x > s.width() - _tileWidth)
+ x = (_tilesX - 1) * _tileWidth;
+ if (y < 0)
+ y = 0;
+ else if (y > s.height() - _tileHeight)
+ y = (_tilesY - 1) * _tileHeight;
+ qDebug("Move D");
+ frame->move(x, y);
+ savePosition(frame);
+ const QPoint &newpos = frame->frameGeometry().topLeft();
+ for (QList<ConnectionFrame*>::iterator it(_clientFrames.begin()); it != _clientFrames.end(); ++it)
+ {
+ if (*it == frame)
+ continue;
+ if ((*it)->frameGeometry().topLeft() == newpos)
+ {
+ placeFrameInFreeSlot(*it);
+ }
+ }
+}
+
+void MainWindow::onFrameClicked(ConnectionFrame* frame)
+{
+ for (QList<ConnectionFrame*>::iterator it(_clientFrames.begin()); it != _clientFrames.end(); ++it)
+ {
+ if (*it == frame)
+ frame->setSelection(true);
+ else
+ (**it).setSelection(false);
+ }
+}
+
+void MainWindow::onSessionNameClick()
+{
+ _sessionNameWindow->show(Global::sessionName());
+}
+
+void MainWindow::onSessionNameUpdate()
+{
+ _sessionNameLabel->setText(tr("Session Name: %1 [click to edit]").arg(Global::sessionName()));
+}
+
+void MainWindow::onButtonSettings()
+{
+ // Move to any free tile
+ placeFrameInFreeSlot(createFrame());
+}
+
+void MainWindow::onButtonChat()
+{
+ /*
+ for (QList<ConnectionFrame*>::iterator it = _clientFrames.begin(); it != _clientFrames.end(); ++it) {
+ (*it)->setSize(100 + qrand() % 200);
+ }
+ */
+}
+
+void MainWindow::onButtonBroadcast()
+{
+ const qint64 now = QDateTime::currentMSecsSinceEpoch();
+ if (now < _buttonBlockTime)
+ return;
+ _buttonBlockTime = now + 3000;
+ Client *source = NULL; // the desired source (selected frame)
+ Client *oldSource = NULL; // if there is a client that is already broadcaster, save it here
+ for (QList<ConnectionFrame*>::iterator it(_clientFrames.begin()); it != _clientFrames.end(); ++it)
+ {
+ Client *c = (**it).client();
+ if ((**it).selected())
+ source = c;
+ if (c != NULL && c->isProjectionSource())
+ oldSource = c;
+ }
+ if (source == NULL && oldSource == NULL)
+ {
+ QMessageBox::information(this, tr("No Projection Source selected"), tr("You did not select any active client as the connection source."));
+ return;
+ }
+ if (oldSource != NULL)
+ {
+ // Is already broadcast source, disable
+ oldSource->setProjectionSource(false);
+ oldSource->stopVncServer();
+ if (source == NULL || source->id() == oldSource->id())
+ return;
+ }
+ source->setProjectionSource(true);
+ for (QList<ConnectionFrame*>::iterator it(_clientFrames.begin()); it != _clientFrames.end(); ++it)
+ {
+ Client *c = (**it).client();
+ if (c == NULL || c->id() == source->id())
+ continue;
+ //c->setDesiredProjectionSource(source->id());
+ c->setProjectionSource(false);
+ }
+ source->startVncServer();
+}
+
+void MainWindow::onButtonLock(bool checked)
+{
+ NetworkMessage msg;
+ msg.setField(_ID, _LOCK);
+ if (checked)
+ msg.setField("ENABLE", QByteArray("1"));
+ else
+ msg.setField("ENABLE", QByteArray("0"));
+ for (QList<ConnectionFrame*>::iterator it(_clientFrames.begin()); it != _clientFrames.end(); ++it)
+ {
+ Client *c = (**it).client();
+ if (c == NULL || (**it).isTutor())
+ continue; // Don't lock the tutor
+ c->sendMessage(msg);
+ }
+}
+
+void MainWindow::onButtonExit()
+{
+ this->close();
+}
+
+void MainWindow::onClientConnected(Client* client)
+{
+ qDebug("ListenServer told MainWindow about new connection");
+ connect(client, SIGNAL(authenticating(Client*, ClientLogin*)), this, SLOT(onClientAuthenticating(Client*, ClientLogin*)));
+ connect(client, SIGNAL(authenticated(Client*)), this, SLOT(onClientAuthenticated(Client*)));
+ connect(client, SIGNAL(vncClientStateChange(Client*)), this, SLOT(onVncClientStateChange(Client*)));
+}
+
+void MainWindow::onClientAuthenticating(Client* client, ClientLogin* request)
+{
+ disconnect(client, SIGNAL(authenticating(Client*, ClientLogin*)), this, SLOT(onClientAuthenticating(Client*, ClientLogin*)));
+ if (!request->accept) // Another receiver of that signal already rejected the client, so nothing to do
+ return;
+ bool inuse;
+ QString check = request->name;
+ int addnum = 1;
+ do
+ {
+ inuse = false;
+ for (QList<ConnectionFrame*>::iterator it(_clientFrames.begin()); it != _clientFrames.end(); ++it)
+ {
+ Client *c = (**it).client();
+ if (c == NULL)
+ continue;
+ if (!c->isAuthed())
+ continue;
+ if (c->ip() == request->ip)
+ {
+ request->accept = false;
+ return;
+ }
+ if (c->name() == check)
+ {
+ inuse = true;
+ check = request->name + " (" + QString::number(++addnum) + ")";
+ break;
+ }
+ }
+ } while (inuse && addnum < 100);
+ if (inuse)
+ request->accept = false;
+ else
+ request->name = check;
+}
+
+void MainWindow::onClientAuthenticated(Client* client)
+{
+ disconnect(client, SIGNAL(authenticated(Client*)), this, SLOT(onClientAuthenticated(Client*)));
+ connect(client, SIGNAL(vncServerStateChange(Client*)), this, SLOT(onVncServerStateChange(Client*)));
+ bool hasActiveTutor = false;
+ ConnectionFrame *deadTutor = NULL;
+ bool anyClient = false;
+ ConnectionFrame *existing = NULL;
+ for (QList<ConnectionFrame*>::iterator it(_clientFrames.begin()); it != _clientFrames.end(); ++it)
+ {
+ if ((*it)->computerId() == client->computerId())
+ existing = *it;
+ if ((*it)->client() != NULL)
+ anyClient = true;
+ if ((*it)->isTutor())
+ {
+ if ((*it)->client() == NULL)
+ deadTutor = (*it)->client();
+ else
+ hasActiveTutor = true;
+ }
+ }
+ if (!anyClient && deadTutor != NULL)
+ deadTutor->setTutor(false);
+ if (existing != NULL)
+ {
+ if (!anyClient && !hasActiveTutor)
+ existing->setTutor(true);
+ existing->assignClient(client);
+ return;
+ }
+ // New one, create
+ ConnectionFrame *cf = createFrame();
+ // Try to load last known position
+ int x, y;
+ bool ok;
+ USER_SETTINGS(usr);
+ ok = loadPosition(usr, client->computerId(), x, y);
+ if (!ok)
+ {
+ SYSTEM_SETTINGS(sys);
+ ok = loadPosition(sys, client->computerId(), x, y);
+ }
+ if (x >= _tilesX || y >= _tilesY)
+ ok = false;
+ if (ok)
+ {
+ if (x < 0)
+ x = 0;
+ if (y < 0)
+ y = 0;
+ qDebug("Move E");
+ cf->move(x * _tileWidth, y * _tileHeight);
+ onPlaceFrame(cf);
+ }
+ else
+ {
+ // Move to any free tile
+ placeFrameInFreeSlot(cf);
+ }
+ // Assign client instance
+ cf->assignClient(client);
+ // Make first active client tutor
+ if (!anyClient && !hasActiveTutor)
+ cf->setTutor(true);
+ // ################
+ NetworkMessage msg;
+ // If clients are currently locked, tell this new client
+ if (ui->action_Lock->isChecked())
+ {
+ msg.reset();
+ msg.setField(_ID, _LOCK);
+ msg.setField("ENABLE", QByteArray("1"));
+ client->sendMessage(msg);
+ }
+ // Same for VNC projections
+ for (QList<ConnectionFrame*>::iterator it(_clientFrames.begin()); it != _clientFrames.end(); ++it)
+ {
+ Client *c = (**it).client();
+ if (c != NULL && c != client && c->isActiveVncServer() && c->isProjectionSource())
+ {
+ msg.reset();
+ msg.setField(_ID, _VNCCLIENT);
+ msg.setField("HOST", c->ip());
+ msg.setField("PORT", QString::number(c->vncPort()));
+ msg.setField("ROPASS", c->vncRoPass());
+ msg.setField("CAPTION", c->name() + " @ " + c->host());
+ client->sendMessage(msg);
+ break;
+ }
+ }
+}
+
+void MainWindow::onVncServerStateChange(Client* client)
+{
+ if (client->vncPort() > 0)
+ {
+ // VNC Server started on some client - start projection on all clients interested in that client's screen
+ NetworkMessage msg;
+ msg.setField(_ID, _VNCCLIENT);
+ msg.setField("HOST", client->ip());
+ msg.setField("PORT", QString::number(client->vncPort()));
+ msg.setField("ROPASS", client->vncRoPass());
+ msg.setField("CAPTION", client->name() + " @ " + client->host());
+ for (QList<ConnectionFrame*>::iterator it(_clientFrames.begin()); it != _clientFrames.end(); ++it)
+ {
+ Client *c = (**it).client();
+ if (c == NULL)
+ continue; // Offline
+ if (c->id() == client->id() ||
+ (c->desiredProjectionSource() != client->id() && !client->isProjectionSource()))
+ continue; // Not interested
+ c->sendMessage(msg);
+ }
+ }
+ else
+ {
+ // VNC server stopped on some client or failed to start - reset local pending projection information
+ // If at least one other client seemed to be waiting for this client's screen, pop up a message
+ // on the console
+ bool wasInterested = client->isProjectionSource();
+ client->setProjectionSource(true);
+ for (QList<ConnectionFrame*>::iterator it(_clientFrames.begin()); it != _clientFrames.end(); ++it)
+ {
+ Client *c = (**it).client();
+ if (c == NULL)
+ continue;
+ if (c->desiredProjectionSource() == client->id())
+ {
+ wasInterested = true;
+ c->setDesiredProjectionSource(0);
+ }
+ }
+ if (wasInterested)
+ {
+ QMessageBox::information(this,
+ tr("Projection Error"),
+ tr("Could not send screen contents of %1 to other clients: VNC Startup failed.").arg(client->name())
+ );
+ }
+ }
+}
+
+void MainWindow::onVncClientStateChange(Client* client)
+{
+ // If the client started projection, ignore event. Also if
+ // the source is not known (anymore), we cannot do anything meaningful here
+ if (client->isActiveVncClient() || client->currentProjectionSource() == 0)
+ return;
+ // Client is not a vnc client anymore. Check if the VNC server it was (supposedly) connected
+ // to is still running, and kill it if there are no more clients connected to it.
+ // (client->currentProjectionSource() will still contain the id of the server it was connected to during this event)
+ // 1. check if vnc server is still running
+ // 2. check if there are any other clients "using" that server
+ bool inUse = false;
+ Client *server = NULL;
+ for (QList<ConnectionFrame*>::iterator it(_clientFrames.begin()); it != _clientFrames.end(); ++it)
+ {
+ Client *c = (**it).client();
+ if (c == NULL || c == client)
+ continue;
+ if (c->currentProjectionSource() == client->currentProjectionSource())
+ {
+ inUse = true;
+ break;
+ }
+ if (c->id() == client->currentProjectionSource())
+ server = c;
+ }
+ // 3. kill server if applicable
+ if (!inUse && server != NULL)
+ server->stopVncServer();
+}