diff --git a/include/RemotePlugin.h b/include/RemotePlugin.h index d016c5acd..7b55c3066 100644 --- a/include/RemotePlugin.h +++ b/include/RemotePlugin.h @@ -414,6 +414,7 @@ private: enum RemoteMessageIDs { IdUndefined, + IdHostInfoGotten, IdInitDone, IdQuit, IdSampleRateInformation, @@ -426,6 +427,7 @@ enum RemoteMessageIDs IdChangeOutputCount, IdShowUI, IdHideUI, + IdToggleUI, IdSaveSettingsToString, IdSaveSettingsToFile, IdLoadSettingsFromString, @@ -769,6 +771,12 @@ public: 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,6 +809,13 @@ public: unlock(); } + void toggleUI() + { + lock(); + sendMessage( IdToggleUI ); + unlock(); + } + inline bool failed() const { return m_failed; @@ -1155,6 +1170,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 @@ -1182,6 +1198,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 ) @@ -1197,6 +1214,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/VstEffect/VstEffectControlDialog.cpp b/plugins/VstEffect/VstEffectControlDialog.cpp index 3c6c84c89..b3100db3e 100644 --- a/plugins/VstEffect/VstEffectControlDialog.cpp +++ b/plugins/VstEffect/VstEffectControlDialog.cpp @@ -76,7 +76,7 @@ VstEffectControlDialog::VstEffectControlDialog( VstEffectControls * _ctl ) : QPushButton * btn = new QPushButton( tr( "Show/hide" ) ); btn->setCheckable( true ); connect( btn, SIGNAL( toggled( bool ) ), - m_pluginWidget, SLOT( setVisible( bool ) ) ); + SLOT( togglePluginUI( bool ) ) ); emit btn->click(); btn->setMinimumWidth( 78 ); @@ -264,3 +264,21 @@ VstEffectControlDialog::~VstEffectControlDialog() //delete m_pluginWidget; } + + + +void VstEffectControlDialog::togglePluginUI( bool checked ) +{ + if( m_plugin ) + { + if( checked ) + { + m_plugin->showUI(); + } + else + { + m_plugin->hideUI(); + } + } +} + diff --git a/plugins/VstEffect/VstEffectControlDialog.h b/plugins/VstEffect/VstEffectControlDialog.h index 658a3d244..ae86315ab 100644 --- a/plugins/VstEffect/VstEffectControlDialog.h +++ b/plugins/VstEffect/VstEffectControlDialog.h @@ -62,6 +62,9 @@ private: VstPlugin * m_plugin; QLabel * tbLabel; + +private slots: + void togglePluginUI( bool checked ); } ; #endif diff --git a/plugins/vestige/vestige.cpp b/plugins/vestige/vestige.cpp index 650af5c52..eeb21b909 100644 --- a/plugins/vestige/vestige.cpp +++ b/plugins/vestige/vestige.cpp @@ -261,7 +261,7 @@ void vestigeInstrument::loadFile( const QString & _file ) return; } - m_plugin->showEditor( NULL, false ); + m_plugin->showUI(); if( set_ch_name ) { @@ -735,19 +735,7 @@ void VestigeInstrumentView::toggleGUI( void ) { return; } - QWidget * w = m_vi->m_plugin->pluginWidget(); - if( w == NULL ) - { - return; - } - if( w->isHidden() ) - { - w->show(); - } - else - { - w->hide(); - } + m_vi->m_plugin->toggleUI(); } diff --git a/plugins/vst_base/CMakeLists.txt b/plugins/vst_base/CMakeLists.txt index a9a808841..a38dc223e 100644 --- a/plugins/vst_base/CMakeLists.txt +++ b/plugins/vst_base/CMakeLists.txt @@ -58,6 +58,18 @@ ADD_CUSTOM_COMMAND( OUTPUTS ../RemoteVstPlugin ) +IF(QT5) + QT5_WRAP_CPP(embed-window_MOC_out EmbedderApplication.h) +ELSE() + QT4_WRAP_CPP(embed-window_MOC_out EmbedderApplication.h) +ENDIF() + +ADD_EXECUTABLE(embed-window embed-window.cpp ${embed-window_MOC_out}) +TARGET_LINK_LIBRARIES(embed-window ${QT_LIBRARIES}) +IF(NOT QT5) + TARGET_LINK_LIBRARIES(embed-window ${X11_X11_LIB}) +ENDIF() + SET_DIRECTORY_PROPERTIES(PROPERTIES ADDITIONAL_MAKE_CLEAN_FILES ../RemoteVstPlugin.exe.so) INSTALL(PROGRAMS "${CMAKE_CURRENT_BINARY_DIR}/../RemoteVstPlugin" "${CMAKE_CURRENT_BINARY_DIR}/../RemoteVstPlugin.exe.so" DESTINATION "${PLUGIN_DIR}") ENDIF(LMMS_BUILD_LINUX AND NOT WANT_VST_NOWINE) diff --git a/plugins/vst_base/EmbedderApplication.h b/plugins/vst_base/EmbedderApplication.h new file mode 100644 index 000000000..2c5b84ef4 --- /dev/null +++ b/plugins/vst_base/EmbedderApplication.h @@ -0,0 +1,82 @@ +/* + * EmbedderApplication.h - simple application that embeds an external window + * + * Copyright (c) 2016 Javier Serrano Polo + * + * This file is part of LMMS - http://lmms.io + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public + * License along with this program (see COPYING); if not, write to the + * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301 USA. + * + */ + +#ifndef EMBEDDER_APPLICATION_H +#define EMBEDDER_APPLICATION_H + +#include +#include + +#if QT_VERSION < 0x050000 +class QX11EmbedContainer; +#endif + + +class MainWindow : public QMainWindow +{ +public: + void init( const char * title, unsigned int windowId, int width, + int height ); + + +protected: + virtual void closeEvent( QCloseEvent *event ); + + +private: +#if QT_VERSION < 0x050000 + QX11EmbedContainer * m_window; +#else + QWindow * m_window; +#endif + +} ; + + + + +class EmbedderApplication : public QApplication +{ + Q_OBJECT +public: + EmbedderApplication( int & argc, char * * argv ); + + virtual ~EmbedderApplication(); + + void init( const char * title, unsigned int windowId, int width, + int height ); + + +private: + MainWindow m_mainWindow; + + +private slots: + void applicationReady(); + void readCommand(); + +} ; + + +#endif diff --git a/plugins/vst_base/RemoteVstPlugin.cpp b/plugins/vst_base/RemoteVstPlugin.cpp index 03e160a8d..0c04a0116 100644 --- a/plugins/vst_base/RemoteVstPlugin.cpp +++ b/plugins/vst_base/RemoteVstPlugin.cpp @@ -52,6 +52,7 @@ #include #endif +#include #include #endif @@ -106,6 +107,12 @@ struct ERect #include #endif +#define EMBEDDER_NAME "embed-window" + +#ifdef LMMS_BUILD_LINUX +#define USE_LINUX_EMBEDDER +#endif + static VstHostLanguages hlang = LanguageEnglish; @@ -115,6 +122,10 @@ RemoteVstPlugin * __plugin = NULL; DWORD __GuiThreadID = 0; +#ifdef USE_LINUX_EMBEDDER +static char * s_embedderPath; +#endif + class RemoteVstPlugin : public RemotePluginClient @@ -131,6 +142,7 @@ public: void init( const std::string & _plugin_file ); void initEditor(); + void destroyEditor(); virtual void process( const sampleFrame * _in, sampleFrame * _out ); @@ -299,8 +311,16 @@ private: intptr_t m_windowID; int m_windowWidth; int m_windowHeight; +#ifdef USE_LINUX_EMBEDDER + pid_t m_embedderPid; + int m_embedderStdin; + int m_embedderStdout; +#else + PROCESS_INFORMATION m_processInfo; +#endif bool m_initialized; + bool m_registeredWindowClass; pthread_mutex_t m_pluginLock; @@ -347,6 +367,7 @@ RemoteVstPlugin::RemoteVstPlugin( const char * socketPath ) : m_windowWidth( 0 ), m_windowHeight( 0 ), m_initialized( false ), + m_registeredWindowClass( false ), m_pluginLock(), m_inputs( NULL ), m_outputs( NULL ), @@ -357,7 +378,6 @@ RemoteVstPlugin::RemoteVstPlugin( const char * socketPath ) : m_in( NULL ), m_shmID( -1 ), m_vstSyncData( NULL ) - { pthread_mutex_init( &m_pluginLock, NULL ); @@ -423,14 +443,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 @@ -464,24 +477,103 @@ RemoteVstPlugin::~RemoteVstPlugin() +#ifdef USE_LINUX_EMBEDDER +static void checkExitStatus( int status ) +{ + if( WIFEXITED( status ) && WEXITSTATUS( status ) == EXIT_SUCCESS ) + { + return; + } + fprintf( stderr, "Child process did not exit properly\n" ); +} +#endif + + + + bool RemoteVstPlugin::processMessage( const message & _m ) { switch( _m.id ) { + case IdShowUI: +#ifdef USE_LINUX_EMBEDDER + if( m_window ) + { + int status; + pid_t pid = waitpid( m_embedderPid, &status, + WNOHANG ); + switch( pid ) + { + case -1: + perror( "waitpid" ); + break; + + case 0: + break; + + default: + checkExitStatus( status ); + m_embedderPid = -1; + destroyEditor(); + } + } +#endif + initEditor(); + break; + + case IdHideUI: + destroyEditor(); + break; + + case IdToggleUI: +#ifdef USE_LINUX_EMBEDDER + if( m_window ) + { + bool restart = false; + int status; + pid_t pid = waitpid( m_embedderPid, &status, + WNOHANG ); + switch( pid ) + { + case -1: + perror( "waitpid" ); + break; + + case 0: + break; + + default: + checkExitStatus( status ); + m_embedderPid = -1; + restart = true; + } + destroyEditor(); + + if( !restart ) + { + break; + } + } + + initEditor(); + break; +#else + // Temporary implementation, not using an embedder + if( m_window ) + { + destroyEditor(); + } + else + { + initEditor(); + } + break; +#endif + case IdVstLoadPlugin: init( _m.getString() ); break; -#ifdef LMMS_BUILD_WIN32 - case IdVstPluginWindowInformation: - { - HWND top = FindWindowEx( NULL, NULL, NULL, - _m.getString().c_str() ); - m_window = FindWindowEx( top, NULL, NULL, NULL ); - break; - } -#endif - case IdVstSetTempo: setBPM( _m.getInt() ); break; @@ -619,9 +711,32 @@ void RemoteVstPlugin::init( const std::string & _plugin_file ) +static void assert_dup2( int oldfd, int newfd ) +{ + if( dup2( oldfd, newfd ) == -1 ) + { + perror( "dup2" ); + exit( EXIT_FAILURE ); + } +} + + + + +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; } @@ -635,45 +750,40 @@ 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 = (HBRUSH) GetStockObject( BLACK_BRUSH ); + wc.lpszMenuName = NULL; + wc.lpszClassName = "LVSL"; + + if( !RegisterClass( &wc ) ) + { + return; + } + m_registeredWindowClass = true; } -#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 ); - - m_window = CreateWindowEx( 0 , "LVSL", m_shortName.c_str(), - WS_POPUP | WS_SYSMENU | WS_BORDER , 0, 0, 10, 10, NULL, NULL, hInst, NULL); -#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 ); +#if QT_VERSION < 0x050000 && defined( LMMS_BUILD_LINUX ) + WS_POPUP | WS_SYSMENU | WS_BORDER, +#else + ( WS_OVERLAPPEDWINDOW | WS_THICKFRAME ) & ~WS_MAXIMIZEBOX, #endif + 0, 0, 10, 10, NULL, NULL, hInst, NULL ); if( m_window == NULL ) { debugMessage( "initEditor(): cannot create editor window\n" ); return; } - pluginDispatch( effEditOpen, 0, 0, m_window ); ERect * er; @@ -682,17 +792,202 @@ void RemoteVstPlugin::initEditor() m_windowWidth = er->right - er->left; m_windowHeight = er->bottom - er->top; +#ifdef USE_LINUX_EMBEDDER + m_windowID = (intptr_t) GetProp( m_window, "__wine_x11_whole_window" ); + + m_embedderPid = -1; + m_embedderStdin = -1; + m_embedderStdout = -1; + + int infd[2]; + int outfd[2]; + if( pipe( infd ) ) + { + perror( "pipe" ); + destroyEditor(); + return; + } + if( pipe( outfd ) ) + { + perror( "pipe" ); + close_check( infd[0] ); + close_check( infd[1] ); + destroyEditor(); + return; + } + + m_embedderPid = fork(); + switch ( m_embedderPid ) + { + case -1: + perror( "fork" ); + close_check( infd[0] ); + close_check( infd[1] ); + close_check( outfd[0] ); + close_check( outfd[1] ); + destroyEditor(); + return; + + case 0: + assert_dup2( infd[0], STDIN_FILENO ); + assert_dup2( outfd[1], STDOUT_FILENO ); + + close_check( infd[0] ); + close_check( infd[1] ); + close_check( outfd[0] ); + close_check( outfd[1] ); + + char * widStr = new char[2 * sizeof m_windowID + 1]; + sprintf( widStr, "%x", m_windowID ); + char * widthStr + = new char[2 * sizeof m_windowWidth + 1]; + sprintf( widthStr, "%x", m_windowWidth ); + char * heightStr + = new char[2 * sizeof m_windowHeight + 1]; + sprintf( heightStr, "%x", m_windowHeight ); + execl( s_embedderPath, s_embedderPath, + m_shortName.c_str(), widStr, widthStr, + heightStr, (char *) NULL ); + perror( "execl" ); + exit( EXIT_FAILURE ); + } + + close_check( infd[0] ); + close_check( outfd[1] ); + m_embedderStdin = infd[1]; + m_embedderStdout = outfd[0]; +#else + // Pending: Check that Qt 5 embedding works on Windows + // Should wait until Qt 4 support is dropped + + // TODO: Set to native window ID + m_windowID = 1; + + const char * name = m_shortName.c_str(); + char * commandLine = new char[sizeof EMBEDDER_NAME " " + strlen( name ) + + 1 + 2 * sizeof m_windowID + 1 + 2 * sizeof m_windowWidth + 1 + + 2 * sizeof m_windowHeight]; + sprintf( commandLine, EMBEDDER_NAME " %s %x %x %x", name, m_windowID, + m_windowWidth, m_windowHeight ); + + // Set up the pipes +/* + SECURITY_ATTRIBUTES saAttr; + + saAttr.nLength = sizeof(SECURITY_ATTRIBUTES); + saAttr.bInheritHandle = TRUE; + saAttr.lpSecurityDescriptor = NULL; + + if ( !CreatePipe(&g_hChildStd_OUT_Rd, &g_hChildStd_OUT_Wr, &saAttr, 0) ) + ErrorExit(TEXT("StdoutRd CreatePipe")); + + if ( !SetHandleInformation(g_hChildStd_OUT_Rd, HANDLE_FLAG_INHERIT, 0) ) + ErrorExit(TEXT("Stdout SetHandleInformation")); + + if (! CreatePipe(&g_hChildStd_IN_Rd, &g_hChildStd_IN_Wr, &saAttr, 0)) + ErrorExit(TEXT("Stdin CreatePipe")); + + if ( ! SetHandleInformation(g_hChildStd_IN_Wr, HANDLE_FLAG_INHERIT, 0) ) + ErrorExit(TEXT("Stdin SetHandleInformation")); +*/ + + STARTUPINFO si; + ZeroMemory( &si, sizeof si ); + si.cb = sizeof si; +// si.hStdOutput = g_hChildStd_OUT_Wr; +// si.hStdInput = g_hChildStd_IN_Rd; +// si.dwFlags |= STARTF_USESTDHANDLES; + ZeroMemory( &m_processInfo, sizeof m_processInfo ); + + // Pending: Create new process when the embedder is ready +// bool ok = CreateProcess( NULL, commandLine, NULL, NULL, TRUE, 0, NULL, +// NULL, &si, &m_processInfo ); + delete[] commandLine; +/* + if ( !ok ) + { + fprintf( stderr, "CreateProcess failed (%d)\n", + GetLastError() ); + destroyEditor(); + return; + } +*/ + // close unused pipe handles if possible +// if ( ! CloseHandle(g_hChildStd_IN_Rd) ) error +#endif + + SetWindowPos( m_window, 0, 0, 0, m_windowWidth + 8, m_windowHeight + 26, SWP_NOACTIVATE | SWP_NOMOVE | SWP_NOZORDER ); pluginDispatch( effEditTop ); - ShowWindow( m_window, SW_SHOWNORMAL ); - UpdateWindow( m_window ); - -#ifdef LMMS_BUILD_LINUX - m_windowID = (intptr_t) GetProp( m_window, "__wine_x11_whole_window" ); +#ifdef USE_LINUX_EMBEDDER + char c; + if( ::read( m_embedderStdout, &c, 1 ) != 1 ) + { + fprintf( stderr, "Could not read from embedder\n" ); + destroyEditor(); + return; + } +#else +// bSuccess = ReadFile(g_hChildStd_OUT_Rd, chBuf, BUFSIZE, &dwRead, NULL); +// if ( ! bSuccess || dwRead == 0 ) break; #endif + ShowWindow( m_window, SW_SHOWNORMAL ); +#ifdef USE_LINUX_EMBEDDER + if( ::write( m_embedderStdin, "", 1 ) != 1 ) + { + fprintf( stderr, "Could not write to embedder\n" ); + destroyEditor(); + return; + } +#else +// bSuccess = WriteFile(g_hChildStd_IN_Wr, chBuf, dwRead, &dwWritten, NULL); +// if ( ! bSuccess ) break; +#endif +} + + + + +void RemoteVstPlugin::destroyEditor() +{ + if( m_window == NULL ) + { + return; + } + +#ifdef USE_LINUX_EMBEDDER + close_check( m_embedderStdin ); + close_check( m_embedderStdout ); + if( m_embedderPid != -1 ) + { + int status; + pid_t pid = waitpid( m_embedderPid, &status, 0 ); + if( pid == -1 ) + { + perror( "waitpid" ); + } + else + { + checkExitStatus( status ); + } + } +#else + // Close pipes + // Wait until child process exits. +// WaitForSingleObject( m_processInfo.hProcess, INFINITE ); + + // Close process and thread handles. +// CloseHandle( m_processInfo.hProcess ); +// CloseHandle( m_processInfo.hThread ); +#endif + + pluginDispatch( effEditClose ); + // Destroying the window takes some time in Wine 1.8.5 + DestroyWindow( m_window ); + m_window = NULL; } @@ -989,7 +1284,7 @@ void RemoteVstPlugin::saveChunkToFile( const std::string & _file ) fprintf( stderr, "Error saving chunk to file.\n" ); } - close( fd ); + close_check( fd ); } } } @@ -1327,7 +1622,7 @@ void RemoteVstPlugin::loadChunkFromFile( const std::string & _file, int _len ) { fprintf( stderr, "Error loading chunk from file.\n" ); } - close( fd ); + close_check( fd ); pluginDispatch( 24, 0, _len, chunk ); delete[] buf; @@ -1807,6 +2102,13 @@ DWORD WINAPI RemoteVstPlugin::guiEventLoop( LPVOID _param ) while( quit == false && GetMessage( &msg, NULL, 0, 0 ) ) { TranslateMessage( &msg ); + + if( msg.message == WM_SYSCOMMAND && msg.wParam == SC_CLOSE ) + { + _this->destroyEditor(); + continue; + } + DispatchMessage( &msg ); if( msg.message == WM_TIMER && _this->isInitialized() ) @@ -1846,6 +2148,63 @@ DWORD WINAPI RemoteVstPlugin::guiEventLoop( LPVOID _param ) +#ifdef USE_LINUX_EMBEDDER +static char * findMyPath( const char * argv0 ) +{ + // TODO: escape ' or use execlp + std::string command = "winepath '"; + command += argv0; + command += '\''; + FILE * stream = popen( command.c_str(), "r" ); + if( !stream ) + { + perror( "popen" ); + return NULL; + } + char * unixPath = (char *) malloc( BUFSIZ ); + char * s = fgets( unixPath, BUFSIZ, stream ); + if( !s ) + { + perror( "fgets" ); + free( unixPath ); + return NULL; + } + if( strlen( s ) == BUFSIZ - 1 ) + { + //TODO: Read a longer line + fprintf( stderr, "findMyPath: Buffer too small\n" ); + } + char * eol = strchr( unixPath, '\n' ); + if( eol ) + { + *eol = '\0'; + } + if( pclose( stream ) == -1 ) + { + perror( "pclose" ); + } + return unixPath; +} + + + + +static char * findEmbedderPath( const char * argv0 ) +{ + char * myPath = findMyPath( argv0 ); + const char * slash = myPath ? strrchr( myPath, '/' ) : NULL; + size_t prefixLength = slash ? slash - myPath + 1 : 0; + char * path = new char[prefixLength + sizeof EMBEDDER_NAME]; + memcpy( path, myPath, prefixLength ); + memcpy( path + prefixLength, EMBEDDER_NAME, sizeof EMBEDDER_NAME ); + free( myPath ); + return path; +} +#endif + + + + int main( int _argc, char * * _argv ) { #ifdef SYNC_WITH_SHM_FIFO @@ -1883,6 +2242,10 @@ int main( int _argc, char * * _argv ) } #endif +#ifdef USE_LINUX_EMBEDDER + s_embedderPath = findEmbedderPath( _argv[0] ); +#endif + // constructor automatically will process messages until it receives // a IdVstLoadPlugin message and processes it #ifdef SYNC_WITH_SHM_FIFO @@ -1907,6 +2270,10 @@ int main( int _argc, char * * _argv ) delete __plugin; +#ifdef USE_LINUX_EMBEDDER + delete[] s_embedderPath; +#endif + #ifdef LMMS_BUILD_WIN32 #ifndef __WINPTHREADS_VERSION diff --git a/plugins/vst_base/VstPlugin.cpp b/plugins/vst_base/VstPlugin.cpp index ff0ca58e4..903e7f539 100644 --- a/plugins/vst_base/VstPlugin.cpp +++ b/plugins/vst_base/VstPlugin.cpp @@ -31,16 +31,6 @@ #include #include #include -#ifdef LMMS_BUILD_LINUX -#if QT_VERSION < 0x050000 -#include -#include -#else -#include -#endif -#else -#include -#endif #include #ifdef LMMS_BUILD_WIN32 @@ -54,32 +44,6 @@ #include "Song.h" #include "templates.h" #include "FileDialog.h" -#include - - -class vstSubWin : public QMdiSubWindow -{ -public: - vstSubWin( QWidget * _parent ) : - QMdiSubWindow( _parent ) - { - setAttribute( Qt::WA_DeleteOnClose, false ); - } - - virtual ~vstSubWin() - { - } - - virtual void closeEvent( QCloseEvent * e ) - { - // ignore close-events - for some reason otherwise the VST GUI - // remains hidden when re-opening - hide(); - e->ignore(); - } -} ; - - VstPlugin::VstPlugin( const QString & _plugin ) : @@ -137,27 +101,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() ) @@ -185,22 +135,6 @@ 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 } @@ -237,53 +171,6 @@ void VstPlugin::showEditor( QWidget * _parent, bool isEffect ) m_pluginWidget = new QWidget( _parent ); m_pluginWidget->setFixedSize( m_pluginGeometry ); m_pluginWidget->setWindowTitle( name() ); - if( _parent == NULL ) - { - vstSubWin * sw = new vstSubWin( - gui->mainWindow()->workspace() ); - 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(); -#else - QWindow * window = QWindow::fromWinId( - m_pluginWindowID ); - QWidget * container = QWidget::createWindowContainer( - window, sw ); - container->setFixedSize( m_pluginGeometry ); - container->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(); -#else - QWindow * window = QWindow::fromWinId( - m_pluginWindowID ); - QWidget * container = QWidget::createWindowContainer( - window, sw ); - container->setAttribute( Qt::WA_NoMousePropagation ); - container->setFixedSize( m_pluginGeometry ); - container->move( 4, 24 ); - container->show(); -#endif - } - } - #endif if( m_pluginWidget ) @@ -297,6 +184,7 @@ void VstPlugin::showEditor( QWidget * _parent, bool isEffect ) void VstPlugin::hideEditor() { + //TODO: Drop m_pluginWidget, showEditor(), hideEditor() QWidget * w = pluginWidget(); if( w ) { @@ -352,6 +240,7 @@ void VstPlugin::loadSettings( const QDomElement & _this ) void VstPlugin::saveSettings( QDomDocument & _doc, QDomElement & _this ) { + //TODO: Replace with m_plugin->isVisible(), add IdIsUIVisble message if( pluginWidget() != NULL ) { _this.setAttribute( "guivisible", pluginWidget()->isVisible() ); diff --git a/plugins/vst_base/communication.h b/plugins/vst_base/communication.h index 40cb4dd30..0ed3c2fad 100644 --- a/plugins/vst_base/communication.h +++ b/plugins/vst_base/communication.h @@ -56,7 +56,6 @@ enum VstRemoteMessageIDs { // vstPlugin -> remoteVstPlugin IdVstLoadPlugin = IdUserBase, - IdVstPluginWindowInformation, IdVstClosePlugin, IdVstSetTempo, IdVstSetLanguage, @@ -73,6 +72,9 @@ enum VstRemoteMessageIDs // remoteVstPlugin -> vstPlugin IdVstFailedLoadingPlugin, IdVstBadDllFormat, + // Window ID and geometry are only useful if external windows can be + // embedded in LMMS, which is not the case in Qt 5 because there are + // glitches. If Qt is not fixed, then drop these messages. IdVstPluginWindowID, IdVstPluginEditorGeometry, IdVstPluginName, diff --git a/plugins/vst_base/embed-window.cpp b/plugins/vst_base/embed-window.cpp new file mode 100644 index 000000000..17de664d4 --- /dev/null +++ b/plugins/vst_base/embed-window.cpp @@ -0,0 +1,151 @@ +/* + * embed-window.cpp - simple application that embeds an external window + * + * Copyright (c) 2016 Javier Serrano Polo + * + * This file is part of LMMS - http://lmms.io + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public + * License along with this program (see COPYING); if not, write to the + * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301 USA. + * + */ + +#include "EmbedderApplication.h" + +#include +#include +#include +#include +#include + +#if QT_VERSION < 0x050000 +#include +#include +#include +#else +#include +#endif + + +void MainWindow::init( const char * title, unsigned int windowId, int width, + int height ) +{ + setWindowTitle( title ); +#if QT_VERSION < 0x050000 + m_window = new QX11EmbedContainer( this ); + QX11EmbedContainer * container = m_window; + container->embedClient( windowId ); +#else + m_window = QWindow::fromWinId( windowId ); + QWidget * container = QWidget::createWindowContainer( m_window, this ); +#endif + container->setFixedSize( width, height ); + container->show(); + setFixedSize( width, height ); +} + + + + +void MainWindow::closeEvent( QCloseEvent *event ) +{ + hide(); +#if QT_VERSION < 0x050000 + XUnmapWindow( QX11Info::display(), m_window->clientWinId() ); + m_window->discardClient(); +#else + m_window->setParent( 0 ); +#endif + QMainWindow::closeEvent( event ); +} + + + + +EmbedderApplication::EmbedderApplication( int & argc, char * * argv ) : + QApplication( argc, argv ) +{ + QSocketNotifier * notifier = new QSocketNotifier( STDIN_FILENO, + QSocketNotifier::Read ); + connect( notifier, SIGNAL( activated( int ) ), SLOT( readCommand() ) ); +} + + + + +EmbedderApplication::~EmbedderApplication() +{ +} + + + + +void EmbedderApplication::init( const char * title, unsigned int windowId, + int width, int height ) +{ + m_mainWindow.init( title, windowId, width, height ); +} + + + + +void EmbedderApplication::applicationReady() +{ + putchar( 0 ); + fflush( stdout ); +} + + + + +void EmbedderApplication::readCommand() +{ + int c = getchar(); + if( c == EOF ) + { + m_mainWindow.close(); + quit(); + return; + } + m_mainWindow.show(); +} + + + + +int main( int argc, char * * argv ) +{ + if( argc < 5 ) + { + fputs( "Missing arguments\n", stderr ); + return EXIT_FAILURE; + } + + EmbedderApplication * app = new EmbedderApplication( argc, argv ); + + const char * title = argv[1]; + unsigned int windowId = strtol( argv[2], NULL, 16 ); + int width = strtol( argv[3], NULL, 16 ); + int height = strtol( argv[4], NULL, 16 ); + + app->init( title, windowId, width, height ); + + QTimer::singleShot( 0, app, SLOT( applicationReady() ) ); + + int ret = app->exec(); + delete app; + + return ret; +}