/* # 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 #include #include // 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" #include "../net/filedownloader.h" // Others #include "../../shared/settings.h" #include "../util/util.h" #include "../util/global.h" // Auto-generated ui class #include "ui_mainwindow.h" /***************************************************************************//** * Initialize MainWindow and ListenServer. * @param ipListUrl * @param parent */ MainWindow::MainWindow(QString ipListUrl, QWidget* parent) : QMainWindow(parent), ui(new Ui::MainWindow), _tbIconSize(24), _tbArea(Qt::LeftToolBarArea) { _sessionNameWindow = new SessionNameWindow(this); ui->setupUi(this); Global::setSessionName(); //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")); // Initialize FileDownloader. if (!ipListUrl.isEmpty()) { _fileDownloader.connectSlot(this, SLOT(onTutorListDownloaded(QByteArray&))); _fileDownloader.downloadFile(QUrl(ipListUrl)); } // Close button in tool bar connect(ui->action_Exit, SIGNAL(triggered()), this, SLOT(onButtonExit())); connect(ui->action_BroadcastScreen, SIGNAL(triggered()), this, SLOT(onButtonStudentToAll())); connect(ui->action_TutorToAll, SIGNAL(triggered()), this, SLOT(onButtonTutorToAll())); connect(ui->action_StudentToTutor, SIGNAL(triggered()), this, SLOT(onButtonStudentToTutor())); connect(ui->action_TutorToStudent, SIGNAL(triggered()), this, SLOT(onButtonTutorToStudent())); connect(ui->action_StopProjection, SIGNAL(triggered()), this, SLOT(onButtonStopProjection())); connect(ui->action_SetAsTutor, SIGNAL(triggered()), this, SLOT(onButtonSetAsTutor())); connect(ui->action_SetAsTutor, SIGNAL(triggered()), this, SLOT(onButtonStopProjection())); connect(ui->action_Lock, SIGNAL(toggled(bool)), this, SLOT(onButtonLock(bool))); /* Stuff for the button lock */ _buttonLockTimer = new QTimer(this); _buttonLockTimer->setSingleShot(true); _buttonLockTimer->setInterval(_buttonBlockTime); _lockingButtons << ui->action_Lock << ui->action_BroadcastScreen << ui->action_TutorToAll << ui->action_StudentToTutor << ui->action_TutorToStudent << ui->action_StopProjection << ui->action_SetAsTutor; // Disable the buttons if a button is clicked foreach (QAction* a, _lockingButtons) connect(a, SIGNAL(triggered()), this, SLOT(DisableButtons())); // Start the timer a button is clicked foreach (QAction* a, _lockingButtons) connect(a, SIGNAL(triggered()), _buttonLockTimer, SLOT(start())); // Enable the buttons if the timer fires connect(_buttonLockTimer, SIGNAL(timeout()), this, SLOT(EnableButtons())); // 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 _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; } /***************************************************************************//** * Place the frames at possible Position. * If current position out of range, place frame into next free cell. * @param frame */ 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::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; } } /***************************************************************************//** * Create new Frame. * Create new frame and add to current available frame list. * Also connect signals frameMoved() and clicked() with slots. * @return ConnectionFrame* */ 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; } /***************************************************************************//** * Load position. * @param settings * @param id * @param x * @param y * @return If loadPosition was successfull. */ 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; } /***************************************************************************//** * Save position of given connectionFrame. * @param cf */ 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 */ /***************************************************************************//** * Handle closeEvent. * If user calls closing MainWindow, close, else ignore. * @param e */ void MainWindow::closeEvent(QCloseEvent* e) { int ret = QMessageBox::question(this, "Test", "Exit?", 0, 1, 2); if (ret == 1) QApplication::exit(0); else e->ignore(); } /***************************************************************************//** * Change Event. * @param e */ void MainWindow::changeEvent(QEvent* e) { QMainWindow::changeEvent(e); } /***************************************************************************//** * Resize event. * @param e */ void MainWindow::resizeEvent(QResizeEvent* e) { QMainWindow::resizeEvent(e); if (_timerId == 0) { _timerId = startTimer(5); } _timerTimeout = 8; } /***************************************************************************//** * Handle Mouse Release Event. * Check if click was inside the frame and if that is the case, set the selection of each connected * client to false. * @param e */ 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::iterator it = _clientFrames.begin(); it != _clientFrames.end(); ++it) { (**it).setSelection(false); } } } /***************************************************************************//** * Handle TimerEvent. * If timerId of event equals _timerId reduce _timerTimeout by 1. * If _timerTimeout equals 1 move toolbar if necessary, otherwise kill timer. * @param event */ 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::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 */ /***************************************************************************//** * Extract information from downloaded tutorList. * Split downloaded file by new line and store IPs in _tutorList. * @param tutorList */ void MainWindow::onTutorListDownloaded(QByteArray& tutorList) { // qDebug() << tutorList; QString data = QString::fromUtf8(tutorList.constData(), tutorList.size()); _tutorList = data.split(QRegExp("[\r\n]"),QString::SkipEmptyParts); qDebug() << _tutorList; } /***************************************************************************//** * Place Frame to from user specified position. * @param frame */ 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::iterator it(_clientFrames.begin()); it != _clientFrames.end(); ++it) { if (*it == frame) continue; if ((*it)->frameGeometry().topLeft() == newpos) { placeFrameInFreeSlot(*it); } } } /***************************************************************************//** * Mark given frame after it was clicked on. * @param frame */ void MainWindow::onFrameClicked(ConnectionFrame* frame) { for (QList::iterator it(_clientFrames.begin()); it != _clientFrames.end(); ++it) { if (*it == frame) frame->setSelection(true); else (**it).setSelection(false); } } /***************************************************************************//** * Show session name, after it was clicked on. */ void MainWindow::onSessionNameClick() { _sessionNameWindow->show(Global::sessionName()); } /***************************************************************************//** * Update session name. */ void MainWindow::onSessionNameUpdate() { _sessionNameLabel->setText(tr("Session Name: %1 [click to edit]").arg(Global::sessionName())); } /***************************************************************************//** * Check if given client is valid. * And if that is the case return true. * @param client * @return If client is valid or not. */ bool MainWindow::isValidClient(Client* client) { for (QList::iterator it(_clientFrames.begin()); it != _clientFrames.end(); ++it) { Client *c = (**it).client(); if (c == client) return true; } return false; } /***************************************************************************//** * Handle VNC settings for a projection from "from" to "to". * Check if projection source is active vnc client. * Check if projection is One --> Many or One --> One. * @param from * @param to */ void MainWindow::prepareForProjection(Client * const from, Client * const to) { // Projection source is never allowed to be an active VNC viewer if (from->isActiveVncClient()) { qDebug("From is active client, stopping..."); from->stopVncClient(); } from->setDesiredProjectionSource(0); if (to == NULL) { // One to many qDebug("One to many requested..."); from->setProjectionSource(true); // Set desired projection source on all clients for (QList::iterator it(_clientFrames.begin()); it != _clientFrames.end(); ++it) { Client *c = (**it).client(); if (c == NULL || c->id() == from->id()) continue; // Self or offline c->setDesiredProjectionSource(from->id()); } if (from->isActiveVncServer()) { // From is already active, if there is at least one active client, assume it is not // shutting down, so we can directly tell the new client to connect to it qDebug("Source is already running a VNC server...."); for (QList::iterator it(_clientFrames.begin()); it != _clientFrames.end(); ++it) { Client *c = (**it).client(); if (c == NULL || c->id() == from->id()) continue; // Self or offline if (c->currentProjectionSource() != from->id()) continue; // Other client // Yep :-) qDebug("Reusing because of active client"); this->onVncServerStateChange(from); return; } qDebug("But no active client found...."); } // Could not take shortcut, (re)start VNC server on source qDebug("Starting VNC server on source machine"); from->startVncServer(); return; } // One to one is desired, figure out what to do with current client to->setProjectionSource(false); if (to->isActiveVncServer()) { if (to->currentProjectionSource() == from->id()) return; // Nothing to do to->stopVncServer(); } to->setDesiredProjectionSource(from->id()); if (from->isActiveVncServer()) { // From is already active, if there is at least one active client, assume it is not // shutting down, so we can directly tell the new client to connect to it for (QList::iterator it(_clientFrames.begin()); it != _clientFrames.end(); ++it) { Client *c = (**it).client(); if (c == NULL || c->id() == from->id()) continue; if (c->currentProjectionSource() != from->id()) continue; // Yep :-) this->onVncServerStateChange(from); return; } } // Could not take shortcut, (re)start VNC server on source from->startVncServer(); } /***************************************************************************//** * Button projection from one student to all the others. * First get which client is projectionSource and set this one as from. * Set projection source for all clients,except the selected one, to false. * Then call prepareForProjection(from, Null). */ void MainWindow::onButtonStudentToAll() { Client *from = NULL; for (QList::iterator it(_clientFrames.begin()); it != _clientFrames.end(); ++it) { Client *c = (**it).client(); if (c == NULL) continue; if ((**it).selected()) from = c; else c->setProjectionSource(false); } if (from == NULL) QMessageBox::critical(this, tr("Projection"), tr("No projection source selected.") ); else prepareForProjection(from, NULL); } /***************************************************************************//** * Handle projection from one student to tutor. * Get the client to project from and get client, who is tutor, as to. * Call prepareForProjection(from, to). */ void MainWindow::onButtonStudentToTutor() { Client *from = NULL; Client *to = NULL; for (QList::iterator it(_clientFrames.begin()); it != _clientFrames.end(); ++it) { Client *c = (**it).client(); if (c == NULL) continue; if ((**it).selected()) from = c; else if ((**it).isTutor()) to = c; } if (from == NULL) QMessageBox::critical(this, tr("Projection"), tr("No projection source selected.") ); else if (to == NULL) QMessageBox::critical(this, tr("Projection"), tr("No tutor defined, or tutor is offline.") ); else if (to == from) QMessageBox::critical(this, tr("Projection"), tr("Selected projection source is tutor.") ); else prepareForProjection(from, to); } /***************************************************************************//** * Handle projection from tutor to all. * Get the client who is tutor and set the projectionSource of all * clients, except the tutor ones, to false. * Call prepareForProjection(from, to). */ void MainWindow::onButtonTutorToAll() { // Client *from = NULL; for (QList::iterator it(_clientFrames.begin()); it != _clientFrames.end(); ++it) { Client *c = (**it).client(); if (c == NULL) continue; if ((**it).isTutor()) from = c; else c->setProjectionSource(false); } if (from == NULL) QMessageBox::critical(this, tr("Projection"), tr("No tutor defined, or tutor is offline.") ); else prepareForProjection(from, NULL); } /***************************************************************************//** * Handle the projection from Tutor to specific student. * Set the client who is tutor as from and the selected client as to. * Call prepareForProjection(from, to). */ void MainWindow::onButtonTutorToStudent() { Client *from = NULL; Client *to = NULL; for (QList::iterator it(_clientFrames.begin()); it != _clientFrames.end(); ++it) { Client *c = (**it).client(); if (c == NULL) continue; if ((**it).selected()) to = c; if ((**it).isTutor()) from = c; } if (to == NULL) QMessageBox::critical(this, tr("Projection"), tr("No projection source selected.") ); else if (from == NULL) QMessageBox::critical(this, tr("Projection"), tr("No tutor defined, or tutor is offline.") ); else if (to == from) QMessageBox::critical(this, tr("Projection"), tr("Selected projection target is tutor.") ); else prepareForProjection(from, to); } /***************************************************************************//** * Handle Button StopProjection. * Set ProjectionSource of each client to false and create a NetworkMessage to * stop the active VNC Server and the active VNC Client(s). */ void MainWindow::onButtonStopProjection() { for (QList::iterator it(_clientFrames.begin()); it != _clientFrames.end(); ++it) { Client *c = (**it).client(); if (c == NULL) continue; c->setDesiredProjectionSource(0); c->setProjectionSource(false); } NetworkMessage stopVncClientmsg; NetworkMessage stopVncServermsg; stopVncClientmsg.setField(_ID, _VNCCLIENT); stopVncServermsg.setField(_ID, _VNCSERVER); for (QList::iterator it(_clientFrames.begin()); it != _clientFrames.end(); ++it) { Client *c = (**it).client(); if (c == NULL) continue; c->sendMessage(stopVncClientmsg); c->sendMessage(stopVncServermsg); } } /***************************************************************************//** * Handle button to lock or unlock screens of client(s). * If already locked, do nothing, else create a NetworkMessage to lock (checked) or * unlock (!checked) the client(s), except the tutor. * @param checked */ 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::iterator it(_clientFrames.begin()); it != _clientFrames.end(); ++it) { Client *c = (**it).client(); // Check if client is Tutor or the manager is also running on this machine. bool isManagerMachine = false; foreach (const QHostAddress &address, QNetworkInterface::allAddresses()) { if (address != QHostAddress(QHostAddress::LocalHost) && c != NULL && c->ip() == address.toString()) isManagerMachine = true; } if (c == NULL || (**it).isTutor() || isManagerMachine) continue; // Don't lock the tutor or the manager running machine. c->sendMessage(msg); } } /***************************************************************************//** * On button exit, close application. */ void MainWindow::onButtonExit() { this->close(); } /***************************************************************************//** * Handle button to set specific client as tutor. * Unset active tutor and set selected client, if not inactive, as new tutor. */ void MainWindow::onButtonSetAsTutor() { bool selected = false; ConnectionFrame *oldTutor = NULL; bool changedTutor = false; for (QList::iterator it(_clientFrames.begin()); it != _clientFrames.end(); ++it) { selected = (*it)->selected(); if (!selected && (*it)->isTutor()) { oldTutor = (*it); (*it)->setTutor(false); } if (selected && ((*it)->client() != NULL)) { // This frame is marked to be Tutor. (*it)->setTutor(true); changedTutor = true; } } if (!changedTutor && oldTutor != NULL) { oldTutor->setTutor(true); } } /***************************************************************************//** * Handle from ListenServer signaled new client connection. * @param client */ 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*))); } /***************************************************************************//** * Authenticate new Client client. * @param client * @param request */ 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::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; } /***************************************************************************//** * After authenticating new client, check if that's the first one and set tutor if necessary. * Also check the current VNC status and take these setting also for the new client. * @param client */ void MainWindow::onClientAuthenticated(Client* client) { disconnect(client, SIGNAL(authenticated(Client*)), this, SLOT(onClientAuthenticated(Client*))); connect(client, SIGNAL(vncServerStateChange(Client*)), this, SLOT(onVncServerStateChange(Client*)), Qt::QueuedConnection); connect(client, SIGNAL(vncClientStateChange(Client*, int)), this, SLOT(onVncClientStateChange(Client*, int)), Qt::QueuedConnection); bool hasActiveTutor = false; ConnectionFrame *existing = NULL; for (QList::iterator it(_clientFrames.begin()); it != _clientFrames.end(); ++it) { if ((*it)->computerId() == client->computerId()) existing = *it; if ((*it)->isTutor()) { if ((*it)->client() != NULL) hasActiveTutor = true; } } bool isTutor = false; if (!hasActiveTutor) { for (int i = 0; i < _tutorList.size(); i++) { // Check if client is possible tutor if (client->computerId() == _tutorList[i]) { isTutor = true; break; } } } if (existing != NULL) { existing->setTutor(isTutor); 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); } // Set Tutor option cf->setTutor(isTutor); // Assign client instance cf->assignClient(client); // ################ 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::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("CLIENTID", QString::number(c->id())); msg.setField("CAPTION", c->name() + " @ " + c->host()); client->sendMessage(msg); client->setDesiredProjectionSource(c->id()); break; } } } /***************************************************************************//** * Handle if VNC Server State has changed. * Check if VNC server has been started/stopped on some client and start VNC server/reset pending * VNC projection information. * @param client */ void MainWindow::onVncServerStateChange(Client* client) { if (!isValidClient(client)) // Check here because this slot is connected queued return; 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("CLIENTID", QString::number(client->id())); msg.setField("CAPTION", client->name() + " @ " + client->host()); for (QList::iterator it(_clientFrames.begin()); it != _clientFrames.end(); ++it) { Client *c = (**it).client(); if (c == NULL || c->id() == client->id()) continue; // Offline or self if (c->currentProjectionSource() == client->id()) continue; // Already watching this client if (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(); //qDebug() << "On VNC stop:" << client->ip() << "was projection source:" << wasInterested; client->setProjectionSource(false); for (QList::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 && QDateTime::currentMSecsSinceEpoch() < _buttonBlockTime) { QMessageBox::information(this, tr("Projection Error"), tr("Could not send screen contents of %1 to other clients: VNC Startup failed.").arg(client->name()) ); } } } /***************************************************************************//** * Handle VNC client state change. * Check if any vnc client is in use, and if that is not the case, stop VNC Server. * @param client * @param lastProjectionSource */ void MainWindow::onVncClientStateChange(Client* client, int lastProjectionSource) { if (!isValidClient(client)) // Check here because this slot is connected queued return; // If last source is not known or not existent, we cannot do anything meaningful here if (lastProjectionSource == 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::iterator it(_clientFrames.begin()); it != _clientFrames.end(); ++it) { Client *c = (**it).client(); if (c == NULL) continue; if (c->id() == lastProjectionSource) server = c; if (c != client && c->currentProjectionSource() == lastProjectionSource) { inUse = true; break; } } // 3. kill server if applicable if (!inUse && server != NULL) server->stopVncServer(); } /***************************************************************************//** * DisableButtons. */ void MainWindow::DisableButtons() { foreach (QAction* a, _lockingButtons) a->setDisabled(true); } /***************************************************************************//** * EnableButtons. */ void MainWindow::EnableButtons() { foreach (QAction* a, _lockingButtons) a->setEnabled(true); }