Merge pull request #4734 from maxGimeno/Demo-WS_Enhancement_and_fixes-maxGimeno

CGAL 3D Demo: Enhancements and fixes
This commit is contained in:
Laurent Rineau 2020-05-27 16:33:25 +02:00
commit a8691e96ad
11 changed files with 363 additions and 184 deletions

View File

@ -402,6 +402,13 @@ add_executable ( CGAL_PMP PMP.cpp )
add_dependencies(CGAL_PMP PMP)
target_link_libraries( CGAL_PMP PRIVATE polyhedron_demo )
add_to_cached_list( CGAL_EXECUTABLE_TARGETS CGAL_PMP )
#WS Server
if(TARGET Qt5::WebSockets)
add_executable (WS_server Server_ws.cpp)
target_link_libraries(WS_server PUBLIC Qt5::WebSockets)
message(STATUS "Qt5WebSockets was found. Using WebSockets is therefore possible.")
endif()
#
# Exporting
#

View File

@ -77,12 +77,6 @@
#include "Color_map.h"
#ifdef CGAL_USE_WEBSOCKETS
#include <QWebSocketServer>
#include <QWebSocket>
#include <QNetworkInterface>
#endif
using namespace CGAL::Three;
QScriptValue
myScene_itemToScriptValue(QScriptEngine *engine,
@ -2940,25 +2934,12 @@ void MainWindow::on_actionSa_ve_Scene_as_Script_triggered()
bool do_upload = false;
#ifdef CGAL_USE_SSH
QString user = settings.value("ssh_user", QString()).toString();
QString pass;
if(!user.isEmpty())
{
QMessageBox::StandardButton doyou =
QMessageBox::question(this, tr("Upload ?"), tr("Do you wish to upload the scene"
" using the SSH preferences ?"));
bool ok;
do_upload = (doyou == QMessageBox::Yes);
if(do_upload)
{
pass = QInputDialog::getText(this, "SSH Password",
"Enter ssh key password:",
QLineEdit::Password,
tr(""),
&ok);
if(!ok)
return;
pass = pass.trimmed();
}
}
#endif
@ -3109,12 +3090,31 @@ void MainWindow::on_actionSa_ve_Scene_as_Script_triggered()
path.prepend("Polyhedron_demo_");
try{
ssh_session session;
bool res = establish_ssh_session(session,
user.toStdString().c_str(),
server.toStdString().c_str(),
pk.toStdString().c_str(),
privK.toStdString().c_str(),
pass.toStdString().c_str());
bool res = establish_ssh_session_from_agent(session,
user.toStdString().c_str(),
server.toStdString().c_str(),
pk.toStdString().c_str());
if(!res)
{
bool ok;
QString pass;
pass = QInputDialog::getText(this, "SSH Password",
"Enter ssh key password:",
QLineEdit::Password,
tr(""),
&ok);
if(!ok)
return;
pass = pass.trimmed();
res = establish_ssh_session(session,
user.toStdString().c_str(),
server.toStdString().c_str(),
pk.toStdString().c_str(),
privK.toStdString().c_str(),
pass.toStdString().c_str());
}
if(!res)
{
QMessageBox::warning(this,
@ -3590,25 +3590,13 @@ void MainWindow::on_actionLoad_a_Scene_from_a_Script_File_triggered()
#ifdef CGAL_USE_SSH
QString user = settings.value("ssh_user", QString()).toString();
QString pass;
if(!user.isEmpty())
{
QMessageBox::StandardButton doyou =
QMessageBox::question(this, tr("Download ?"), tr("Do you wish to download the scene"
" using the SSH preferences ?"));
bool ok;
do_download= (doyou == QMessageBox::Yes);
if(do_download)
{
pass = QInputDialog::getText(this, "SSH Password",
"Enter ssh key password:",
QLineEdit::Password,
tr(""),
&ok);
if(!ok)
return;
pass = pass.trimmed();
}
}
#endif
@ -3623,22 +3611,30 @@ void MainWindow::on_actionLoad_a_Scene_from_a_Script_File_triggered()
server = server.trimmed();
pk = pk.trimmed();
privK=privK.trimmed();
QString path;
path = QInputDialog::getText(this,
"",
tr("Enter the name of the scene file."));
if(path.isEmpty())
return;
if(!path.contains("Polyhedron_demo_"))
path.prepend("Polyhedron_demo_");
try{
ssh_session session;
bool res = establish_ssh_session(session,
user.toStdString().c_str(),
server.toStdString().c_str(),
pk.toStdString().c_str(),
privK.toStdString().c_str(),
pass.toStdString().c_str());
bool res = establish_ssh_session_from_agent(session,
user.toStdString().c_str(),
server.toStdString().c_str(),
pk.toStdString().c_str());
if(!res){
bool ok;
QString pass= QInputDialog::getText(this, "SSH Password",
"Enter ssh key password:",
QLineEdit::Password,
tr(""),
&ok);
if(!ok)
return;
pass = pass.trimmed();
res = establish_ssh_session(session,
user.toStdString().c_str(),
server.toStdString().c_str(),
pk.toStdString().c_str(),
privK.toStdString().c_str(),
pass.toStdString().c_str());
}
if(!res)
{
QMessageBox::warning(this,
@ -3646,7 +3642,22 @@ void MainWindow::on_actionLoad_a_Scene_from_a_Script_File_triggered()
"The SSH session could not be started.");
return;
}
QStringList names;
if(!CGAL::ssh_internal::explore_the_galaxy(session, names))
{
QMessageBox::warning(this,
"Error",
"Could not find remote directory.");
}
QString path;
path = QInputDialog::getItem(this,
"Choose a file",
tr("Choose the scene file."),
names);
filename = QString("%1/load_scene.js").arg(QDir::tempPath());
if(path.isEmpty())
return;
path.prepend("Polyhedron_demo_");
path = tr("/tmp/%2").arg(path);
res = pull_file(session,path.toStdString().c_str(), filename.toStdString().c_str());
if(!res)
@ -3681,90 +3692,3 @@ void MainWindow::on_actionLoad_a_Scene_from_a_Script_File_triggered()
tmp_file.remove();
}
}
#ifdef CGAL_USE_WEBSOCKETS
void MainWindow::on_action_Start_a_Session_triggered()
{
QAction * action= findChild<QAction*>("action_Start_a_Session");
static EchoServer *server =nullptr;
if(action->isChecked()){
server = new EchoServer(1234);
QObject::connect(server, &EchoServer::closed, server,&EchoServer::deleteLater);
}
else
{
server->deleteLater();
}
}
EchoServer::EchoServer(quint16 port) :
QObject(CGAL::Three::Three::mainWindow()),
m_pWebSocketServer(new QWebSocketServer(QStringLiteral("Echo Server"),
QWebSocketServer::NonSecureMode, this))
{
if (m_pWebSocketServer->listen(QHostAddress::Any, port)) {
connect(m_pWebSocketServer, &QWebSocketServer::newConnection,
this, &EchoServer::onNewConnection);
connect(m_pWebSocketServer, &QWebSocketServer::closed, this, &EchoServer::closed);
}
QHostAddress local_host("0.0.0.0");
//to avoid printing 127.0.0.1. Not realy sure it won't ever print the external ipv4 though.
const QHostAddress &localhost = QHostAddress(QHostAddress::LocalHost);
for (const QHostAddress &address: QNetworkInterface::allAddresses()) {
if (address.protocol() == QAbstractSocket::IPv4Protocol && address != localhost)
{
local_host= address;
break;
}
}
QMessageBox mb(QMessageBox::NoIcon, "WS Server",
tr("WebSockets Server started.\nEnter the following address in\nyour Network Preferences to be able to join it :\n"
"ws://%1:%2").arg(local_host.toString()).arg(port), QMessageBox::Ok, CGAL::Three::Three::mainWindow());
mb.setTextInteractionFlags(Qt::TextSelectableByMouse);
mb.exec();
}
EchoServer::~EchoServer()
{
m_pWebSocketServer->close();
qDeleteAll(m_clients.begin(), m_clients.end());
}
void EchoServer::onNewConnection()
{
QWebSocket *pSocket = m_pWebSocketServer->nextPendingConnection();
connect(pSocket, &QWebSocket::textMessageReceived, this, &EchoServer::processTextMessage);
connect(pSocket, &QWebSocket::binaryMessageReceived, this, &EchoServer::processBinaryMessage);
connect(pSocket, &QWebSocket::disconnected, this, &EchoServer::socketDisconnected);
m_clients << pSocket;
}
void EchoServer::processTextMessage(QString message)
{
QWebSocket *pClient = qobject_cast<QWebSocket *>(sender());
for(auto *client : m_clients) {
if(client != pClient)
client->sendTextMessage(message);
}
}
void EchoServer::processBinaryMessage(QByteArray message)
{
QWebSocket *pClient = qobject_cast<QWebSocket *>(sender());
if (pClient) {
pClient->sendBinaryMessage(message);
}
}
void EchoServer::socketDisconnected()
{
QWebSocket *pClient = qobject_cast<QWebSocket *>(sender());
if (pClient) {
m_clients.removeAll(pClient);
pClient->deleteLater();
}
}
#endif

View File

@ -354,10 +354,6 @@ protected Q_SLOTS:
void save(QString filename, QList<CGAL::Three::Scene_item*>& to_save);
//!Calls the function saveSnapShot of the viewer.
void on_actionSaveSnapshot_triggered();
#ifdef CGAL_USE_WEBSOCKETS
//!Starts a new WS server if none is already exist. Else, does nothing.
void on_action_Start_a_Session_triggered();
#endif
//!Opens a Dialog to choose a color and make it the background color.
void setBackgroundColor();
//!Opens a Dialog to change the lighting settings
@ -501,30 +497,5 @@ protected:
private:
bool is_main;
};
#ifdef CGAL_USE_WEBSOCKETS
QT_FORWARD_DECLARE_CLASS(QWebSocketServer)
QT_FORWARD_DECLARE_CLASS(QWebSocket)
class EchoServer : public QObject
{
Q_OBJECT
public:
explicit EchoServer(quint16 port);
~EchoServer();
Q_SIGNALS:
void closed();
private Q_SLOTS:
void onNewConnection();
void processTextMessage(QString message);
void processBinaryMessage(QByteArray message);
void socketDisconnected();
private:
QWebSocketServer *m_pWebSocketServer;
QList<QWebSocket *> m_clients;
};
#endif
#endif // ifndef MAINWINDOW_H

View File

@ -97,7 +97,6 @@
<addaction name="action_Rearrange_Viewers"/>
<addaction name="menuDockWindows"/>
<addaction name="separator"/>
<addaction name="action_Start_a_Session"/>
</widget>
<addaction name="menuFile"/>
<addaction name="menuEdit"/>
@ -470,17 +469,6 @@
<string>Load a Scene &amp;from a Script File...</string>
</property>
</action>
<action name="action_Start_a_Session">
<property name="checkable">
<bool>true</bool>
</property>
<property name="text">
<string>&amp;Start a Session</string>
</property>
<property name="toolTip">
<string>Start a WebSocket Server to Share your Camera with Others on your Network.</string>
</property>
</action>
</widget>
<resources>
<include location="Polyhedron_3.qrc"/>

View File

@ -59,7 +59,7 @@ public :
QMenu* menu = menuFile->findChild<QMenu*>("menuGenerateObject");
if(!menu){
QAction* actionLoad = mw->findChild<QAction*>("actionLoadPlugin");
menu = new QMenu(tr("Generate &Objet"), menuFile);
menu = new QMenu(tr("Generate &Object"), menuFile);
menu->setObjectName("menuGenerateObject");
menuFile->insertMenu(actionLoad, menu);
}

View File

@ -59,7 +59,7 @@ if(EIGEN3_FOUND)
CGAL_target_use_pointmatcher(register_point_sets_plugin)
endif()
else()
message(STATUS "NOTICE: OpenGR and libpointmatcher were not found. Registrationp plugin will not be available.")
message(STATUS "NOTICE: OpenGR and libpointmatcher were not found. Registration plugin will not be available.")
endif()
else(EIGEN3_FOUND)

View File

@ -0,0 +1,100 @@
#include <QtCore/QCoreApplication>
#include <QtCore/QCommandLineParser>
#include <QtCore/QCommandLineOption>
#include <QNetworkInterface>
#include <QApplication>
#include <QMessageBox>
#include "Server_ws.h"
#include <iostream>
EchoServer::EchoServer(quint16 port) :
QObject(),
m_pWebSocketServer(new QWebSocketServer(QStringLiteral("Echo Server"),
QWebSocketServer::NonSecureMode, this))
{
if (m_pWebSocketServer->listen(QHostAddress::Any, port)) {
connect(m_pWebSocketServer, &QWebSocketServer::newConnection,
this, &EchoServer::onNewConnection);
connect(m_pWebSocketServer, &QWebSocketServer::closed, this, &EchoServer::closed);
}
QHostAddress local_host("0.0.0.0");
//to avoid printing 127.0.0.1. Not realy sure it won't ever print the external ipv4 though.
const QHostAddress &localhost = QHostAddress(QHostAddress::LocalHost);
for (const QHostAddress &address: QNetworkInterface::allAddresses()) {
if (address.protocol() == QAbstractSocket::IPv4Protocol && address != localhost)
{
local_host= address;
break;
}
}
QMessageBox mb(QMessageBox::NoIcon, "WS Server",
tr("WebSockets Server started.\nEnter the following address in\nyour Network Preferences to be able to join it :\n"
"ws://%1:%2").arg(local_host.toString()).arg(port), QMessageBox::Ok);
mb.setTextInteractionFlags(Qt::TextSelectableByMouse);
mb.exec();
}
EchoServer::~EchoServer()
{
m_pWebSocketServer->close();
qDeleteAll(m_clients.begin(), m_clients.end());
}
void EchoServer::onNewConnection()
{
QWebSocket *pSocket = m_pWebSocketServer->nextPendingConnection();
connect(pSocket, &QWebSocket::textMessageReceived, this, &EchoServer::processTextMessage);
connect(pSocket, &QWebSocket::binaryMessageReceived, this, &EchoServer::processBinaryMessage);
connect(pSocket, &QWebSocket::disconnected, this, &EchoServer::socketDisconnected);
m_clients << pSocket;
}
void EchoServer::processTextMessage(QString message)
{
QWebSocket *pClient = qobject_cast<QWebSocket *>(sender());
for(auto *client : m_clients) {
if(client != pClient)
client->sendTextMessage(message);
}
}
void EchoServer::processBinaryMessage(QByteArray message)
{
QWebSocket *pClient = qobject_cast<QWebSocket *>(sender());
if (pClient) {
pClient->sendBinaryMessage(message);
}
}
void EchoServer::socketDisconnected()
{
QWebSocket *pClient = qobject_cast<QWebSocket *>(sender());
if (pClient) {
m_clients.removeAll(pClient);
pClient->deleteLater();
}
}
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
QCommandLineParser parser;
parser.setApplicationDescription("WS Server");
parser.addHelpOption();
QCommandLineOption portOption(QStringList() << "p" << "port",
QCoreApplication::translate("main", "Port for echoserver [default: 1234]."),
QCoreApplication::translate("main", "port"), QLatin1Literal("1234"));
parser.addOption(portOption);
parser.process(a);
int port = parser.value(portOption).toInt();
EchoServer *server = new EchoServer(port);
QObject::connect(server, &EchoServer::closed, &a, &QCoreApplication::quit);
return a.exec();
}

View File

@ -0,0 +1,30 @@
#ifndef SERVER_WS_H
#define SERVER_WS_H
#include <QObject>
#include <QWebSocketServer>
#include <QWebSocket>
class EchoServer : public QObject
{
Q_OBJECT
public:
explicit EchoServer(quint16 port);
~EchoServer();
Q_SIGNALS:
void closed();
private Q_SLOTS:
void onNewConnection();
void processTextMessage(QString message);
void processBinaryMessage(QByteArray message);
void socketDisconnected();
private:
QWebSocketServer *m_pWebSocketServer;
QList<QWebSocket *> m_clients;
};
#endif // SERVER_WS_H

View File

@ -20,8 +20,10 @@
#include <vector>
#include <chrono>
#include <thread>
#include <sstream>
#include <QMessageBox>
#include <QStringList>
bool test_result(int res)
{
@ -135,6 +137,79 @@ bool establish_ssh_session(ssh_session &session,
return true;
}
bool establish_ssh_session_from_agent(ssh_session& session,
const char *user,
const char *server,
const char *pub_key_path)
{
int port = 22;
//Can use SSH_LOG_PROTOCOL here for verbose output
int verbosity = SSH_LOG_NOLOG;
int res;
//retry 4 times max each time the connection asks to be retried.
for(int k = 0; k < 4; ++k)
{
session = ssh_new();
ssh_options_set( session, SSH_OPTIONS_LOG_VERBOSITY, &verbosity );
ssh_options_set( session, SSH_OPTIONS_PORT, &port );
ssh_options_set( session, SSH_OPTIONS_USER, user );
ssh_options_set( session, SSH_OPTIONS_HOST, server);
ssh_connect(session);
#if LIBSSH_VERSION_MAJOR <1 && LIBSSH_VERSION_MINOR < 8
if( ssh_is_server_known(session) != SSH_SERVER_KNOWN_OK )
#else
if( ssh_session_is_known_server(session) != SSH_KNOWN_HOSTS_OK )
#endif
{
if(QMessageBox::warning(CGAL::Three::Three::mainWindow(), QString("Unknown Server"),
QString ("The server you are trying to join is not known.\n"
"Do you wish to add it to the known servers list and continue?"),
QMessageBox::Yes | QMessageBox::No) != QMessageBox::Yes)
{
return false;
}
#if LIBSSH_VERSION_MAJOR <1 && LIBSSH_VERSION_MINOR < 8
if( ssh_write_knownhost(session) != SSH_OK )
#else
if( ssh_session_update_known_hosts(session) != SSH_OK )
#endif
{
std::cerr << "writeKnownHost failed" << std::endl;
return false;
}
else
{
ssh_connect(session);
}
}
ssh_key pubkey = ssh_key_new();
ssh_pki_import_pubkey_file(pub_key_path, &pubkey);
res = ssh_userauth_try_publickey(session, NULL, pubkey);
if(res == SSH_AUTH_AGAIN)
ssh_disconnect(session);
else
break;
}
if(!test_result(res))
{
ssh_disconnect(session);
return false;
}
res = ssh_userauth_agent(session, user);
if(!test_result(res))
{
ssh_disconnect(session);
return false;
}
return true;
}
void close_connection(ssh_session &session)
{
ssh_disconnect(session);
@ -282,5 +357,62 @@ bool pull_file(ssh_session &session,
return true;
}
}}
bool explore_the_galaxy(ssh_session &session,
QStringList& files)
{
ssh_channel channel;
int rc;
channel = ssh_channel_new(session);
if (channel == NULL) return false;
rc = ssh_channel_open_session(channel);
if (rc != SSH_OK)
{
ssh_channel_free(channel);
return rc;
}
rc = ssh_channel_request_exec(channel, "ls /tmp");
if (rc != SSH_OK)
{
ssh_channel_close(channel);
ssh_channel_free(channel);
return rc;
}
char buffer[256];
int nbytes;
nbytes = ssh_channel_read(channel, buffer, sizeof(buffer), 0);
while (nbytes > 0)
{
std::string sbuf(buffer, nbytes);
if(sbuf.find("Polyhedron_demo_") != std::string::npos)
{
std::istringstream iss(sbuf);
std::string file;
while(iss >> file)
{
if(file.find("Polyhedron_demo_") != std::string::npos)
{
QString name(file.c_str());
files.push_back(name.remove("Polyhedron_demo_"));
}
}
}
nbytes = ssh_channel_read(channel, buffer, sizeof(buffer), 0);
}
if (nbytes < 0)
{
ssh_channel_close(channel);
ssh_channel_free(channel);
return false;
}
ssh_channel_send_eof(channel);
ssh_channel_close(channel);
ssh_channel_free(channel);
return true;
}
}// end of ssh_internal
}// end of CGAL
#endif

