diff --git a/CMakeLists.txt b/CMakeLists.txt index c56a266..c44d3bc 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -4,7 +4,7 @@ # Daniel Nachbaur cmake_minimum_required(VERSION 3.1 FATAL_ERROR) -project(Deflect VERSION 0.11.0) +project(Deflect VERSION 0.11.1) set(Deflect_VERSION_ABI 4) list(APPEND CMAKE_MODULE_PATH ${PROJECT_SOURCE_DIR}/CMake diff --git a/apps/DesktopStreamer/CMakeLists.txt b/apps/DesktopStreamer/CMakeLists.txt index 883fb8c..74c2347 100644 --- a/apps/DesktopStreamer/CMakeLists.txt +++ b/apps/DesktopStreamer/CMakeLists.txt @@ -18,6 +18,7 @@ set(DESKTOPSTREAMER_SOURCES set(DESKTOPSTREAMER_LINK_LIBRARIES Deflect Qt5::Core + Qt5::Network Qt5::Widgets ) @@ -36,12 +37,11 @@ if(APPLE) list(APPEND DESKTOPSTREAMER_SOURCES AppNapSuspender.mm) list(APPEND DESKTOPSTREAMER_LINK_LIBRARIES "-framework Foundation") if(TARGET Qt5::MacExtras) - option(DESKTOPSTREAMER_ENABLE_MULTIWINDOW "Enable support for streaming multiple windows" OFF) - if(DESKTOPSTREAMER_ENABLE_MULTIWINDOW) - list(APPEND DESKTOPSTREAMER_SOURCES DesktopWindowsModel.mm) - list(APPEND DESKTOPSTREAMER_LINK_LIBRARIES - Qt5::MacExtras "-framework AppKit") - endif() + list(APPEND DESKTOPSTREAMER_HEADERS DesktopWindowsModel.h) + list(APPEND DESKTOPSTREAMER_SOURCES DesktopWindowsModel.mm) + list(APPEND DESKTOPSTREAMER_LINK_LIBRARIES + Qt5::MacExtras "-framework AppKit" + ) endif() if(OSX_VERSION VERSION_LESS 10.9) list(APPEND DESKTOPSTREAMER_LINK_LIBRARIES "-framework ApplicationServices") @@ -55,9 +55,6 @@ endif() common_application(${DESKTOPSTREAMER_APP_NAME} GUI) if(APPLE) - if(TARGET Qt5::MacExtras AND DESKTOPSTREAMER_ENABLE_MULTIWINDOW) - target_compile_definitions(${DESKTOPSTREAMER_APP_NAME} PRIVATE DESKTOPSTREAMER_ENABLE_MULTIWINDOW) - endif() # create a zip for Puppet deployment install(CODE "execute_process(COMMAND zip -r ${DESKTOPSTREAMER_APP_NAME}-${PROJECT_VERSION}.zip ${DESKTOPSTREAMER_APP_NAME}.app diff --git a/apps/DesktopStreamer/MainWindow.cpp b/apps/DesktopStreamer/MainWindow.cpp index 1715c5a..b387d6c 100644 --- a/apps/DesktopStreamer/MainWindow.cpp +++ b/apps/DesktopStreamer/MainWindow.cpp @@ -43,6 +43,7 @@ #include +#include #include #include #include @@ -56,10 +57,9 @@ typedef __int32 int32_t; # include #else # include -# include #endif -#ifdef DESKTOPSTREAMER_ENABLE_MULTIWINDOW +#ifdef DEFLECT_USE_QT5MACEXTRAS # include "DesktopWindowsModel.h" #endif @@ -67,14 +67,17 @@ typedef __int32 int32_t; #define FAILURE_UPDATE_DELAY 100 #define FRAME_RATE_DAMPING 0.1f // influence of new value between 0-1 +namespace +{ const std::vector< std::pair< QString, QString > > defaultHosts = { { "DisplayWall Ground floor", "bbpav02.epfl.ch" }, { "DisplayWall 3rd floor", "bbpav04.epfl.ch" }, { "DisplayWall 5th floor", "bbpav05.epfl.ch" }, { "DisplayWall 6th floor", "bbpav06.epfl.ch" } }; - +const QString streamButtonDefaultText = "Stream"; const QString streamSelected = "Stream selected item(s)"; +} MainWindow::MainWindow() : _streamID( 0 ) @@ -82,11 +85,10 @@ MainWindow::MainWindow() { setupUi( this ); -#ifndef __APPLE__ - // Event injection support is currently limited to OSX - _remoteControlLabel->setVisible( false ); - _remoteControlCheckBox->setVisible( false ); -#endif + for( const auto& entry : defaultHosts ) + _hostComboBox->addItem( entry.first, entry.second ); + + _hostComboBox->setCurrentIndex( -1 ); // no default host selected initially connect( _hostComboBox, &QComboBox::currentTextChanged, [&]( const QString& text ) @@ -95,47 +97,42 @@ MainWindow::MainWindow() _listView->setEnabled( !text.isEmpty( )); }); - for( const auto& entry : defaultHosts ) - _hostComboBox->addItem( entry.first, entry.second ); - - // no default host selected initially - _hostComboBox->setCurrentIndex( -1 ); + _streamIdLineEdit->setText( QHostInfo::localHostName( )); - char hostname[256] = { 0 }; - gethostname( hostname, 256 ); - _streamIdLineEdit->setText( QString( "%1" ).arg( hostname )); - -#ifdef DESKTOPSTREAMER_ENABLE_MULTIWINDOW - _listView->setModel( new DesktopWindowsModel ); - - // select 'Desktop' item as initial default stream item - _listView->setCurrentIndex( _listView->model()->index( 0, 0 )); - _streamButton->setText( streamSelected ); - - const int itemsHorizontal = std::min( 3.f, - std::ceil( std::sqrt( float(_listView->model()->rowCount( ))))); - const int itemsVertical = std::min( 3.f, - std::ceil(float( _listView->model()->rowCount( )) / itemsHorizontal )); - - // 230 (itemSize + spacing), frameWidth for decorations - resize( QSize( 230 * itemsHorizontal + 2 * _listView->frameWidth(), - 230 * itemsVertical + 2 * _listView->frameWidth() + 50 )); -#else - _listView->setHidden( true ); - adjustSize(); -#endif + connect( _streamButton, &QPushButton::clicked, + this, &MainWindow::_update ); + connect( _streamButton, &QPushButton::clicked, + _actionMultiWindowMode, &QAction::setDisabled ); connect( _remoteControlCheckBox, &QCheckBox::clicked, this, &MainWindow::_onStreamEventsBoxClicked ); - connect( _streamButton, &QPushButton::clicked, - this, &MainWindow::_update ); + connect( _actionAdvancedSettings, &QAction::triggered, + this, &MainWindow::_showAdvancedSettings ); + + connect( _actionMultiWindowMode, &QAction::triggered, + [this]( const bool checked ) + { + if( checked ) + _showMultiWindowMode(); + else + _showSingleWindowMode(); + }); connect( _actionAbout, &QAction::triggered, this, &MainWindow::_openAboutWidget ); - // Update timer connect( &_updateTimer, &QTimer::timeout, this, &MainWindow::_update ); + +#ifndef __APPLE__ + // Event injection support is currently limited to OSX + _showRemoteControl( false ); +#endif +#ifndef DEFLECT_USE_QT5MACEXTRAS + _actionMultiWindowMode->setVisible( false ); +#endif + _showAdvancedSettings( false ); + _showSingleWindowMode(); } MainWindow::~MainWindow() {} @@ -193,11 +190,69 @@ void MainWindow::_update() _stopStreaming(); } +void MainWindow::_showRemoteControl( const bool visible ) +{ + _remoteControlLabel->setVisible( visible ); + _remoteControlCheckBox->setVisible( visible ); +} + +void MainWindow::_showMultiWindowMode() +{ +#ifdef DEFLECT_USE_QT5MACEXTRAS + if( !_listView->model( )) + { + auto model = new DesktopWindowsModel(); + model->setParent( _listView ); + _listView->setModel( model ); + } + + _listView->setVisible( true ); + + // select 'Desktop' item as initial default stream item + _listView->setCurrentIndex( _listView->model()->index( 0, 0 )); + _streamButton->setText( streamSelected ); + + const int itemsHorizontal = std::min( 3.f, + std::ceil( std::sqrt( float(_listView->model()->rowCount( ))))); + const int itemsVertical = std::min( 3.f, + std::ceil(float( _listView->model()->rowCount( )) / itemsHorizontal )); + + layout()->setSizeConstraint( QLayout::SetDefaultConstraint ); + setFixedSize( QWIDGETSIZE_MAX, QWIDGETSIZE_MAX ); + // 230 (itemSize + spacing), frameWidth for decorations + resize( QSize( 230 * itemsHorizontal + 2 * _listView->frameWidth(), + 230 * itemsVertical + 2 * _listView->frameWidth() + 50 )); +#endif +} + +void MainWindow::_showSingleWindowMode() +{ + _listView->setHidden( true ); + _streamButton->setText( streamButtonDefaultText ); + + layout()->setSizeConstraint( QLayout::SetFixedSize ); +} + +void MainWindow::_showAdvancedSettings( const bool visible ) +{ + _maxFrameRateSpinBox->setVisible( visible ); + _maxFrameRateLabel->setVisible( visible ); + + _streamIdLineEdit->setVisible( visible ); + _streamIdLabel->setVisible( visible ); +} + void MainWindow::_updateStreams() { - const std::string& host = _getStreamHost(); + if( _actionMultiWindowMode->isChecked( )) + _updateMultipleStreams(); + else + _updateSingleStream(); +} -#ifdef DESKTOPSTREAMER_ENABLE_MULTIWINDOW +void MainWindow::_updateMultipleStreams() +{ +#ifdef DEFLECT_USE_QT5MACEXTRAS const QModelIndexList windowIndices = _listView->selectionModel()->selectedIndexes(); @@ -219,12 +274,12 @@ void MainWindow::_updateStreams() const int pid = index.isValid() ? _listView->model()->data( index, DesktopWindowsModel::ROLE_PID).toInt(): 0; + const std::string host = _getStreamHost(); StreamPtr stream( new Stream( *this, index, streamId, host, pid )); if( !stream->isConnected( )) { - _statusbar->showMessage( QString( "Could not connect to host %1" ). - arg( host.c_str( ))); + _showConnectionErrorStatus(); continue; } @@ -240,9 +295,11 @@ void MainWindow::_updateStreams() _streamButton->setChecked( true ); _startStreaming(); }; +#endif +} -#else // No window list: Stream button toggles - +void MainWindow::_updateSingleStream() +{ if( !_streamButton->isChecked( )) { _stopStreaming(); @@ -254,7 +311,7 @@ void MainWindow::_updateStreams() const QPersistentModelIndex index; // default == use desktop StreamPtr stream( new Stream( *this, index, _streamIdLineEdit->text().toStdString(), - host )); + _getStreamHost( ))); if( stream->isConnected( )) { if( _remoteControlCheckBox->isChecked( )) @@ -263,9 +320,14 @@ void MainWindow::_updateStreams() _startStreaming(); } else - _statusbar->showMessage( "Could not connect to host" ); + _showConnectionErrorStatus(); } -#endif +} + +void MainWindow::_showConnectionErrorStatus() +{ + _statusbar->showMessage( QString( "Cannot connect to host: '%1'" ). + arg( _getStreamHost().c_str( ))); } void MainWindow::_processStreamEvents() @@ -306,22 +368,20 @@ void MainWindow::_shareDesktopUpdate() } } -#ifdef DESKTOPSTREAMER_ENABLE_MULTIWINDOW void MainWindow::_deselect( ConstStreamPtr stream ) { - const QPersistentModelIndex& index = stream->getIndex(); - if( index.isValid( )) + if( _actionMultiWindowMode->isChecked( )) { - QItemSelectionModel* model = _listView->selectionModel(); - model->select( index, QItemSelectionModel::Deselect ); + const QPersistentModelIndex& index = stream->getIndex(); + if( index.isValid( )) + { + QItemSelectionModel* model = _listView->selectionModel(); + model->select( index, QItemSelectionModel::Deselect ); + } } + else + _streamButton->setChecked( false ); } -#else -void MainWindow::_deselect( ConstStreamPtr ) -{ - _streamButton->setChecked( false ); -} -#endif void MainWindow::_regulateFrameRate() { diff --git a/apps/DesktopStreamer/MainWindow.h b/apps/DesktopStreamer/MainWindow.h index 473cdd1..71935e8 100644 --- a/apps/DesktopStreamer/MainWindow.h +++ b/apps/DesktopStreamer/MainWindow.h @@ -38,8 +38,8 @@ /* or implied, of The University of Texas at Austin. */ /*********************************************************************/ -#ifndef MAIN_WINDOW_H -#define MAIN_WINDOW_H +#ifndef MAINWINDOW_H +#define MAINWINDOW_H #include @@ -92,9 +92,19 @@ private slots: AppNapSuspender _napSuspender; #endif + void _showMultiWindowMode(); + void _showSingleWindowMode(); + + void _showRemoteControl( bool visible ); + void _showAdvancedSettings( bool visible ); + void _startStreaming(); void _stopStreaming(); void _updateStreams(); + void _updateMultipleStreams(); + void _updateSingleStream(); + void _showConnectionErrorStatus(); + void _deselect( ConstStreamPtr stream ); void _processStreamEvents(); void _shareDesktopUpdate(); diff --git a/apps/DesktopStreamer/MainWindow.ui b/apps/DesktopStreamer/MainWindow.ui index aed8fee..296d7c0 100644 --- a/apps/DesktopStreamer/MainWindow.ui +++ b/apps/DesktopStreamer/MainWindow.ui @@ -33,7 +33,7 @@ - QLayout::SetFixedSize + QLayout::SetMinimumSize QFormLayout::ExpandingFieldsGrow @@ -51,7 +51,7 @@ 6 - + Stream to @@ -86,44 +86,10 @@ - - - - 268 - 40 - - - - false - - - false - - - Stream - - - true - - - false - - - false - - - false - - - false - - + + false - - - - 0 @@ -165,42 +131,82 @@ + + + + false + + + + 268 + 40 + + + + false + + + false + + + Stream + + + true + + + false + + + false + + + false + + + false + + + false + + + - + - Stream name + Remote control - - - - 0 - 0 - - - + + false - + - Remote control + Stream name - - + + + + 0 + 0 + + + false - + Frame rate @@ -247,6 +253,14 @@ + + + View + + + + + @@ -255,6 +269,22 @@ About + + + true + + + Multi-window mode (experimental) + + + + + true + + + Advanced settings + + diff --git a/apps/DesktopStreamer/Stream.cpp b/apps/DesktopStreamer/Stream.cpp index 8317df5..bff09ec 100644 --- a/apps/DesktopStreamer/Stream.cpp +++ b/apps/DesktopStreamer/Stream.cpp @@ -41,7 +41,7 @@ #include "MainWindow.h" #ifdef __APPLE__ -# ifdef DESKTOPSTREAMER_ENABLE_MULTIWINDOW +# ifdef DEFLECT_USE_QT5MACEXTRAS # include "DesktopWindowsModel.h" # endif # if MAC_OS_X_VERSION_MIN_REQUIRED >= MAC_OS_X_VERSION_10_9 @@ -121,31 +121,29 @@ std::string update() { QPixmap pixmap; -#ifdef DESKTOPSTREAMER_ENABLE_MULTIWINDOW - if( !_window.isValid( )) - return "Window does not exist anymore"; - - const QAbstractItemModel* model = _parent.getItemModel(); - if( _window.row() > 0 ) + if( !_window.isValid( ) || _window.row() == 0 ) + { + pixmap = QApplication::primaryScreen()->grabWindow( 0 ); + _windowRect = QRect( 0, 0, pixmap.width() / _parent.devicePixelRatio(), + pixmap.height() / _parent.devicePixelRatio( )); + } + else { +#ifdef DEFLECT_USE_QT5MACEXTRAS + const QAbstractItemModel* model = _parent.getItemModel(); pixmap = model->data( _window, DesktopWindowsModel::ROLE_PIXMAP ).value< QPixmap >(); _windowRect = model->data( _window, DesktopWindowsModel::ROLE_RECT ).value< QRect >(); - } - else #endif - { - pixmap = QApplication::primaryScreen()->grabWindow( 0 ); - _windowRect = QRect( 0, 0, pixmap.width() / _parent.devicePixelRatio(), - pixmap.height() / _parent.devicePixelRatio( )); } if( pixmap.isNull( )) return "Got no pixmap for desktop or window"; QImage image = pixmap.toImage(); -#ifdef DESKTOPSTREAMER_ENABLE_MULTIWINDOW +#ifdef DEFLECT_USE_QT5MACEXTRAS + const QAbstractItemModel* model = _parent.getItemModel(); // render mouse cursor only on active window and full desktop streams if( DesktopWindowsModel::isActive( _pid ) || model->data( _window, Qt::DisplayRole ) == "Desktop" ) @@ -181,7 +179,7 @@ void _sendMouseEvent( const CGEventType type, const CGMouseButton button, CGEventRef event = CGEventCreateMouseEvent( 0, type, point, button ); CGEventSetType( event, type ); -#ifdef DESKTOPSTREAMER_ENABLE_MULTIWINDOW +#ifdef DEFLECT_USE_QT5MACEXTRAS // If the destination app is not active, store the event in a queue and // consume it after it's been activated (next iteration of main run loop) if( !DesktopWindowsModel::isActive( _pid )) diff --git a/doc/Changelog.md b/doc/Changelog.md index c8b77f2..88acc8c 100644 --- a/doc/Changelog.md +++ b/doc/Changelog.md @@ -3,7 +3,16 @@ Changelog {#Changelog} ## Deflect 0.11 -### 0.11.0 (git master) +### 0.11.1 (git master) + +* [112](https://github.com/BlueBrain/Deflect/pull/112): + DesktopStreamer improvements: + - new "view" menu to show advanced options (stream id, fps), hidden by default + - the experimental multi-window streaming on OSX can be selected at runtime + through the view menu (no longer a CMake option) + - improved resizing policy of the main window + +### 0.11.0 (17-06-2016) * [111](https://github.com/BlueBrain/Deflect/pull/111): DesktopStreamer: bugfix; stop streaming when the server closes the stream