diff --git a/.gitmodules b/.gitmodules new file mode 100644 index 00000000000..a81bc81747b --- /dev/null +++ b/.gitmodules @@ -0,0 +1,3 @@ +[submodule "src/3rdparty/qt5-x11embed"] + path = src/3rdparty/qt5-x11embed + url = https://github.com/Lukas-W/qt5-x11embed.git diff --git a/.travis/linux..install.sh b/.travis/linux..install.sh index a5fc3e4d540..c42276f5048 100755 --- a/.travis/linux..install.sh +++ b/.travis/linux..install.sh @@ -4,11 +4,13 @@ set -e PACKAGES="cmake libsndfile-dev fftw3-dev libvorbis-dev libogg-dev libmp3lame-dev libasound2-dev libjack-dev libsdl-dev libsamplerate0-dev libstk0-dev stk - libfluidsynth-dev portaudio19-dev wine-dev g++-multilib libfltk1.3-dev + libfluidsynth-dev portaudio19-dev g++-multilib libfltk1.3-dev libgig-dev libsoundio-dev" +VST_PACKAGES="wine-dev libqt5x11extras5-dev qtbase5-private-dev libxcb-util0-dev libxcb-keysyms1-dev" + # Help with unmet dependencies -PACKAGES="$PACKAGES libjack0" +PACKAGES="$PACKAGES $VST_PACKAGES libjack0" if [ "$QT5" ]; then PACKAGES="$PACKAGES qt58base qt58translations qt58tools" diff --git a/include/RemotePlugin.h b/include/RemotePlugin.h index 2d811487587..8881b179b8f 100644 --- a/include/RemotePlugin.h +++ b/include/RemotePlugin.h @@ -414,6 +414,7 @@ class shmFifo enum RemoteMessageIDs { IdUndefined, + IdHostInfoGotten, IdInitDone, IdQuit, IdSampleRateInformation, @@ -427,6 +428,8 @@ enum RemoteMessageIDs IdChangeInputOutputCount, IdShowUI, IdHideUI, + IdToggleUI, + IdIsUIVisible, IdSaveSettingsToString, IdSaveSettingsToFile, IdLoadSettingsFromString, @@ -783,6 +786,12 @@ class EXPORT RemotePlugin : public QObject, public RemotePluginBase bool init( const QString &pluginExecutable, bool waitForInitDoneMsg ); + inline void waitForHostInfoGotten() + { + m_failed = waitForMessage( IdHostInfoGotten ).id + != IdHostInfoGotten; + } + inline void waitForInitDone( bool _busyWaiting = true ) { m_failed = waitForMessage( IdInitDone, _busyWaiting ).id != IdInitDone; @@ -801,18 +810,21 @@ class EXPORT RemotePlugin : public QObject, public RemotePluginBase unlock(); } - void showUI() + + void toggleUI() { lock(); - sendMessage( IdShowUI ); + sendMessage( IdToggleUI ); unlock(); } - void hideUI() + int isUIVisible() { lock(); - sendMessage( IdHideUI ); + sendMessage( IdIsUIVisible ); unlock(); + message m = waitForMessage( IdIsUIVisible ); + return m.id != IdIsUIVisible ? -1 : m.getInt() ? 1 : 0; } inline bool failed() const @@ -830,6 +842,9 @@ class EXPORT RemotePlugin : public QObject, public RemotePluginBase m_commMutex.unlock(); } +public slots: + void showUI(); + void hideUI(); protected: inline void setSplittedChannels( bool _on ) @@ -1206,6 +1221,7 @@ RemotePluginClient::RemotePluginClient( const char * socketPath ) : m_vstSyncData = (VstSyncData *) m_shmQtID.data(); m_bufferSize = m_vstSyncData->m_bufferSize; m_sampleRate = m_vstSyncData->m_sampleRate; + sendMessage( IdHostInfoGotten ); return; } #else @@ -1233,6 +1249,7 @@ RemotePluginClient::RemotePluginClient( const char * socketPath ) : { m_bufferSize = m_vstSyncData->m_bufferSize; m_sampleRate = m_vstSyncData->m_sampleRate; + sendMessage( IdHostInfoGotten ); // detach segment if( shmdt(m_vstSyncData) == -1 ) @@ -1248,6 +1265,12 @@ RemotePluginClient::RemotePluginClient( const char * socketPath ) : // if attaching shared memory fails sendMessage( IdSampleRateInformation ); sendMessage( IdBufferSizeInformation ); + if( waitForMessage( IdBufferSizeInformation ).id + != IdBufferSizeInformation ) + { + fprintf( stderr, "Could not get buffer size information\n" ); + } + sendMessage( IdHostInfoGotten ); } diff --git a/plugins/CMakeLists.txt b/plugins/CMakeLists.txt index 28a1a8afa3f..7fd0a6d5acf 100644 --- a/plugins/CMakeLists.txt +++ b/plugins/CMakeLists.txt @@ -35,6 +35,29 @@ IF(LMMS_MINIMAL) SET(PLUGIN_LIST ${MINIMAL_LIST} ${PLUGIN_LIST}) ENDIF() +OPTION(LMMS_EMBED_VST "Turn on to embed VSTs as subwindows. Turn off to open VSTs in separate windows." ON) + +IF(WANT_QT5) + OPTION(LMMS_EMBED_VST_X11 "Turn on to embed VSTs using the X11Embed protocol. Turn off to use Qt's own embedding, which may be broken with your Qt version." ON) +ENDIF() + +IF(NOT LMMS_EMBED_VST OR NOT LMMS_BUILD_LINUX) + SET(LMMS_EMBED_VST_X11 OFF) +ENDIF() + +# Qt4 has no QWindow::createWindowContainer, so we always use QX11EmbedContainer +IF(LMMS_EMBED_VST AND NOT WANT_QT5) + SET(LMMS_EMBED_VST_X11 ON) +ENDIF() + +IF(LMMS_EMBED_VST) + SET(EMBED_FLAGS "-DLMMS_EMBED_VST") + IF(LMMS_EMBED_VST_X11) + LIST(APPEND EMBED_FLAGS "-DLMMS_EMBED_VST_X11") + ENDIF() +ENDIF() + + IF("${PLUGIN_LIST}" STREQUAL "") SET(PLUGIN_LIST ${MINIMAL_LIST} diff --git a/plugins/VstEffect/CMakeLists.txt b/plugins/VstEffect/CMakeLists.txt index ec05a14ea4c..b81e8e4c4ba 100644 --- a/plugins/VstEffect/CMakeLists.txt +++ b/plugins/VstEffect/CMakeLists.txt @@ -10,6 +10,8 @@ ELSE() SET(CMAKE_INSTALL_RPATH "${CMAKE_INSTALL_PREFIX}/${PLUGIN_DIR}") ENDIF() +ADD_DEFINITIONS(${EMBED_FLAGS}) + BUILD_PLUGIN(vsteffect VstEffect.cpp VstEffectControls.cpp VstEffectControlDialog.cpp VstSubPluginFeatures.cpp VstEffect.h VstEffectControls.h VstEffectControlDialog.h VstSubPluginFeatures.h MOCFILES VstEffectControlDialog.h VstEffectControls.h EMBEDDED_RESOURCES *.png) SET_TARGET_PROPERTIES(vsteffect PROPERTIES COMPILE_FLAGS "-Wno-attributes") TARGET_LINK_LIBRARIES(vsteffect -lvstbase) diff --git a/plugins/VstEffect/VstEffectControlDialog.cpp b/plugins/VstEffect/VstEffectControlDialog.cpp index b9a7d9822c1..8a343440ca1 100644 --- a/plugins/VstEffect/VstEffectControlDialog.cpp +++ b/plugins/VstEffect/VstEffectControlDialog.cpp @@ -43,7 +43,9 @@ VstEffectControlDialog::VstEffectControlDialog( VstEffectControls * _ctl ) : EffectControlDialog( _ctl ), +#ifdef LMMS_EMBED_VST m_pluginWidget( NULL ), +#endif m_plugin( NULL ), tbLabel( NULL ) { @@ -56,6 +58,7 @@ VstEffectControlDialog::VstEffectControlDialog( VstEffectControls * _ctl ) : _ctl->m_effect->m_plugin != NULL ) { m_plugin = _ctl->m_effect->m_plugin; +#ifdef LMMS_EMBED_VST m_plugin->showEditor( NULL, true ); m_pluginWidget = m_plugin->pluginWidget(); @@ -66,18 +69,31 @@ VstEffectControlDialog::VstEffectControlDialog( VstEffectControls * _ctl ) : m_pluginWidget = m_plugin->pluginWidget( false ); } #endif + +#else // LMMS_EMBED_VST + m_plugin->showUI(); +#endif } +#ifdef LMMS_EMBED_VST if( m_pluginWidget ) +#else + if( m_plugin ) +#endif { - setWindowTitle( m_pluginWidget->windowTitle() ); + setWindowTitle( m_plugin->name() ); setMinimumWidth( 250 ); QPushButton * btn = new QPushButton( tr( "Show/hide" ) ); +#ifdef LMMS_EMBED_VST btn->setCheckable( true ); connect( btn, SIGNAL( toggled( bool ) ), - m_pluginWidget, SLOT( setVisible( bool ) ) ); + SLOT( togglePluginUI( bool ) ) ); emit btn->click(); +#else + connect( btn, SIGNAL( clicked( bool ) ), + SLOT( togglePluginUI( bool ) ) ); +#endif btn->setMinimumWidth( 78 ); btn->setMaximumWidth( 78 ); @@ -206,8 +222,12 @@ VstEffectControlDialog::VstEffectControlDialog( VstEffectControls * _ctl ) : m_savePresetButton->setMinimumHeight( 21 ); m_savePresetButton->setMaximumHeight( 21 ); +#ifdef LMMS_EMBED_VST int newSize = m_pluginWidget->width() + 20; newSize = (newSize < 250) ? 250 : newSize; +#else + int newSize = 250; +#endif QWidget* resize = new QWidget(this); resize->resize( newSize, 10 ); QWidget* space0 = new QWidget(this); @@ -219,7 +239,9 @@ VstEffectControlDialog::VstEffectControlDialog( VstEffectControls * _ctl ) : l->addItem( new QSpacerItem( newSize - 20, 30, QSizePolicy::Fixed, QSizePolicy::Fixed ), 1, 0 ); l->addWidget( resize, 2, 0, 1, 1, Qt::AlignCenter ); +#ifdef LMMS_EMBED_VST l->addWidget( m_pluginWidget, 3, 0, 1, 1, Qt::AlignCenter ); +#endif l->setRowStretch( 5, 1 ); l->setColumnStretch( 1, 1 ); @@ -264,3 +286,25 @@ VstEffectControlDialog::~VstEffectControlDialog() //delete m_pluginWidget; } + + + +void VstEffectControlDialog::togglePluginUI( bool checked ) +{ + if( m_plugin ) + { +#ifdef LMMS_EMBED_VST + if( checked ) + { + m_plugin->showEditor( NULL, true ); + } + else + { + m_plugin->hideEditor(); + } +#else + m_plugin->toggleUI(); +#endif + } +} + diff --git a/plugins/VstEffect/VstEffectControlDialog.h b/plugins/VstEffect/VstEffectControlDialog.h index 658a3d24436..69263a80452 100644 --- a/plugins/VstEffect/VstEffectControlDialog.h +++ b/plugins/VstEffect/VstEffectControlDialog.h @@ -51,7 +51,9 @@ class VstEffectControlDialog : public EffectControlDialog virtual void paintEvent( QPaintEvent * _pe ); private: +#ifdef LMMS_EMBED_VST QWidget * m_pluginWidget; +#endif PixmapButton * m_openPresetButton; PixmapButton * m_rolLPresetButton; @@ -62,6 +64,9 @@ class VstEffectControlDialog : public EffectControlDialog VstPlugin * m_plugin; QLabel * tbLabel; + +private slots: + void togglePluginUI( bool checked ); } ; #endif diff --git a/plugins/vestige/CMakeLists.txt b/plugins/vestige/CMakeLists.txt index 21803a92433..2457462f0c8 100644 --- a/plugins/vestige/CMakeLists.txt +++ b/plugins/vestige/CMakeLists.txt @@ -1,3 +1,10 @@ +IF(LMMS_EMBED_VST) + ADD_DEFINITIONS(-DLMMS_EMBED_VST) + IF(LMMS_EMBED_VST_X11) + ADD_DEFINITIONS(-DLMMS_EMBED_VST_X11) + ENDIF() +ENDIF() + IF(LMMS_SUPPORT_VST) INCLUDE(BuildPlugin) INCLUDE_DIRECTORIES("${CMAKE_CURRENT_SOURCE_DIR}/../vst_base") diff --git a/plugins/vestige/vestige.cpp b/plugins/vestige/vestige.cpp index ec87e87808e..317f97c545f 100644 --- a/plugins/vestige/vestige.cpp +++ b/plugins/vestige/vestige.cpp @@ -263,7 +263,11 @@ void vestigeInstrument::loadFile( const QString & _file ) return; } +#ifdef LMMS_EMBED_VST m_plugin->showEditor( NULL, false ); +#else + m_plugin->showUI(); +#endif if( set_ch_name ) { @@ -740,19 +744,11 @@ void VestigeInstrumentView::toggleGUI( void ) { return; } - QWidget * w = m_vi->m_plugin->pluginWidget(); - if( w == NULL ) - { - return; - } - if( w->isHidden() ) - { - w->show(); - } - else - { - w->hide(); - } +#ifdef LMMS_EMBED_VST + m_vi->m_plugin->toggleEditor(); +#else + m_vi->m_plugin->toggleUI(); +#endif } diff --git a/plugins/vst_base/CMakeLists.txt b/plugins/vst_base/CMakeLists.txt index a3f919adf31..85444229491 100644 --- a/plugins/vst_base/CMakeLists.txt +++ b/plugins/vst_base/CMakeLists.txt @@ -2,6 +2,8 @@ IF(LMMS_SUPPORT_VST) INCLUDE(BuildPlugin) +ADD_DEFINITIONS(${EMBED_FLAGS}) + IF(LMMS_BUILD_WIN32) ADD_DEFINITIONS(-DPTW32_STATIC_LIB) ADD_EXECUTABLE(RemoteVstPlugin "${CMAKE_CURRENT_SOURCE_DIR}/RemoteVstPlugin.cpp") @@ -28,6 +30,7 @@ SET(REMOTE_VST_PLUGIN_FILEPATH "RemoteVstPlugin" CACHE STRING "Relative file pat ADD_DEFINITIONS(-DREMOTE_VST_PLUGIN_FILEPATH="${REMOTE_VST_PLUGIN_FILEPATH}") BUILD_PLUGIN(vstbase vst_base.cpp VstPlugin.cpp VstPlugin.h communication.h MOCFILES VstPlugin.h) +TARGET_LINK_LIBRARIES(vstbase qx11embedcontainer) IF(LMMS_BUILD_LINUX AND NOT WANT_VST_NOWINE) @@ -43,6 +46,7 @@ SET(WINE_CXX_FLAGS "" CACHE STRING "Extra flags passed to wineg++") STRING(REPLACE "include/wine" "include" WINE_INCLUDE_BASE_DIR ${WINE_INCLUDE_DIR}) STRING(REPLACE "lib/libwine.so" "lib" WINE_LIBRARY_DIR ${WINE_LIBRARY}) STRING(REPLACE " " ";" WINE_BUILD_FLAGS ${CMAKE_CXX_FLAGS} " " ${CMAKE_EXE_LINKER_FLAGS} " " ${WINE_CXX_FLAGS}) + ADD_CUSTOM_COMMAND( SOURCE "${CMAKE_CURRENT_SOURCE_DIR}/RemoteVstPlugin.cpp" COMMAND ${WINE_CXX} @@ -51,8 +55,10 @@ ADD_CUSTOM_COMMAND( -I${WINE_INCLUDE_BASE_DIR} -L${WINE_LIBRARY_DIR} ${CMAKE_CURRENT_SOURCE_DIR}/RemoteVstPlugin.cpp - -ansi -mwindows -lpthread ${EXTRA_FLAGS} -fno-omit-frame-pointer + -std=c++0x + -mwindows -lpthread ${EXTRA_FLAGS} -fno-omit-frame-pointer ${WINE_BUILD_FLAGS} + ${EMBED_FLAGS} -o ../RemoteVstPlugin # Ensure correct file extension COMMAND sh -c "mv ../RemoteVstPlugin.exe ../RemoteVstPlugin || true" diff --git a/plugins/vst_base/RemoteVstPlugin.cpp b/plugins/vst_base/RemoteVstPlugin.cpp index 09f8569e989..8b594a4b2ce 100644 --- a/plugins/vst_base/RemoteVstPlugin.cpp +++ b/plugins/vst_base/RemoteVstPlugin.cpp @@ -63,13 +63,6 @@ #define USE_WS_PREFIX #include -#if defined(LMMS_BUILD_WIN32) || defined(LMMS_BUILD_WIN64) -#include "basename.c" -#else -#include -#endif - - #include #include #include @@ -105,7 +98,6 @@ struct ERect #ifndef USE_QT_SHMEM #include #include -#include #include #include #include @@ -136,6 +128,7 @@ class RemoteVstPlugin : public RemotePluginClient void init( const std::string & _plugin_file ); void initEditor(); + void destroyEditor(); virtual void process( const sampleFrame * _in, sampleFrame * _out ); @@ -356,6 +349,7 @@ class RemoteVstPlugin : public RemotePluginClient int m_windowHeight; bool m_initialized; + bool m_registeredWindowClass; pthread_mutex_t m_pluginLock; bool m_processing; @@ -401,7 +395,6 @@ RemoteVstPlugin::RemoteVstPlugin( key_t _shm_in, key_t _shm_out ) : RemoteVstPlugin::RemoteVstPlugin( const char * socketPath ) : RemotePluginClient( socketPath ), #endif - m_shortName( "" ), m_libInst( NULL ), m_plugin( NULL ), m_window( NULL ), @@ -409,6 +402,7 @@ RemoteVstPlugin::RemoteVstPlugin( const char * socketPath ) : m_windowWidth( 0 ), m_windowHeight( 0 ), m_initialized( false ), + m_registeredWindowClass( false ), m_pluginLock(), m_processing( false ), m_messageList(), @@ -424,7 +418,6 @@ RemoteVstPlugin::RemoteVstPlugin( const char * socketPath ) : m_in( NULL ), m_shmID( -1 ), m_vstSyncData( NULL ) - { pthread_mutex_init( &m_pluginLock, NULL ); pthread_mutex_init( &m_shmLock, NULL ); @@ -491,14 +484,7 @@ RemoteVstPlugin::RemoteVstPlugin( const char * socketPath ) : RemoteVstPlugin::~RemoteVstPlugin() { - if( m_window != NULL ) - { - pluginDispatch( effEditClose ); -#ifdef LMMS_BUILD_LINUX - CloseWindow( m_window ); -#endif - m_window = NULL; - } + destroyEditor(); pluginDispatch( effMainsChanged, 0, 0 ); pluginDispatch( effClose ); #ifndef USE_QT_SHMEM @@ -537,10 +523,45 @@ bool RemoteVstPlugin::processMessage( const message & _m ) { switch( _m.id ) { + +#ifdef LMMS_EMBED_VST_X11 + case IdShowUI: + ShowWindow( m_window, SW_SHOWNORMAL ); + UpdateWindow( m_window ); + break; +#endif + +#ifndef LMMS_EMBED_VST + case IdShowUI: + initEditor(); + break; + + case IdHideUI: + destroyEditor(); + break; + + case IdToggleUI: + if( m_window ) + { + destroyEditor(); + } + else + { + initEditor(); + } + break; + + case IdIsUIVisible: + sendMessage( message( IdIsUIVisible ) + .addInt( m_window ? 1 : 0 ) ); + break; +#endif + case IdVstLoadPlugin: init( _m.getString() ); break; +// TODO: Drop Windows hack for Qt 4 #ifdef LMMS_BUILD_WIN32 case IdVstPluginWindowInformation: { @@ -688,9 +709,20 @@ void RemoteVstPlugin::init( const std::string & _plugin_file ) +static void close_check( int fd ) +{ + if( close( fd ) ) + { + perror( "close" ); + } +} + + + + void RemoteVstPlugin::initEditor() { - if( !( m_plugin->flags & effFlagsHasEditor ) ) + if( m_window || !( m_plugin->flags & effFlagsHasEditor ) ) { return; } @@ -704,38 +736,34 @@ void RemoteVstPlugin::initEditor() } - WNDCLASS wc; - wc.style = CS_HREDRAW | CS_VREDRAW; - wc.lpfnWndProc = DefWindowProc; - wc.cbClsExtra = 0; - wc.cbWndExtra = 0; - wc.hInstance = hInst; - wc.hIcon = LoadIcon( NULL, IDI_APPLICATION ); - wc.hCursor = LoadCursor( NULL, IDC_ARROW ); - wc.hbrBackground = (HBRUSH) GetStockObject( BLACK_BRUSH ); - wc.lpszMenuName = NULL; - wc.lpszClassName = "LVSL"; - - if( !RegisterClass( &wc ) ) + if( !m_registeredWindowClass ) { - return; - } + WNDCLASS wc; + wc.style = CS_HREDRAW | CS_VREDRAW; + wc.lpfnWndProc = DefWindowProc; + wc.cbClsExtra = 0; + wc.cbWndExtra = 0; + wc.hInstance = hInst; + wc.hIcon = LoadIcon( NULL, IDI_APPLICATION ); + wc.hCursor = LoadCursor( NULL, IDC_ARROW ); + wc.hbrBackground = NULL; + wc.lpszMenuName = NULL; + wc.lpszClassName = "LVSL"; -#ifdef LMMS_BUILD_LINUX - //m_window = CreateWindowEx( 0, "LVSL", m_shortName.c_str(), - // ( WS_OVERLAPPEDWINDOW | WS_THICKFRAME ) & ~WS_MAXIMIZEBOX, - // 0, 0, 10, 10, NULL, NULL, hInst, NULL ); + if( !RegisterClass( &wc ) ) + { + return; + } + m_registeredWindowClass = true; + } - m_window = CreateWindowEx( 0 , "LVSL", m_shortName.c_str(), - WS_POPUP | WS_SYSMENU | WS_BORDER , 0, 0, 10, 10, NULL, NULL, hInst, NULL); + m_window = CreateWindowEx( 0, "LVSL", pluginName(), +#ifdef LMMS_EMBED_VST + WS_POPUP | WS_SYSMENU | WS_BORDER, #else - m_windowID = 1; // arbitrary value on win32 to signal - // vstPlugin-class that we have an editor - - m_window = CreateWindowEx( 0, "LVSL", m_shortName.c_str(), - WS_CHILD, 0, 0, 10, 10, - m_window, NULL, hInst, NULL ); + WS_OVERLAPPEDWINDOW & ~WS_MAXIMIZEBOX, #endif + 0, 0, 10, 10, NULL, NULL, hInst, NULL ); if( m_window == NULL ) { debugMessage( "initEditor(): cannot create editor window\n" ); @@ -756,17 +784,37 @@ void RemoteVstPlugin::initEditor() SWP_NOMOVE | SWP_NOZORDER ); pluginDispatch( effEditTop ); +#ifndef LMMS_EMBED_VST_X11 ShowWindow( m_window, SW_SHOWNORMAL ); - UpdateWindow( m_window ); +#endif #ifdef LMMS_BUILD_LINUX m_windowID = (intptr_t) GetProp( m_window, "__wine_x11_whole_window" ); +#else + // 64-bit versions of Windows use 32-bit handles for interoperability + m_windowID = (intptr_t) m_window; #endif } +void RemoteVstPlugin::destroyEditor() +{ + if( m_window == NULL ) + { + return; + } + + pluginDispatch( effEditClose ); + // Destroying the window takes some time in Wine 1.8.5 + DestroyWindow( m_window ); + m_window = NULL; +} + + + + bool RemoteVstPlugin::load( const std::string & _plugin_file ) { if( ( m_libInst = LoadLibrary( _plugin_file.c_str() ) ) == NULL ) @@ -779,10 +827,6 @@ bool RemoteVstPlugin::load( const std::string & _plugin_file ) return false; } - char * tmp = strdup( _plugin_file.c_str() ); - m_shortName = basename( tmp ); - free( tmp ); - typedef AEffect * ( __stdcall * mainEntryPointer ) ( audioMasterCallback ); mainEntryPointer mainEntry = (mainEntryPointer) @@ -1067,7 +1111,7 @@ void RemoteVstPlugin::saveChunkToFile( const std::string & _file ) fprintf( stderr, "Error saving chunk to file.\n" ); } - close( fd ); + close_check( fd ); } } } @@ -1392,7 +1436,7 @@ void RemoteVstPlugin::loadChunkFromFile( const std::string & _file, int _len ) { fprintf( stderr, "Error loading chunk from file.\n" ); } - close( fd ); + close_check( fd ); pluginDispatch( effSetChunk, 0, _len, chunk ); @@ -1924,6 +1968,13 @@ DWORD WINAPI RemoteVstPlugin::guiEventLoop() while( GetMessage( &msg, NULL, 0, 0 ) > 0 ) { TranslateMessage( &msg ); + + if( msg.message == WM_SYSCOMMAND && msg.wParam == SC_CLOSE ) + { + __plugin->destroyEditor(); + continue; + } + DispatchMessage( &msg ); } diff --git a/plugins/vst_base/VstPlugin.cpp b/plugins/vst_base/VstPlugin.cpp index 9873d28e5ae..bb4116cc6e4 100644 --- a/plugins/vst_base/VstPlugin.cpp +++ b/plugins/vst_base/VstPlugin.cpp @@ -35,6 +35,9 @@ #if QT_VERSION < 0x050000 #include #include +#else +#include "X11EmbedContainer.h" +#include #endif #else #include @@ -52,9 +55,8 @@ #include "Song.h" #include "templates.h" #include "FileDialog.h" -#include - +#ifdef LMMS_EMBED_VST class vstSubWin : public QMdiSubWindow { public: @@ -76,6 +78,7 @@ class vstSubWin : public QMdiSubWindow e->ignore(); } } ; +#endif @@ -135,27 +138,13 @@ void VstPlugin::tryLoad( const QString &remoteVstPluginExecutable ) { init( remoteVstPluginExecutable, false ); + waitForHostInfoGotten(); + if( failed() ) + { + return; + } + lock(); -#ifdef LMMS_BUILD_WIN32 - QWidget * helper = new QWidget; - QHBoxLayout * l = new QHBoxLayout( helper ); - QWidget * target = new QWidget( helper ); - l->setSpacing( 0 ); - l->setMargin( 0 ); - l->addWidget( target ); - - static int k = 0; - const QString t = QString( "vst%1%2" ).arg( GetCurrentProcessId()<<10 ). - arg( ++k ); - helper->setWindowTitle( t ); - - // we've to call that for making sure, Qt created the windows - (void) helper->winId(); - (void) target->winId(); - - sendMessage( message( IdVstPluginWindowInformation ). - addString( QSTR_TO_STDSTR( t ) ) ); -#endif VstHostLanguages hlang = LanguageEnglish; switch( QLocale::system().language() ) @@ -183,27 +172,10 @@ void VstPlugin::tryLoad( const QString &remoteVstPluginExecutable ) waitForInitDone(); unlock(); - -#ifdef LMMS_BUILD_WIN32 - if( !failed() && m_pluginWindowID ) - { - target->setFixedSize( m_pluginGeometry ); - vstSubWin * sw = new vstSubWin( - gui->mainWindow()->workspace() ); - sw->setWidget( helper ); - helper->setWindowTitle( name() ); - m_pluginWidget = helper; - } - else - { - delete helper; - } -#endif } - - +#ifdef LMMS_EMBED_VST void VstPlugin::showEditor( QWidget * _parent, bool isEffect ) { QWidget * w = pluginWidget(); @@ -232,46 +204,47 @@ void VstPlugin::showEditor( QWidget * _parent, bool isEffect ) return; } - m_pluginWidget = new QWidget( _parent ); - m_pluginWidget->setFixedSize( m_pluginGeometry ); - m_pluginWidget->setWindowTitle( name() ); + vstSubWin * sw = new vstSubWin( gui->mainWindow()->workspace() ); + //auto sw = new SubWindow(); + +#ifdef LMMS_EMBED_VST_X11 + QX11EmbedContainer * container = new QX11EmbedContainer( sw ); + connect(container, SIGNAL(clientIsEmbedded()), this, SLOT(showUI())); + container->embedClient( m_pluginWindowID ); +#else + QWindow* vw = QWindow::fromWinId(m_pluginWindowID); + QWidget* container = QWidget::createWindowContainer(vw, sw ); + // TODO: Synchronize show + // Tell remote that it is embedded + // Wait for remote reply +#endif + + container->setFixedSize( m_pluginGeometry ); + container->setWindowTitle( name() ); + if( _parent == NULL ) { - vstSubWin * sw = new vstSubWin( - gui->mainWindow()->workspace() ); + m_pluginWidget = container; + + sw->setWidget(container); + if( isEffect ) { sw->setAttribute( Qt::WA_TranslucentBackground ); sw->setWindowFlags( Qt::FramelessWindowHint ); - sw->setWidget( m_pluginWidget ); -#if QT_VERSION < 0x050000 - QX11EmbedContainer * xe = new QX11EmbedContainer( sw ); - xe->embedClient( m_pluginWindowID ); - xe->setFixedSize( m_pluginGeometry ); - xe->show(); -#endif - } + } else { sw->setWindowFlags( Qt::WindowCloseButtonHint ); - sw->setWidget( m_pluginWidget ); - -#if QT_VERSION < 0x050000 - QX11EmbedContainer * xe = new QX11EmbedContainer( sw ); - xe->embedClient( m_pluginWindowID ); - xe->setFixedSize( m_pluginGeometry ); - xe->move( 4, 24 ); - xe->show(); -#endif } - } + }; +#ifdef LMMS_EMBED_VST_X11 +#endif + container->setFixedSize( m_pluginGeometry ); #endif - if( m_pluginWidget ) - { - m_pluginWidget->show(); - } + //m_pluginWidget->show(); } @@ -289,8 +262,22 @@ void VstPlugin::hideEditor() +void VstPlugin::toggleEditor() +{ + QWidget * w = pluginWidget(); + if( w ) + { + w->setVisible( !w->isVisible() ); + } +} +#endif + + + + void VstPlugin::loadSettings( const QDomElement & _this ) { +#ifdef LMMS_EMBED_VST if( pluginWidget() != NULL ) { if( _this.attribute( "guivisible" ).toInt() ) @@ -302,6 +289,16 @@ void VstPlugin::loadSettings( const QDomElement & _this ) hideEditor(); } } +#else + if( _this.attribute( "guivisible" ).toInt() ) + { + showUI(); + } + else + { + hideUI(); + } +#endif const int num_params = _this.attribute( "numparams" ).toInt(); // if it exists try to load settings chunk @@ -334,10 +331,18 @@ void VstPlugin::loadSettings( const QDomElement & _this ) void VstPlugin::saveSettings( QDomDocument & _doc, QDomElement & _this ) { +#ifdef LMMS_EMBED_VST if( pluginWidget() != NULL ) { _this.setAttribute( "guivisible", pluginWidget()->isVisible() ); } +#else + int visible = isUIVisible(); + if ( visible != -1 ) + { + _this.setAttribute( "guivisible", visible ); + } +#endif // try to save all settings in a chunk QByteArray chunk = saveChunk(); diff --git a/plugins/vst_base/VstPlugin.h b/plugins/vst_base/VstPlugin.h index 1b6d62ff715..f30335bdab9 100644 --- a/plugins/vst_base/VstPlugin.h +++ b/plugins/vst_base/VstPlugin.h @@ -52,8 +52,11 @@ class PLUGIN_EXPORT VstPlugin : public RemotePlugin, public JournallingObject return m_pluginWindowID != 0; } +#ifdef LMMS_EMBED_VST void showEditor( QWidget * _parent = NULL, bool isEffect = false ); void hideEditor(); + void toggleEditor(); +#endif inline const QString & name() const { @@ -93,6 +96,7 @@ class PLUGIN_EXPORT VstPlugin : public RemotePlugin, public JournallingObject inline QWidget * pluginWidget( bool _top_widget = true ) { +#ifdef LMMS_EMBED_VST if( _top_widget && m_pluginWidget ) { if( m_pluginWidget->parentWidget() ) @@ -100,6 +104,7 @@ class PLUGIN_EXPORT VstPlugin : public RemotePlugin, public JournallingObject return m_pluginWidget->parentWidget(); } } +#endif return m_pluginWidget; } diff --git a/plugins/vst_base/basename.c b/plugins/vst_base/basename.c deleted file mode 100644 index c8b4ee45c92..00000000000 --- a/plugins/vst_base/basename.c +++ /dev/null @@ -1,166 +0,0 @@ -/* basename.c - * - * $Id: basename.c,v 1.2 2007/03/08 23:15:58 keithmarshall Exp $ - * - * Provides an implementation of the "basename" function, conforming - * to SUSv3, with extensions to accommodate Win32 drive designators, - * and suitable for use on native Microsoft(R) Win32 platforms. - * - * Written by Keith Marshall - * - * This is free software. You may redistribute and/or modify it as you - * see fit, without restriction of copyright. - * - * This software is provided "as is", in the hope that it may be useful, - * but WITHOUT WARRANTY OF ANY KIND, not even any implied warranty of - * MERCHANTABILITY, nor of FITNESS FOR ANY PARTICULAR PURPOSE. At no - * time will the author accept any form of liability for any damages, - * however caused, resulting from the use of this software. - * - */ - -#include -#include -#include -#include - -#ifndef __cdecl /* If compiling on any non-Win32 platform ... */ -#define __cdecl /* this may not be defined. */ -#endif - -__cdecl char *basename( char *path ) -{ - size_t len; - static char *retfail = NULL; - - /* to handle path names for files in multibyte character locales, - * we need to set up LC_CTYPE to match the host file system locale - */ - - char *locale = setlocale( LC_CTYPE, NULL ); - if( locale != NULL ) locale = strdup( locale ); - setlocale( LC_CTYPE, "" ); - - if( path && *path ) - { - /* allocate sufficient local storage space, - * in which to create a wide character reference copy of path - */ - - wchar_t refcopy[1 + (len = mbstowcs( NULL, path, 0 ))]; - - /* create the wide character reference copy of path, - * and step over the drive designator, if present ... - */ - - wchar_t *refpath = refcopy; - if( ((len = mbstowcs( refpath, path, len )) > 1) && (refpath[1] == L':') ) - { - /* FIXME: maybe should confirm *refpath is a valid drive designator */ - - refpath += 2; - } - - /* ensure that our wide character reference path is NUL terminated */ - - refcopy[ len ] = L'\0'; - - /* check again, just to ensure we still have a non-empty path name ... */ - - if( *refpath ) - { - /* and, when we do, process it in the wide character domain ... - * scanning from left to right, to the char after the final dir separator - */ - - wchar_t *refname; - for( refname = refpath ; *refpath ; ++refpath ) - { - if( (*refpath == L'/') || (*refpath == L'\\') ) - { - /* we found a dir separator ... - * step over it, and any others which immediately follow it - */ - - while( (*refpath == L'/') || (*refpath == L'\\') ) - ++refpath; - - /* if we didn't reach the end of the path string ... */ - - if( *refpath ) - - /* then we have a new candidate for the base name */ - - refname = refpath; - - /* otherwise ... - * strip off any trailing dir separators which we found - */ - - else while( (refpath > refname) - && ((*--refpath == L'/') || (*refpath == L'\\')) ) - *refpath = L'\0'; - } - } - - /* in the wide character domain ... - * refname now points at the resolved base name ... - */ - - if( *refname ) - { - /* if it's not empty, - * then we transform the full normalised path back into - * the multibyte character domain, and skip over the dirname, - * to return the resolved basename. - */ - - if( (len = wcstombs( path, refcopy, len )) != (size_t)(-1) ) - path[ len ] = '\0'; - *refname = L'\0'; - if( (len = wcstombs( NULL, refcopy, 0 )) != (size_t)(-1) ) - path += len; - } - - else - { - /* the basename is empty, so return the default value of "/", - * transforming from wide char to multibyte char domain, and - * returning it in our own buffer. - */ - - retfail = (char *) realloc( retfail, len = 1 + wcstombs( NULL, L"/", 0 )); - wcstombs( path = retfail, L"/", len ); - } - - /* restore the caller's locale, clean up, and return the result */ - - setlocale( LC_CTYPE, locale ); - free( locale ); - return( path ); - } - - /* or we had an empty residual path name, after the drive designator, - * in which case we simply fall through ... - */ - } - - /* and, if we get to here ... - * the path name is either NULL, or it decomposes to an empty string; - * in either case, we return the default value of "." in our own buffer, - * reloading it with the correct value, transformed from the wide char - * to the multibyte char domain, just in case the caller trashed it - * after a previous call. - */ - - retfail = (char *) realloc( retfail, len = 1 + wcstombs( NULL, L".", 0 )); - wcstombs( retfail, L".", len ); - - /* restore the caller's locale, clean up, and return the result */ - - setlocale( LC_CTYPE, locale ); - free( locale ); - return( retfail ); -} - -/* $RCSfile: basename.c,v $$Revision: 1.2 $: end of file */ diff --git a/plugins/vst_base/communication.h b/plugins/vst_base/communication.h index 40cb4dd308b..ab7a97dfefe 100644 --- a/plugins/vst_base/communication.h +++ b/plugins/vst_base/communication.h @@ -30,6 +30,9 @@ #include "RemotePlugin.h" +//#define LMMS_EMBED_VST + + struct VstParameterDumpItem { int32_t index; @@ -56,6 +59,7 @@ enum VstRemoteMessageIDs { // vstPlugin -> remoteVstPlugin IdVstLoadPlugin = IdUserBase, + // TODO: Drop IdVstPluginWindowInformation, Windows hack for Qt 4 IdVstPluginWindowInformation, IdVstClosePlugin, IdVstSetTempo, diff --git a/src/3rdparty/CMakeLists.txt b/src/3rdparty/CMakeLists.txt new file mode 100644 index 00000000000..f1d7a9ba1a5 --- /dev/null +++ b/src/3rdparty/CMakeLists.txt @@ -0,0 +1,8 @@ +include(ExternalProject) + +IF(QT5 AND LMMS_BUILD_LINUX) + set(BUILD_SHARED_LIBS OFF) + add_subdirectory(qt5-x11embed) +ELSE() + add_library(qx11embedcontainer STATIC /dev/null) +ENDIF() diff --git a/src/3rdparty/qt5-x11embed b/src/3rdparty/qt5-x11embed new file mode 160000 index 00000000000..dad35c07cdb --- /dev/null +++ b/src/3rdparty/qt5-x11embed @@ -0,0 +1 @@ +Subproject commit dad35c07cdb704f3e9306e6301f7eb4c098552a2 diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index c2a635eda53..fd55521a3fe 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -14,6 +14,7 @@ IF(LMMS_BUILD_APPLE) SET(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -stdlib=libc++") ENDIF() +ADD_SUBDIRECTORY(3rdparty) ADD_SUBDIRECTORY(core) ADD_SUBDIRECTORY(gui) ADD_SUBDIRECTORY(tracks) @@ -131,7 +132,7 @@ IF(LMMS_BUILD_HAIKU) SET(EXTRA_LIBRARIES "-lnetwork") ENDIF() -SET(LMMS_REQUIRED_LIBS +SET(LMMS_REQUIRED_LIBS ${LMMS_REQUIRED_LIBS} ${CMAKE_THREAD_LIBS_INIT} ${QT_LIBRARIES} ${ASOUND_LIBRARY} diff --git a/src/core/RemotePlugin.cpp b/src/core/RemotePlugin.cpp index df6a0d948b3..311f3abd6bb 100644 --- a/src/core/RemotePlugin.cpp +++ b/src/core/RemotePlugin.cpp @@ -393,6 +393,20 @@ void RemotePlugin::processMidiEvent( const MidiEvent & _e, unlock(); } +void RemotePlugin::showUI() +{ + lock(); + sendMessage( IdShowUI ); + unlock(); +} + +void RemotePlugin::hideUI() +{ + lock(); + sendMessage( IdHideUI ); + unlock(); +} + diff --git a/src/gui/SubWindow.cpp b/src/gui/SubWindow.cpp index 15d8a382eab..4881c4e31e3 100644 --- a/src/gui/SubWindow.cpp +++ b/src/gui/SubWindow.cpp @@ -99,7 +99,10 @@ void SubWindow::paintEvent( QPaintEvent * ) { QPainter p( this ); QRect rect( 0, 0, width(), m_titleBarHeight ); - bool isActive = SubWindow::mdiArea()->activeSubWindow() == this; + + bool isActive = mdiArea() + ? mdiArea()->activeSubWindow() == this + : false; p.fillRect( rect, isActive ? activeColor() : p.pen().brush() );