View File

@ -1690,6 +1690,12 @@ void Viewer::setTotalPass(int p)
void Viewer::messageLogged(QOpenGLDebugMessage msg)
{
//filter out useless warning
// From those two links, we decided we didn't care for this warning:
// https://community.khronos.org/t/vertex-shader-in-program-2-is-being-recompiled-based-on-gl-state/76019
// https://stackoverflow.com/questions/12004396/opengl-debug-context-performance-warning
if(msg.message().contains("is being recompiled"))
return;
QString error;
// Format based on severity
@ -2017,7 +2023,15 @@ void Viewer::onTextMessageSocketReceived(QString message)
if(session != d->session){
return;
}
moveCameraToCoordinates(position, 0.05f);
QStringList sl = position.split(" ");
if(sl.size() != 7)
return;
CGAL::qglviewer::Vec pos(sl[0].toDouble(),sl[1].toDouble(),sl[2].toDouble());
CGAL::qglviewer::Quaternion q(sl[3].toDouble(),sl[4].toDouble(),
sl[5].toDouble(),sl[6].toDouble());
camera()->frame()->setPositionAndOrientation(pos, q);
update();
}
#endif
#include "Viewer.moc"

View File

@ -1,6 +1,10 @@
#ifndef USE_SSH_H
#define USE_SSH_H
#include <libssh/libsshpp.hpp>
#include <vector>
class QStringList;
namespace CGAL{
namespace ssh_internal{
//should be used inside a try/catch(ssh::SshException e)
@ -12,6 +16,12 @@ bool establish_ssh_session(ssh_session& session,
const char *pub_key_path,
const char *priv_key_path,
const char *priv_key_password);
bool establish_ssh_session_from_agent(ssh_session& session,
const char *user,
const char *server,
const char *pub_key_path);
void close_connection(ssh_session& session);
bool push_file(ssh_session& session,
@ -21,5 +31,8 @@ bool pull_file(ssh_session &session,
const char *from_path,
const char *to_path);
bool explore_the_galaxy(ssh_session &session,
QStringList &files);
}}
#endif