From a81f4ca8e9d8be9a439da272af3375d8a993af2e Mon Sep 17 00:00:00 2001 From: Javier Serrano Polo Date: Fri, 17 Jun 2016 04:04:14 +0200 Subject: [PATCH 01/53] Embed VST GUI in subwindow --- plugins/vst_base/VstPlugin.cpp | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/plugins/vst_base/VstPlugin.cpp b/plugins/vst_base/VstPlugin.cpp index 22c9f7c20e6..ff0ca58e4f6 100644 --- a/plugins/vst_base/VstPlugin.cpp +++ b/plugins/vst_base/VstPlugin.cpp @@ -35,6 +35,8 @@ #if QT_VERSION < 0x050000 #include #include +#else +#include #endif #else #include @@ -249,6 +251,13 @@ void VstPlugin::showEditor( QWidget * _parent, bool isEffect ) 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 @@ -262,6 +271,15 @@ void VstPlugin::showEditor( QWidget * _parent, bool isEffect ) 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 } } From 878dd94e8d149ebaefb7ad7634dfa62a02f34206 Mon Sep 17 00:00:00 2001 From: Javier Serrano Polo Date: Sun, 2 Oct 2016 05:13:17 +0200 Subject: [PATCH 02/53] Embed in a separate window --- include/RemotePlugin.h | 23 + plugins/VstEffect/VstEffectControlDialog.cpp | 20 +- plugins/VstEffect/VstEffectControlDialog.h | 3 + plugins/vestige/vestige.cpp | 16 +- plugins/vst_base/CMakeLists.txt | 12 + plugins/vst_base/EmbedderApplication.h | 82 ++++ plugins/vst_base/RemoteVstPlugin.cpp | 471 +++++++++++++++++-- plugins/vst_base/VstPlugin.cpp | 127 +---- plugins/vst_base/communication.h | 4 +- plugins/vst_base/embed-window.cpp | 151 ++++++ 10 files changed, 722 insertions(+), 187 deletions(-) create mode 100644 plugins/vst_base/EmbedderApplication.h create mode 100644 plugins/vst_base/embed-window.cpp diff --git a/include/RemotePlugin.h b/include/RemotePlugin.h index d016c5acd21..7b55c306605 100644 --- a/include/RemotePlugin.h +++ b/include/RemotePlugin.h @@ -414,6 +414,7 @@ class shmFifo enum RemoteMessageIDs { IdUndefined, + IdHostInfoGotten, IdInitDone, IdQuit, IdSampleRateInformation, @@ -426,6 +427,7 @@ enum RemoteMessageIDs IdChangeOutputCount, IdShowUI, IdHideUI, + IdToggleUI, IdSaveSettingsToString, IdSaveSettingsToFile, IdLoadSettingsFromString, @@ -769,6 +771,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,6 +809,13 @@ class EXPORT RemotePlugin : public QObject, public RemotePluginBase 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 3c6c84c8992..b3100db3e75 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 658a3d24436..ae86315abbe 100644 --- a/plugins/VstEffect/VstEffectControlDialog.h +++ b/plugins/VstEffect/VstEffectControlDialog.h @@ -62,6 +62,9 @@ class VstEffectControlDialog : public EffectControlDialog 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 650af5c52b1..eeb21b9098d 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 a9a8088411b..a38dc223e8b 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 00000000000..2c5b84ef488 --- /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 03e160a8dd7..0c04a011681 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 @@ class RemoteVstPlugin : public RemotePluginClient void init( const std::string & _plugin_file ); void initEditor(); + void destroyEditor(); virtual void process( const sampleFrame * _in, sampleFrame * _out ); @@ -299,8 +311,16 @@ class RemoteVstPlugin : public RemotePluginClient 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 IdVstLoadPlugin: - init( _m.getString() ); + 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; -#ifdef LMMS_BUILD_WIN32 - case IdVstPluginWindowInformation: - { - HWND top = FindWindowEx( NULL, NULL, NULL, - _m.getString().c_str() ); - m_window = FindWindowEx( top, NULL, NULL, NULL ); + 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; + 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 ); +#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 ); - UpdateWindow( m_window ); +#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 +} -#ifdef LMMS_BUILD_LINUX - m_windowID = (intptr_t) GetProp( m_window, "__wine_x11_whole_window" ); + + + +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 ff0ca58e4f6..903e7f539b7 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 40cb4dd308b..0ed3c2fad0f 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 00000000000..17de664d47c --- /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; +} From ebe63d1425641613eda3280564614bf1cf4f980a Mon Sep 17 00:00:00 2001 From: Javier Serrano Polo Date: Mon, 2 Jan 2017 21:52:58 +0100 Subject: [PATCH 03/53] Fixes for Windows builds --- plugins/vst_base/RemoteVstPlugin.cpp | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/plugins/vst_base/RemoteVstPlugin.cpp b/plugins/vst_base/RemoteVstPlugin.cpp index 0c04a011681..f512464bb29 100644 --- a/plugins/vst_base/RemoteVstPlugin.cpp +++ b/plugins/vst_base/RemoteVstPlugin.cpp @@ -67,6 +67,7 @@ #endif +#include #include #include @@ -711,6 +712,7 @@ void RemoteVstPlugin::init( const std::string & _plugin_file ) +#ifdef USE_LINUX_EMBEDDER static void assert_dup2( int oldfd, int newfd ) { if( dup2( oldfd, newfd ) == -1 ) @@ -719,6 +721,7 @@ static void assert_dup2( int oldfd, int newfd ) exit( EXIT_FAILURE ); } } +#endif @@ -838,7 +841,7 @@ void RemoteVstPlugin::initEditor() close_check( outfd[1] ); char * widStr = new char[2 * sizeof m_windowID + 1]; - sprintf( widStr, "%x", m_windowID ); + sprintf( widStr, "%" PRIxPTR, m_windowID ); char * widthStr = new char[2 * sizeof m_windowWidth + 1]; sprintf( widthStr, "%x", m_windowWidth ); @@ -867,8 +870,8 @@ void RemoteVstPlugin::initEditor() 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 ); + sprintf( commandLine, EMBEDDER_NAME " %s %" PRIxPTR " %x %x", name, + m_windowID, m_windowWidth, m_windowHeight ); // Set up the pipes /* From 6395cd4c41eb2c1eafce1f5df7c926529a639756 Mon Sep 17 00:00:00 2001 From: Javier Serrano Polo Date: Tue, 3 Jan 2017 11:36:38 +0100 Subject: [PATCH 04/53] Enable C++11 --- plugins/vst_base/CMakeLists.txt | 25 +++++++++++++++++-------- plugins/vst_base/RemoteVstPlugin.cpp | 2 +- 2 files changed, 18 insertions(+), 9 deletions(-) diff --git a/plugins/vst_base/CMakeLists.txt b/plugins/vst_base/CMakeLists.txt index a38dc223e8b..483954760b8 100644 --- a/plugins/vst_base/CMakeLists.txt +++ b/plugins/vst_base/CMakeLists.txt @@ -49,14 +49,23 @@ SET(WINE_CXX_FLAGS "" CACHE STRING "Extra flags passed to wineg++") 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} - ARGS -I${CMAKE_BINARY_DIR} -I${CMAKE_SOURCE_DIR}/include -I${CMAKE_INSTALL_PREFIX}/include/wine/windows -I${CMAKE_INSTALL_PREFIX}/include -I/usr/include/wine/windows ${CMAKE_CURRENT_SOURCE_DIR}/RemoteVstPlugin.cpp -ansi -mwindows -lpthread ${EXTRA_FLAGS} -fno-omit-frame-pointer ${WINE_BUILD_FLAGS} -o ../RemoteVstPlugin - # Ensure correct file extension - COMMAND sh -c "mv ../RemoteVstPlugin.exe ../RemoteVstPlugin || true" - TARGET vstbase - OUTPUTS ../RemoteVstPlugin - ) + SOURCE "${CMAKE_CURRENT_SOURCE_DIR}/RemoteVstPlugin.cpp" + COMMAND ${WINE_CXX} + ARGS -I${CMAKE_BINARY_DIR} + -I${CMAKE_SOURCE_DIR}/include + -I${CMAKE_INSTALL_PREFIX}/include/wine/windows + -I${CMAKE_INSTALL_PREFIX}/include + -I${WINE_INCLUDE_BASE_DIR} + -L${WINE_LIBRARY_DIR} + ${CMAKE_CURRENT_SOURCE_DIR}/RemoteVstPlugin.cpp + -std=c++0x + -mwindows -lpthread ${EXTRA_FLAGS} -fno-omit-frame-pointer + ${WINE_BUILD_FLAGS} + -o ../RemoteVstPlugin + COMMAND sh -c "mv ../RemoteVstPlugin.exe ../RemoteVstPlugin || true" + TARGET vstbase + OUTPUTS ../RemoteVstPlugin +) IF(QT5) QT5_WRAP_CPP(embed-window_MOC_out EmbedderApplication.h) diff --git a/plugins/vst_base/RemoteVstPlugin.cpp b/plugins/vst_base/RemoteVstPlugin.cpp index f512464bb29..98bd322f690 100644 --- a/plugins/vst_base/RemoteVstPlugin.cpp +++ b/plugins/vst_base/RemoteVstPlugin.cpp @@ -67,7 +67,7 @@ #endif -#include +#include #include #include From e3d3052e428558e044a18e5228bbb854cda48b12 Mon Sep 17 00:00:00 2001 From: Javier Serrano Polo Date: Wed, 4 Jan 2017 15:14:24 +0100 Subject: [PATCH 05/53] Enable C++11 on Windows --- plugins/vst_base/CMakeLists.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/vst_base/CMakeLists.txt b/plugins/vst_base/CMakeLists.txt index 483954760b8..1b3b253bb01 100644 --- a/plugins/vst_base/CMakeLists.txt +++ b/plugins/vst_base/CMakeLists.txt @@ -3,7 +3,7 @@ IF(LMMS_SUPPORT_VST) INCLUDE(BuildPlugin) IF(LMMS_BUILD_WIN32) - ADD_DEFINITIONS(-DPTW32_STATIC_LIB) + ADD_DEFINITIONS(-DPTW32_STATIC_LIB -std=c++0x) ADD_EXECUTABLE(RemoteVstPlugin "${CMAKE_CURRENT_SOURCE_DIR}/RemoteVstPlugin.cpp") IF(QT5) From 12f6ea5566685e7c3e28af662eea45969623f621 Mon Sep 17 00:00:00 2001 From: Javier Serrano Polo Date: Tue, 24 Jan 2017 12:45:05 +0100 Subject: [PATCH 06/53] C++11 is enabled by default now --- plugins/vst_base/CMakeLists.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/vst_base/CMakeLists.txt b/plugins/vst_base/CMakeLists.txt index 1b3b253bb01..483954760b8 100644 --- a/plugins/vst_base/CMakeLists.txt +++ b/plugins/vst_base/CMakeLists.txt @@ -3,7 +3,7 @@ IF(LMMS_SUPPORT_VST) INCLUDE(BuildPlugin) IF(LMMS_BUILD_WIN32) - ADD_DEFINITIONS(-DPTW32_STATIC_LIB -std=c++0x) + ADD_DEFINITIONS(-DPTW32_STATIC_LIB) ADD_EXECUTABLE(RemoteVstPlugin "${CMAKE_CURRENT_SOURCE_DIR}/RemoteVstPlugin.cpp") IF(QT5) From f87f3638e1511e5173143943ee155d30ba26995b Mon Sep 17 00:00:00 2001 From: Javier Serrano Polo Date: Sat, 1 Apr 2017 01:11:05 +0200 Subject: [PATCH 07/53] Use window instead of embedder application --- include/RemotePlugin.h | 2 +- plugins/VstEffect/VstEffectControlDialog.cpp | 6 +- plugins/vestige/vestige.cpp | 4 +- plugins/vst_base/CMakeLists.txt | 12 - plugins/vst_base/EmbedderApplication.h | 82 ----- plugins/vst_base/RemoteVstPlugin.cpp | 367 +------------------ plugins/vst_base/VstPlugin.cpp | 93 ++++- plugins/vst_base/VstPlugin.h | 3 + plugins/vst_base/communication.h | 5 +- plugins/vst_base/embed-window.cpp | 151 -------- 10 files changed, 115 insertions(+), 610 deletions(-) delete mode 100644 plugins/vst_base/EmbedderApplication.h delete mode 100644 plugins/vst_base/embed-window.cpp diff --git a/include/RemotePlugin.h b/include/RemotePlugin.h index 7b55c306605..588cb866479 100644 --- a/include/RemotePlugin.h +++ b/include/RemotePlugin.h @@ -1217,7 +1217,7 @@ RemotePluginClient::RemotePluginClient( const char * socketPath ) : if( waitForMessage( IdBufferSizeInformation ).id != IdBufferSizeInformation ) { - fprintf( stderr, "Could not get buffer size information.\n" ); + fprintf( stderr, "Could not get buffer size information\n" ); } sendMessage( IdHostInfoGotten ); } diff --git a/plugins/VstEffect/VstEffectControlDialog.cpp b/plugins/VstEffect/VstEffectControlDialog.cpp index b3100db3e75..13c0d4c7a0c 100644 --- a/plugins/VstEffect/VstEffectControlDialog.cpp +++ b/plugins/VstEffect/VstEffectControlDialog.cpp @@ -219,7 +219,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 ); +#if QT_VERSION < 0x050000 l->addWidget( m_pluginWidget, 3, 0, 1, 1, Qt::AlignCenter ); +#endif l->setRowStretch( 5, 1 ); l->setColumnStretch( 1, 1 ); @@ -273,11 +275,11 @@ void VstEffectControlDialog::togglePluginUI( bool checked ) { if( checked ) { - m_plugin->showUI(); + m_plugin->showEditor( NULL, true ); } else { - m_plugin->hideUI(); + m_plugin->hideEditor(); } } } diff --git a/plugins/vestige/vestige.cpp b/plugins/vestige/vestige.cpp index eeb21b9098d..44fb50ba4f7 100644 --- a/plugins/vestige/vestige.cpp +++ b/plugins/vestige/vestige.cpp @@ -261,7 +261,7 @@ void vestigeInstrument::loadFile( const QString & _file ) return; } - m_plugin->showUI(); + m_plugin->showEditor( NULL, false ); if( set_ch_name ) { @@ -735,7 +735,7 @@ void VestigeInstrumentView::toggleGUI( void ) { return; } - m_vi->m_plugin->toggleUI(); + m_vi->m_plugin->toggleEditor(); } diff --git a/plugins/vst_base/CMakeLists.txt b/plugins/vst_base/CMakeLists.txt index 483954760b8..995731a5f2c 100644 --- a/plugins/vst_base/CMakeLists.txt +++ b/plugins/vst_base/CMakeLists.txt @@ -67,18 +67,6 @@ 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 deleted file mode 100644 index 2c5b84ef488..00000000000 --- a/plugins/vst_base/EmbedderApplication.h +++ /dev/null @@ -1,82 +0,0 @@ -/* - * 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 98bd322f690..801999b3306 100644 --- a/plugins/vst_base/RemoteVstPlugin.cpp +++ b/plugins/vst_base/RemoteVstPlugin.cpp @@ -52,7 +52,6 @@ #include #endif -#include #include #endif @@ -67,7 +66,6 @@ #endif -#include #include #include @@ -108,12 +106,6 @@ struct ERect #include #endif -#define EMBEDDER_NAME "embed-window" - -#ifdef LMMS_BUILD_LINUX -#define USE_LINUX_EMBEDDER -#endif - static VstHostLanguages hlang = LanguageEnglish; @@ -123,10 +115,6 @@ RemoteVstPlugin * __plugin = NULL; DWORD __GuiThreadID = 0; -#ifdef USE_LINUX_EMBEDDER -static char * s_embedderPath; -#endif - class RemoteVstPlugin : public RemotePluginClient @@ -312,13 +300,6 @@ class RemoteVstPlugin : public RemotePluginClient 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; @@ -478,47 +459,11 @@ 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; @@ -527,39 +472,6 @@ bool RemoteVstPlugin::processMessage( const message & _m ) 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(); @@ -569,12 +481,22 @@ bool RemoteVstPlugin::processMessage( const message & _m ) initEditor(); } break; -#endif case IdVstLoadPlugin: init( _m.getString() ); break; +// TODO: Drop Windows hack for Qt 4 +#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; @@ -712,20 +634,6 @@ void RemoteVstPlugin::init( const std::string & _plugin_file ) -#ifdef USE_LINUX_EMBEDDER -static void assert_dup2( int oldfd, int newfd ) -{ - if( dup2( oldfd, newfd ) == -1 ) - { - perror( "dup2" ); - exit( EXIT_FAILURE ); - } -} -#endif - - - - static void close_check( int fd ) { if( close( fd ) ) @@ -775,11 +683,7 @@ void RemoteVstPlugin::initEditor() } m_window = CreateWindowEx( 0, "LVSL", m_shortName.c_str(), -#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 ) { @@ -787,6 +691,7 @@ void RemoteVstPlugin::initEditor() return; } + pluginDispatch( effEditOpen, 0, 0, m_window ); ERect * er; @@ -795,159 +700,17 @@ 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, "%" PRIxPTR, 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 %" PRIxPTR " %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 ); -#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; - } +#ifdef LMMS_BUILD_LINUX + m_windowID = (intptr_t) GetProp( m_window, "__wine_x11_whole_window" ); #else -// bSuccess = WriteFile(g_hChildStd_IN_Wr, chBuf, dwRead, &dwWritten, NULL); -// if ( ! bSuccess ) break; + // 64-bit versions of Windows use 32-bit handles for interoperability + m_windowID = (intptr_t) m_window; #endif } @@ -961,32 +724,6 @@ void RemoteVstPlugin::destroyEditor() 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 ); @@ -2105,13 +1842,6 @@ 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() ) @@ -2151,63 +1881,6 @@ 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 @@ -2245,10 +1918,6 @@ 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 @@ -2273,10 +1942,6 @@ 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 903e7f539b7..250c244a06d 100644 --- a/plugins/vst_base/VstPlugin.cpp +++ b/plugins/vst_base/VstPlugin.cpp @@ -31,6 +31,16 @@ #include #include #include +#ifdef LMMS_BUILD_LINUX +#if QT_VERSION < 0x050000 +#include +#include +#else +#include +#endif +#else +#include +#endif #include #ifdef LMMS_BUILD_WIN32 @@ -46,6 +56,33 @@ #include "FileDialog.h" +#if QT_VERSION < 0x050000 +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(); + } +} ; +#endif + + + + VstPlugin::VstPlugin( const QString & _plugin ) : RemotePlugin(), JournallingObject(), @@ -168,15 +205,49 @@ void VstPlugin::showEditor( QWidget * _parent, bool isEffect ) return; } +#if QT_VERSION < 0x050000 m_pluginWidget = new QWidget( _parent ); m_pluginWidget->setFixedSize( m_pluginGeometry ); m_pluginWidget->setWindowTitle( name() ); -#endif - - if( m_pluginWidget ) + if( _parent == NULL ) { - m_pluginWidget->show(); + vstSubWin * sw = new vstSubWin( + gui->mainWindow()->workspace() ); + if( isEffect ) + { + sw->setAttribute( Qt::WA_TranslucentBackground ); + sw->setWindowFlags( Qt::FramelessWindowHint ); + sw->setWidget( m_pluginWidget ); + QX11EmbedContainer * xe = new QX11EmbedContainer( sw ); + xe->embedClient( m_pluginWindowID ); + xe->setFixedSize( m_pluginGeometry ); + xe->show(); + } + else + { + sw->setWindowFlags( Qt::WindowCloseButtonHint ); + sw->setWidget( m_pluginWidget ); + + 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 ); + m_pluginWidget = QWidget::createWindowContainer( window, _parent, + Qt::Window ); + m_pluginWidget->setFixedSize( m_pluginGeometry ); + m_pluginWidget->setWindowTitle( name() ); + // TODO: Synchronize show + // Tell remote that it is embedded + // Wait for remote reply +#endif +#endif + + m_pluginWidget->show(); } @@ -184,7 +255,6 @@ void VstPlugin::showEditor( QWidget * _parent, bool isEffect ) void VstPlugin::hideEditor() { - //TODO: Drop m_pluginWidget, showEditor(), hideEditor() QWidget * w = pluginWidget(); if( w ) { @@ -195,6 +265,18 @@ void VstPlugin::hideEditor() +void VstPlugin::toggleEditor() +{ + QWidget * w = m_pluginWidget; + if( w ) + { + w->setVisible( !w->isVisible() ); + } +} + + + + void VstPlugin::loadSettings( const QDomElement & _this ) { if( pluginWidget() != NULL ) @@ -240,7 +322,6 @@ 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/VstPlugin.h b/plugins/vst_base/VstPlugin.h index 1b6d62ff715..126064bc040 100644 --- a/plugins/vst_base/VstPlugin.h +++ b/plugins/vst_base/VstPlugin.h @@ -54,6 +54,7 @@ class PLUGIN_EXPORT VstPlugin : public RemotePlugin, public JournallingObject void showEditor( QWidget * _parent = NULL, bool isEffect = false ); void hideEditor(); + void toggleEditor(); inline const QString & name() const { @@ -93,6 +94,7 @@ class PLUGIN_EXPORT VstPlugin : public RemotePlugin, public JournallingObject inline QWidget * pluginWidget( bool _top_widget = true ) { +#if QT_VERSION < 0x050000 if( _top_widget && m_pluginWidget ) { if( m_pluginWidget->parentWidget() ) @@ -100,6 +102,7 @@ class PLUGIN_EXPORT VstPlugin : public RemotePlugin, public JournallingObject return m_pluginWidget->parentWidget(); } } +#endif return m_pluginWidget; } diff --git a/plugins/vst_base/communication.h b/plugins/vst_base/communication.h index 0ed3c2fad0f..f4bbfaa8287 100644 --- a/plugins/vst_base/communication.h +++ b/plugins/vst_base/communication.h @@ -56,6 +56,8 @@ enum VstRemoteMessageIDs { // vstPlugin -> remoteVstPlugin IdVstLoadPlugin = IdUserBase, + // TODO: Drop IdVstPluginWindowInformation, Windows hack for Qt 4 + IdVstPluginWindowInformation, IdVstClosePlugin, IdVstSetTempo, IdVstSetLanguage, @@ -72,9 +74,6 @@ 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 deleted file mode 100644 index 17de664d47c..00000000000 --- a/plugins/vst_base/embed-window.cpp +++ /dev/null @@ -1,151 +0,0 @@ -/* - * 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; -} From 2d35de4182d0ed30924220cd81283a570db41c86 Mon Sep 17 00:00:00 2001 From: Javier Serrano Polo Date: Sat, 1 Apr 2017 20:23:49 +0200 Subject: [PATCH 08/53] Debug window ID --- plugins/vst_base/RemoteVstPlugin.cpp | 1 + plugins/vst_base/VstPlugin.cpp | 2 ++ 2 files changed, 3 insertions(+) diff --git a/plugins/vst_base/RemoteVstPlugin.cpp b/plugins/vst_base/RemoteVstPlugin.cpp index 801999b3306..8ca812d4ae2 100644 --- a/plugins/vst_base/RemoteVstPlugin.cpp +++ b/plugins/vst_base/RemoteVstPlugin.cpp @@ -708,6 +708,7 @@ void RemoteVstPlugin::initEditor() ShowWindow( m_window, SW_SHOWNORMAL ); #ifdef LMMS_BUILD_LINUX m_windowID = (intptr_t) GetProp( m_window, "__wine_x11_whole_window" ); +fprintf(stderr, "m_windowID %x\n", m_windowID); #else // 64-bit versions of Windows use 32-bit handles for interoperability m_windowID = (intptr_t) m_window; diff --git a/plugins/vst_base/VstPlugin.cpp b/plugins/vst_base/VstPlugin.cpp index 250c244a06d..fc32dbe93b2 100644 --- a/plugins/vst_base/VstPlugin.cpp +++ b/plugins/vst_base/VstPlugin.cpp @@ -237,6 +237,8 @@ void VstPlugin::showEditor( QWidget * _parent, bool isEffect ) } #else QWindow * window = QWindow::fromWinId( m_pluginWindowID ); +fprintf(stderr, "m_pluginWindowID %x\n", m_pluginWindowID); +fprintf(stderr, "window %p\n", window); m_pluginWidget = QWidget::createWindowContainer( window, _parent, Qt::Window ); m_pluginWidget->setFixedSize( m_pluginGeometry ); From d04fd9f542ff8ff52c60e189ec0688d7f21ba96a Mon Sep 17 00:00:00 2001 From: Javier Serrano Polo Date: Mon, 10 Apr 2017 16:23:17 +0200 Subject: [PATCH 09/53] FluidSynth is currently undistributable --- .travis/linux..install.sh | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.travis/linux..install.sh b/.travis/linux..install.sh index f5988a83d70..9ab73f6c942 100644 --- a/.travis/linux..install.sh +++ b/.travis/linux..install.sh @@ -2,8 +2,9 @@ PACKAGES="cmake libsndfile-dev fftw3-dev libvorbis-dev libogg-dev libmp3lame-dev libasound2-dev libjack-dev libsdl-dev libsamplerate0-dev libstk0-dev - libfluidsynth-dev portaudio19-dev wine-dev g++-multilib libfltk1.3-dev + portaudio19-dev wine-dev g++-multilib libfltk1.3-dev libgig-dev libsoundio-dev" +# TODO: Add libfluidsynth-dev when ready # Help with unmet dependencies PACKAGES="$PACKAGES libjack0" From 7a9396d6ea53ff12dc857b06b54bc75256560fdd Mon Sep 17 00:00:00 2001 From: Javier Serrano Polo Date: Mon, 10 Apr 2017 20:31:11 +0200 Subject: [PATCH 10/53] Let Wine handle the window interface --- include/RemotePlugin.h | 10 ++ plugins/VstEffect/VstEffectControlDialog.cpp | 28 +++- plugins/VstEffect/VstEffectControlDialog.h | 2 + plugins/vestige/vestige.cpp | 8 + plugins/vst_base/RemoteVstPlugin.cpp | 32 ++-- plugins/vst_base/VstPlugin.cpp | 23 ++- plugins/vst_base/VstPlugin.h | 2 + plugins/vst_base/basename.c | 166 ------------------- plugins/vst_base/communication.h | 3 + 9 files changed, 90 insertions(+), 184 deletions(-) delete mode 100644 plugins/vst_base/basename.c diff --git a/include/RemotePlugin.h b/include/RemotePlugin.h index 588cb866479..f3bc718b543 100644 --- a/include/RemotePlugin.h +++ b/include/RemotePlugin.h @@ -428,6 +428,7 @@ enum RemoteMessageIDs IdShowUI, IdHideUI, IdToggleUI, + IdIsUIVisible, IdSaveSettingsToString, IdSaveSettingsToFile, IdLoadSettingsFromString, @@ -816,6 +817,15 @@ class EXPORT RemotePlugin : public QObject, public RemotePluginBase unlock(); } + int isUIVisible() + { + lock(); + sendMessage( IdIsUIVisible ); + unlock(); + message m = waitForMessage( IdIsUIVisible ); + return m.id != IdIsUIVisible ? -1 : m.getInt() ? 1 : 0; + } + inline bool failed() const { return m_failed; diff --git a/plugins/VstEffect/VstEffectControlDialog.cpp b/plugins/VstEffect/VstEffectControlDialog.cpp index 13c0d4c7a0c..ce65912574a 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 ) ), 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,8 +239,10 @@ 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 #if QT_VERSION < 0x050000 l->addWidget( m_pluginWidget, 3, 0, 1, 1, Qt::AlignCenter ); +#endif #endif l->setRowStretch( 5, 1 ); l->setColumnStretch( 1, 1 ); @@ -273,6 +295,7 @@ void VstEffectControlDialog::togglePluginUI( bool checked ) { if( m_plugin ) { +#ifdef LMMS_EMBED_VST if( checked ) { m_plugin->showEditor( NULL, true ); @@ -281,6 +304,9 @@ void VstEffectControlDialog::togglePluginUI( bool checked ) { m_plugin->hideEditor(); } +#else + m_plugin->toggleUI(); +#endif } } diff --git a/plugins/VstEffect/VstEffectControlDialog.h b/plugins/VstEffect/VstEffectControlDialog.h index ae86315abbe..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; diff --git a/plugins/vestige/vestige.cpp b/plugins/vestige/vestige.cpp index 44fb50ba4f7..548cde1bbdd 100644 --- a/plugins/vestige/vestige.cpp +++ b/plugins/vestige/vestige.cpp @@ -261,7 +261,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 ) { @@ -735,7 +739,11 @@ void VestigeInstrumentView::toggleGUI( void ) { return; } +#ifdef LMMS_EMBED_VST m_vi->m_plugin->toggleEditor(); +#else + m_vi->m_plugin->toggleUI(); +#endif } diff --git a/plugins/vst_base/RemoteVstPlugin.cpp b/plugins/vst_base/RemoteVstPlugin.cpp index 8ca812d4ae2..a08af982622 100644 --- a/plugins/vst_base/RemoteVstPlugin.cpp +++ b/plugins/vst_base/RemoteVstPlugin.cpp @@ -59,13 +59,6 @@ #define USE_WS_PREFIX #include -#if defined(LMMS_BUILD_WIN32) || defined(LMMS_BUILD_WIN64) -#include "basename.c" -#else -#include -#endif - - #include #include @@ -100,7 +93,6 @@ struct ERect #ifndef USE_QT_SHMEM #include #include -#include #include #include #include @@ -341,7 +333,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 ), @@ -482,6 +473,11 @@ bool RemoteVstPlugin::processMessage( const message & _m ) } break; + case IdIsUIVisible: + sendMessage( message( IdIsUIVisible ) + .addInt( m_window ? 1 : 0 ) ); + break; + case IdVstLoadPlugin: init( _m.getString() ); break; @@ -682,8 +678,12 @@ void RemoteVstPlugin::initEditor() m_registeredWindowClass = true; } - m_window = CreateWindowEx( 0, "LVSL", m_shortName.c_str(), + m_window = CreateWindowEx( 0, "LVSL", pluginName(), +#ifdef LMMS_EMBED_VST WS_POPUP | WS_SYSMENU | WS_BORDER, +#else + WS_OVERLAPPEDWINDOW & ~WS_MAXIMIZEBOX, +#endif 0, 0, 10, 10, NULL, NULL, hInst, NULL ); if( m_window == NULL ) { @@ -708,7 +708,6 @@ void RemoteVstPlugin::initEditor() ShowWindow( m_window, SW_SHOWNORMAL ); #ifdef LMMS_BUILD_LINUX m_windowID = (intptr_t) GetProp( m_window, "__wine_x11_whole_window" ); -fprintf(stderr, "m_windowID %x\n", m_windowID); #else // 64-bit versions of Windows use 32-bit handles for interoperability m_windowID = (intptr_t) m_window; @@ -746,10 +745,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) @@ -1843,6 +1838,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() ) diff --git a/plugins/vst_base/VstPlugin.cpp b/plugins/vst_base/VstPlugin.cpp index fc32dbe93b2..463b136023c 100644 --- a/plugins/vst_base/VstPlugin.cpp +++ b/plugins/vst_base/VstPlugin.cpp @@ -177,6 +177,7 @@ void VstPlugin::tryLoad( const QString &remoteVstPluginExecutable ) +#ifdef LMMS_EMBED_VST void VstPlugin::showEditor( QWidget * _parent, bool isEffect ) { QWidget * w = pluginWidget(); @@ -237,8 +238,6 @@ void VstPlugin::showEditor( QWidget * _parent, bool isEffect ) } #else QWindow * window = QWindow::fromWinId( m_pluginWindowID ); -fprintf(stderr, "m_pluginWindowID %x\n", m_pluginWindowID); -fprintf(stderr, "window %p\n", window); m_pluginWidget = QWidget::createWindowContainer( window, _parent, Qt::Window ); m_pluginWidget->setFixedSize( m_pluginGeometry ); @@ -275,12 +274,14 @@ void VstPlugin::toggleEditor() w->setVisible( !w->isVisible() ); } } +#endif void VstPlugin::loadSettings( const QDomElement & _this ) { +#ifdef LMMS_EMBED_VST if( pluginWidget() != NULL ) { if( _this.attribute( "guivisible" ).toInt() ) @@ -292,6 +293,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 @@ -324,10 +335,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 126064bc040..0078a9f3f7f 100644 --- a/plugins/vst_base/VstPlugin.h +++ b/plugins/vst_base/VstPlugin.h @@ -52,9 +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 { 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 f4bbfaa8287..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; From 47d0e66fcca0602d9c4c0c689795ea69ce0d46ae Mon Sep 17 00:00:00 2001 From: Tres Finocchiaro Date: Mon, 10 Apr 2017 11:04:18 -0400 Subject: [PATCH 11/53] Re-enable SF2 Per https://github.com/LMMS/lmms/pull/3489#issuecomment-292976079 --- .travis/linux..install.sh | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/.travis/linux..install.sh b/.travis/linux..install.sh index 9ab73f6c942..f5988a83d70 100644 --- a/.travis/linux..install.sh +++ b/.travis/linux..install.sh @@ -2,9 +2,8 @@ PACKAGES="cmake libsndfile-dev fftw3-dev libvorbis-dev libogg-dev libmp3lame-dev libasound2-dev libjack-dev libsdl-dev libsamplerate0-dev libstk0-dev - portaudio19-dev wine-dev g++-multilib libfltk1.3-dev + libfluidsynth-dev portaudio19-dev wine-dev g++-multilib libfltk1.3-dev libgig-dev libsoundio-dev" -# TODO: Add libfluidsynth-dev when ready # Help with unmet dependencies PACKAGES="$PACKAGES libjack0" From e661d26f42dbf69a5e525a288b5a766568fbd662 Mon Sep 17 00:00:00 2001 From: Lukas W Date: Sat, 1 Apr 2017 15:36:06 +0200 Subject: [PATCH 12/53] Add X11EmbedContainer Qt5 port --- plugins/vst_base/CMakeLists.txt | 1 + plugins/vst_base/VstPlugin.cpp | 9 +- plugins/vst_base/VstPlugin.h | 2 +- src/gui/CMakeLists.txt | 19 + src/gui/X11EmbedContainer.cpp | 1175 +++++++++++++++++++++++++++++++ src/gui/X11EmbedContainer.h | 98 +++ 6 files changed, 1295 insertions(+), 9 deletions(-) create mode 100644 src/gui/X11EmbedContainer.cpp create mode 100644 src/gui/X11EmbedContainer.h diff --git a/plugins/vst_base/CMakeLists.txt b/plugins/vst_base/CMakeLists.txt index a9a8088411b..0f5db8f5931 100644 --- a/plugins/vst_base/CMakeLists.txt +++ b/plugins/vst_base/CMakeLists.txt @@ -28,6 +28,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 x11embedcontainer) IF(LMMS_BUILD_LINUX AND NOT WANT_VST_NOWINE) diff --git a/plugins/vst_base/VstPlugin.cpp b/plugins/vst_base/VstPlugin.cpp index 22c9f7c20e6..010774d8574 100644 --- a/plugins/vst_base/VstPlugin.cpp +++ b/plugins/vst_base/VstPlugin.cpp @@ -32,10 +32,7 @@ #include #include #ifdef LMMS_BUILD_LINUX -#if QT_VERSION < 0x050000 -#include -#include -#endif +#include "X11EmbedContainer.h" #else #include #endif @@ -244,25 +241,21 @@ void VstPlugin::showEditor( QWidget * _parent, bool 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 } } diff --git a/plugins/vst_base/VstPlugin.h b/plugins/vst_base/VstPlugin.h index 1b6d62ff715..6d2317c85bd 100644 --- a/plugins/vst_base/VstPlugin.h +++ b/plugins/vst_base/VstPlugin.h @@ -130,7 +130,7 @@ public slots: QString m_plugin; QPointer m_pluginWidget; - int m_pluginWindowID; + WId m_pluginWindowID; QSize m_pluginGeometry; bool m_badDllFormat; diff --git a/src/gui/CMakeLists.txt b/src/gui/CMakeLists.txt index 5b4050bca70..58fb8e2dddd 100644 --- a/src/gui/CMakeLists.txt +++ b/src/gui/CMakeLists.txt @@ -91,6 +91,25 @@ SET(LMMS_SRCS PARENT_SCOPE ) +IF(QT5) + add_library(x11embedcontainer STATIC + X11EmbedContainer + ) + FIND_PACKAGE(Qt5Core COMPONENTS Private) + FIND_PACKAGE(Qt5Widgets COMPONENTS Private) + FIND_PACKAGE(Qt5X11Extras REQUIRED) + + include_directories(${Qt5Core_PRIVATE_INCLUDE_DIRS}) + include_directories(${Qt5Widgets_PRIVATE_INCLUDE_DIRS}) + + + target_link_Libraries(x11embedcontainer + xcb xcb-util X11-xcb Qt5::X11Extras + ) +ELSE() + add_library(x11embedcontainer STATIC /dev/null) +ENDIF() + set(LMMS_UIS ${LMMS_UIS} gui/dialogs/about_dialog.ui diff --git a/src/gui/X11EmbedContainer.cpp b/src/gui/X11EmbedContainer.cpp new file mode 100644 index 00000000000..f34ddb38a55 --- /dev/null +++ b/src/gui/X11EmbedContainer.cpp @@ -0,0 +1,1175 @@ +/**************************************************************************** +** +** Copyright (C) 2015 The Qt Company Ltd. +** Copyright (C) 2017 Lukas W +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the QtGui module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see http://www.qt.io/terms-conditions. For further +** information use the contact form at http://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 or version 3 as published by the Free +** Software Foundation and appearing in the file LICENSE.LGPLv21 and +** LICENSE.LGPLv3 included in the packaging of this file. Please review the +** following information to ensure the GNU Lesser General Public License +** requirements will be met: https://www.gnu.org/licenses/lgpl.html and +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** As a special exception, The Qt Company gives you certain additional +** rights. These rights are described in The Qt Company LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "X11EmbedContainer.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include +#include +//#include +//#include + +#define XK_MISCELLANY +#define XK_LATIN1 +#define None 0 +#include +#include +#include +#include +#include +#include +#include + +#ifndef XK_ISO_Left_Tab +#define XK_ISO_Left_Tab 0xFE20 +#endif + +//#define QX11EMBED_DEBUG +#ifdef QX11EMBED_DEBUG +#include +#endif + +#undef KeyPress +#undef KeyRelease +#undef FocusIn +#undef FocusOut + +static const int XRevertToParent = RevertToParent; +#undef RevertToParent + +QT_BEGIN_NAMESPACE + +/*! + \class QX11EmbedContainer + \ingroup advanced + + \brief The QX11EmbedContainer class provides an XEmbed container + widget. + + XEmbed is an X11 protocol that supports the embedding of a widget + from one application into another application. + + An XEmbed \e container is the graphical location that embeds an + external \e {client widget}. A client widget is a window that is + embedded into a container. + + When a widget has been embedded and the container receives tab + focus, focus is passed on to the widget. When the widget reaches + the end of its focus chain, focus is passed back to the + container. Window activation, accelerator support, modality and + drag and drop (XDND) are also handled. + + QX11EmbedContainer is commonly used for writing panels or + toolbars that hold applets, or for \e swallowing X11 + applications. When writing a panel application, one container + widget is created on the toolbar, and it can then either swallow + another widget using embed(), or allow an XEmbed widget to be + embedded into itself. The container's X11 window ID, which is + retrieved with winId(), must then be known to the client widget. + After embedding, the client's window ID can be retrieved with + clientWinId(). + + In the following example, a container widget is created as the + main widget. It then invokes an application called "playmovie", + passing its window ID as a command line argument. The "playmovie" + program is an XEmbed client widget. The widget embeds itself into + the container using the container's window ID. + + \snippet doc/src/snippets/qx11embedcontainer/main.cpp 0 + + When the client widget is embedded, the container emits the + signal clientIsEmbedded(). The signal clientClosed() is emitted + when a widget is closed. + + It is possible for QX11EmbedContainer to embed XEmbed widgets + from toolkits other than Qt, such as GTK+. Arbitrary (non-XEmbed) + X11 widgets can also be embedded, but the XEmbed-specific + features such as window activation and focus handling are then + lost. + + The GTK+ equivalent of QX11EmbedContainer is GtkSocket. The + corresponding KDE 3 widget is called QXEmbed. + + \sa QX11EmbedWidget, {XEmbed Specification} +*/ + +/*! \fn void QX11EmbedWidget::embedded() + + This signal is emitted by the widget that has been embedded by an + XEmbed container. +*/ + +/*! \fn void QX11EmbedWidget::containerClosed() + + This signal is emitted by the client widget when the container + closes the widget. This can happen if the container itself + closes, or if the widget is rejected. + + The container can reject a widget for any reason, but the most + common cause of a rejection is when an attempt is made to embed a + widget into a container that already has an embedded widget. +*/ + +/*! \fn void QX11EmbedContainer::clientIsEmbedded() + + This signal is emitted by the container when a client widget has + been embedded. +*/ + +/*! \fn void QX11EmbedContainer::clientClosed() + + This signal is emitted by the container when the client widget + closes. +*/ + +/*! + \fn void QX11EmbedWidget::error(QX11EmbedWidget::Error error) + + This signal is emitted if an error occurred as a result of + embedding into or communicating with a container. The specified + \a error describes the problem that occurred. + + \sa QX11EmbedWidget::Error +*/ + +/*! + \fn QX11EmbedContainer::Error QX11EmbedContainer::error() const + + Returns the last error that occurred. +*/ + +/*! \fn void QX11EmbedContainer::error(QX11EmbedContainer::Error error) + + This signal is emitted if an error occurred when embedding or + communicating with a client. The specified \a error describes the + problem that occurred. + + \sa QX11EmbedContainer::Error +*/ + +/*! + \enum QX11EmbedWidget::Error + + \value Unknown An unrecognized error occurred. + + \value InvalidWindowID The X11 window ID of the container was + invalid. This error is usually triggered by passing an invalid + window ID to embedInto(). + + \omitvalue Internal +*/ + +/*! + \enum QX11EmbedContainer::Error + + \value Unknown An unrecognized error occurred. + + \value InvalidWindowID The X11 window ID of the container was + invalid. This error is usually triggered by passing an invalid + window ID to embed(). + + \omitvalue Internal +*/ + + +enum ATOM_ID : int { + _XEMBED + ,_XEMBED_INFO + ,WM_PROTOCOLS + ,WM_DELETE_WINDOW + ,WM_STATE +}; + +static const std::vector> atom_list({ + {_XEMBED, "_XEMBED"}, + {_XEMBED, "_XEMBED_INFO"}, + {WM_PROTOCOLS, "WM_PROTOCOLS"}, + {WM_DELETE_WINDOW, "WM_DELETE_WINDOW"}, + {WM_STATE, "WM_STATE"}, +}); + +static QMap atoms; +static QMutex atoms_lock; + +void initAtoms() +{ + atoms_lock.lock(); + std::vector cookies; + cookies.reserve(atom_list.size()); + + for (const auto& pair : atom_list) + { + cookies.push_back(xcb_intern_atom(QX11Info::connection(), false, pair.second.length(), pair.second.data())); + } + + for (const auto& pair : atom_list) + { + auto cookie = cookies.back(); + + auto reply = xcb_intern_atom_reply(QX11Info::connection(), cookie, nullptr); + atoms[pair.first] = reply->atom; + free(reply); + + cookies.pop_back(); + } + atoms_lock.unlock(); +} + +xcb_atom_t ATOM(int atomID) +{ + return atoms.value(atomID); +} + + +// This is a hack to move topData() out from QWidgetPrivate to public. We +// need to to inspect window()'s embedded state. +class QHackWidget : public QWidget +{ + Q_DECLARE_PRIVATE(QWidget) +public: + QTLWExtra* topData() { return d_func()->topData(); } +}; + +static unsigned int XEMBED_VERSION = 0; + +enum QX11EmbedMessageType { + XEMBED_EMBEDDED_NOTIFY = 0, + XEMBED_WINDOW_ACTIVATE = 1, + XEMBED_WINDOW_DEACTIVATE = 2, + XEMBED_REQUEST_FOCUS = 3, + XEMBED_FOCUS_IN = 4, + XEMBED_FOCUS_OUT = 5, + XEMBED_FOCUS_NEXT = 6, + XEMBED_FOCUS_PREV = 7, + XEMBED_MODALITY_ON = 10, + XEMBED_MODALITY_OFF = 11, + XEMBED_REGISTER_ACCELERATOR = 12, + XEMBED_UNREGISTER_ACCELERATOR = 13, + XEMBED_ACTIVATE_ACCELERATOR = 14 +}; + +enum QX11EmbedFocusInDetail { + XEMBED_FOCUS_CURRENT = 0, + XEMBED_FOCUS_FIRST = 1, + XEMBED_FOCUS_LAST = 2 +}; + +enum QX11EmbedFocusInFlags { + XEMBED_FOCUS_OTHER = (0 << 0), + XEMBED_FOCUS_WRAPAROUND = (1 << 0) +}; + +enum QX11EmbedInfoFlags { + XEMBED_MAPPED = (1 << 0) +}; + +enum QX11EmbedAccelModifiers { + XEMBED_MODIFIER_SHIFT = (1 << 0), + XEMBED_MODIFIER_CONTROL = (1 << 1), + XEMBED_MODIFIER_ALT = (1 << 2), + XEMBED_MODIFIER_SUPER = (1 << 3), + XEMBED_MODIFIER_HYPER = (1 << 4) +}; + +enum QX11EmbedAccelFlags { + XEMBED_ACCELERATOR_OVERLOADED = (1 << 0) +}; + +// Silence the default X11 error handler. +/*static int x11ErrorHandler(Display *, xcb_generic_error_t *) +{ + return 0; +}*/ + +// Returns the X11 timestamp. Maintained mainly by qapplication +// internals, but also updated by the XEmbed widgets. +static xcb_timestamp_t x11Time() +{ + return QX11Info::getTimestamp(); +} + +// Gives the version and flags of the supported XEmbed protocol. +static unsigned int XEmbedVersion() +{ + return 0; +} + +// Sends an XEmbed message. +static void sendXEmbedMessage(WId window, long message, + long detail = 0, long data1 = 0, long data2 = 0) +{ + auto display = QX11Info::display(); + + XClientMessageEvent c; + memset(&c, 0, sizeof(c)); + c.type = ClientMessage; + + c.type = ATOM(_XEMBED); + c.format = 32; + c.display = display; + c.window = window; + + c.data.l[0] = x11Time(); + c.data.l[1] = message; + c.data.l[2] = detail; + c.data.l[3] = data1; + c.data.l[4] = data2; + + XSendEvent(display, window, false, NoEventMask, (XEvent *) &c); +} + +// From qapplication_x11.cpp +static xcb_key_press_event_t lastKeyEvent; + +// The purpose of this global x11 filter is for one to capture the key +// events in their original state, but most importantly this is the +// only way to get the WM_TAKE_FOCUS message from WM_PROTOCOLS. +class X11EventFilter : public QAbstractNativeEventFilter +{ +public: + bool nativeEventFilter(const QByteArray &eventType, void *message, long *result) + { + if (eventType != "xcb_generic_event_t") { + return false; + } + + xcb_generic_event_t *event = reinterpret_cast(message); + if (event->response_type == XCB_KEY_PRESS || event->response_type == XCB_KEY_RELEASE) { + lastKeyEvent = *reinterpret_cast(message); + } + + return false; + } +} static x11EventFilter; + +// +struct functorData +{ + xcb_window_t id, rootWindow; + bool clearedWmState; + bool reparentedToRoot; +}; + + +class QX11EmbedContainerPrivate : public QWidgetPrivate +{ + Q_DECLARE_PUBLIC(QX11EmbedContainer) +public: + inline QX11EmbedContainerPrivate() + { + lastError = QX11EmbedContainer::Unknown; + client = 0; + focusProxy = 0; + clientIsXEmbed = false; + xgrab = false; + } + + bool isEmbedded() const; + void moveInputToProxy(); + + void acceptClient(WId window); + void rejectClient(WId window); + + void checkGrab(); + + WId topLevelParentWinId() const; + + void emitError(QX11EmbedContainer::Error error) { + Q_Q(QX11EmbedContainer); + lastError = error; + emit q->error(error); + } + + WId client; + QWidget *focusProxy; + bool clientIsXEmbed; + bool xgrab; + QRect clientOriginalRect; + QSize wmMinimumSizeHint; + + QX11EmbedContainer::Error lastError; + + static QX11EmbedContainer *activeContainer; +}; + +QX11EmbedContainer *QX11EmbedContainerPrivate::activeContainer = 0; + +/*! + Creates a QX11EmbedContainer object with the given \a parent. +*/ +QX11EmbedContainer::QX11EmbedContainer(QWidget *parent) + : QWidget(*new QX11EmbedContainerPrivate, parent, 0) +{ + initAtoms(); + Q_D(QX11EmbedContainer); + //XSetErrorHandler(x11ErrorHandler); + + setAttribute(Qt::WA_NativeWindow); + setAttribute(Qt::WA_DontCreateNativeAncestors); + createWinId(); + + setFocusPolicy(Qt::StrongFocus); + setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding); + // ### PORT setKeyCompression(false); + setAcceptDrops(true); + setEnabled(false); + + // Everybody gets a focus proxy, but only one toplevel container's + // focus proxy is actually in use. + d->focusProxy = new QWidget(this); + d->focusProxy->setAttribute(Qt::WA_NativeWindow); + d->focusProxy->setAttribute(Qt::WA_DontCreateNativeAncestors); + d->focusProxy->createWinId(); + d->focusProxy->winId(); + d->focusProxy->setGeometry(-1, -1, 1, 1); + + // We need events from the window (activation status) and + // from qApp (keypress/release). + qApp->installEventFilter(this); + + // Install X11 event filter. + QCoreApplication::instance()->installNativeEventFilter(&x11EventFilter); + + XSelectInput(QX11Info::display(), internalWinId(), + KeyPressMask | KeyReleaseMask + | ButtonPressMask | ButtonReleaseMask | ButtonMotionMask + | KeymapStateMask + | PointerMotionMask + | EnterWindowMask | LeaveWindowMask + | FocusChangeMask + | ExposureMask + | StructureNotifyMask + | SubstructureNotifyMask); + + // Make sure our new event mask takes effect as soon as possible. + XFlush(QX11Info::display()); + + // Move input to our focusProxy if this widget is active, and not + // shaded by a modal dialog (in which case isActiveWindow() would + // still return true, but where we must not move input focus). + if (qApp->activeWindow() == window() && !d->isEmbedded()) + d->moveInputToProxy(); + +#ifdef QX11EMBED_DEBUG + qDebug() << "QX11EmbedContainer::QX11EmbedContainer: constructed container" + << (void *)this << "with winId" << winId(); +#endif +} + +/*! + Destructs a QX11EmbedContainer. +*/ +QX11EmbedContainer::~QX11EmbedContainer() +{ + Q_D(QX11EmbedContainer); + if (d->client) { + XUnmapWindow(QX11Info::display(), d->client); + XReparentWindow(QX11Info::display(), d->client, QX11Info::appRootWindow(QX11Info::appScreen()), 0, 0); + } + + if (d->xgrab) + XUngrabButton(QX11Info::display(), AnyButton, AnyModifier, internalWinId()); +} + + +QX11EmbedContainer::Error QX11EmbedContainer::error() const { + return d_func()->lastError; +} + +bool QX11EmbedContainer::nativeEvent(const QByteArray &eventType, void *message, long *result) +{ + if (eventType == "xcb_generic_event_t") { + return x11Event(message, result); + } else { + return false; + } +} + + +/*! \reimp +*/ +void QX11EmbedContainer::paintEvent(QPaintEvent *) +{ +} + +/*! \internal + + Returns whether or not the windows' embedded flag is set. +*/ +bool QX11EmbedContainerPrivate::isEmbedded() const +{ + Q_Q(const QX11EmbedContainer); + return ((QHackWidget *)q->window())->topData()->embedded == 1; +} + +/*! \internal + + Returns the parentWinId of the window. +*/ +WId QX11EmbedContainerPrivate::topLevelParentWinId() const +{ + Q_Q(const QX11EmbedContainer); + return q->window()->effectiveWinId(); + //TODO + //return ((QHackWidget *)q->window())->topData()->parentWinId; +} + +/*! + If the container has an embedded widget, this function returns + the X11 window ID of the client; otherwise it returns 0. +*/ +WId QX11EmbedContainer::clientWinId() const +{ + Q_D(const QX11EmbedContainer); + return d->client; +} + +/*! + Instructs the container to embed the X11 window with window ID \a + id. The client widget will then move on top of the container + window and be resized to fit into the container. + + The \a id should be the ID of a window controlled by an XEmbed + enabled application, but this is not mandatory. If \a id does not + belong to an XEmbed client widget, then focus handling, + activation, accelerators and other features will not work + properly. +*/ +void QX11EmbedContainer::embedClient(WId id) +{ + Q_D(QX11EmbedContainer); + + if (id == 0) { + d->emitError(InvalidWindowID); + return; + } + + // Walk up the tree of parent windows to prevent embedding of ancestors. + WId thisId = internalWinId(); + xcb_window_t rootReturn; + xcb_window_t parentReturn; + do { + auto cookie = xcb_query_tree(QX11Info::connection(), thisId); + xcb_generic_error_t* error = nullptr; + auto reply = xcb_query_tree_reply(QX11Info::connection(), cookie, &error); + + if (error) { + d->emitError(InvalidWindowID); + return; + } + + rootReturn = reply->root; + parentReturn = reply->parent; + + thisId = parentReturn; + if (id == thisId) { + d->emitError(InvalidWindowID); + return; + } + } while (thisId != rootReturn); + + switch (XReparentWindow(QX11Info::display(), id, internalWinId(), 0, 0)) { + case BadWindow: + case BadMatch: + d->emitError(InvalidWindowID); + break; + default: + break; + } +} + +/*! \internal + + Handles key, activation and focus events for the container. +*/ +bool QX11EmbedContainer::eventFilter(QObject *o, QEvent *event) +{ + Q_D(QX11EmbedContainer); + switch (event->type()) { + case QEvent::KeyPress: + // Forward any keypresses to our client. + if (o == this && d->client) { + lastKeyEvent.event = d->client; + xcb_send_event(QX11Info::connection(), false, d->client, KeyPressMask, (char*) &lastKeyEvent); + return true; + } + break; + case QEvent::KeyRelease: + // Forward any keyreleases to our client. + if (o == this && d->client) { + lastKeyEvent.event = d->client; + xcb_send_event(QX11Info::connection(), false, d->client, KeyReleaseMask, (char*) &lastKeyEvent); + return true; + } + break; + + case QEvent::WindowActivate: + // When our container window is activated, we pass the + // activation message on to our client. Note that X input + // focus is set to our focus proxy. We want to intercept all + // keypresses. + if (o == window() && d->client) { + if (d->clientIsXEmbed) { + sendXEmbedMessage(d->client, XEMBED_WINDOW_ACTIVATE); + } else { + d->checkGrab(); + if (hasFocus()) + XSetInputFocus(QX11Info::display(), d->client, XRevertToParent, x11Time()); + } + if (!d->isEmbedded()) + d->moveInputToProxy(); + } + break; + case QEvent::WindowDeactivate: + // When our container window is deactivated, we pass the + // deactivation message to our client. + if (o == window() && d->client) { + if (d->clientIsXEmbed) + sendXEmbedMessage(d->client, XEMBED_WINDOW_DEACTIVATE); + else + d->checkGrab(); + } + break; + case QEvent::FocusIn: + // When receiving FocusIn events generated by Tab or Backtab, + // we pass focus on to our client. Any mouse activity is sent + // directly to the client, and it will ask us for focus with + // XEMBED_REQUEST_FOCUS. + if (o == this && d->client) { + if (!d->isEmbedded()) + d->activeContainer = this; + + if (d->clientIsXEmbed) { + if (!d->isEmbedded()) + d->moveInputToProxy(); + + QFocusEvent *fe = (QFocusEvent *)event; + switch (fe->reason()) { + case Qt::TabFocusReason: + sendXEmbedMessage(d->client, XEMBED_FOCUS_IN, XEMBED_FOCUS_FIRST); + break; + case Qt::BacktabFocusReason: + sendXEmbedMessage(d->client, XEMBED_FOCUS_IN, XEMBED_FOCUS_LAST); + break; + default: + sendXEmbedMessage(d->client, XEMBED_FOCUS_IN, XEMBED_FOCUS_CURRENT); + break; + } + } else { + d->checkGrab(); + XSetInputFocus(QX11Info::display(), d->client, XRevertToParent, x11Time()); + } + } + + break; + case QEvent::FocusOut: { + // When receiving a FocusOut, we ask our client to remove its + // focus. + if (o == this && d->client) { + if (!d->isEmbedded()) { + d->activeContainer = 0; + if (isActiveWindow()) + d->moveInputToProxy(); + } + + if (d->clientIsXEmbed) { + QFocusEvent *fe = (QFocusEvent *)event; + if (o == this && d->client && fe->reason() != Qt::ActiveWindowFocusReason) + sendXEmbedMessage(d->client, XEMBED_FOCUS_OUT); + } else { + d->checkGrab(); + } + } + } + break; + + case QEvent::Close: { + if (o == this && d->client) { + // Unmap the client and reparent it to the root window. + // Wait until the messages have been processed. Then ask + // the window manager to delete the window. + XUnmapWindow(QX11Info::display(), d->client); + XReparentWindow(QX11Info::display(), d->client, QX11Info::appRootWindow(QX11Info::appScreen()), 0, 0); + XSync(QX11Info::display(), false); + + XEvent ev; + memset(&ev, 0, sizeof(ev)); + ev.xclient.type = ClientMessage; + ev.xclient.window = d->client; + ev.xclient.message_type = ATOM(WM_PROTOCOLS); + ev.xclient.format = 32; + ev.xclient.data.s[0] = ATOM(WM_DELETE_WINDOW); + XSendEvent(QX11Info::display(), d->client, false, NoEventMask, &ev); + + XFlush(QX11Info::display()); + d->client = 0; + d->clientIsXEmbed = false; + d->wmMinimumSizeHint = QSize(); + updateGeometry(); + setEnabled(false); + update(); + + emit clientClosed(); + } + } + default: + break; + } + + return QWidget::eventFilter(o, event); +} + +/*! \internal + + Handles X11 events for the container. +*/ +bool QX11EmbedContainer::x11Event(void *e, long* result) +{ + xcb_generic_event_t* event = reinterpret_cast(e); + Q_D(QX11EmbedContainer); + + switch (event->response_type & ~0x80) { + case XCB_CREATE_NOTIFY: + // The client created an embedded window. + if (d->client) + d->rejectClient(reinterpret_cast(event)->window); + else + d->acceptClient(reinterpret_cast(event)->window); + break; + case XCB_DESTROY_NOTIFY: + if (reinterpret_cast(event)->window == d->client) { + // The client died. + d->client = 0; + d->clientIsXEmbed = false; + d->wmMinimumSizeHint = QSize(); + updateGeometry(); + update(); + setEnabled(false); + emit clientClosed(); + } + break; + case XCB_REPARENT_NOTIFY: + // The client sends us this if it reparents itself out of our + // widget. + { + auto* event = reinterpret_cast(e); + if (event->window == d->client && event->parent != internalWinId()) { + d->client = 0; + d->clientIsXEmbed = false; + d->wmMinimumSizeHint = QSize(); + updateGeometry(); + update(); + setEnabled(false); + emit clientClosed(); + } else if (event->parent == internalWinId()) { + // The client reparented itself into this window. + if (d->client) + d->rejectClient(event->window); + else + d->acceptClient(event->window); + } + break; + } + case XCB_CLIENT_MESSAGE: { + auto* event = reinterpret_cast(e); + if (event->type == ATOM(_XEMBED)) { + // Ignore XEMBED messages not to ourselves + if (event->window != internalWinId()) + break; + + // Receiving an XEmbed message means the client + // is an XEmbed client. + d->clientIsXEmbed = true; + + Time msgtime = (Time) event->data.data32[0]; + //TODO + //if (msgtime > X11->time) + //X11->time = msgtime; + + switch (event->data.data32[1]) { + case XEMBED_REQUEST_FOCUS: { + // This typically happens when the client gets focus + // because of a mouse click. + if (!hasFocus()) + setFocus(Qt::OtherFocusReason); + + // The message is passed along to the topmost container + // that eventually responds with a XEMBED_FOCUS_IN + // message. The focus in message is passed all the way + // back until it reaches the original focus + // requestor. In the end, not only the original client + // has focus, but also all its ancestor containers. + if (d->isEmbedded()) { + // If our window's embedded flag is set, then + // that suggests that we are part of a client. The + // parentWinId will then point to an container to whom + // we must pass this message. + sendXEmbedMessage(d->topLevelParentWinId(), XEMBED_REQUEST_FOCUS); + } else { + // Our window's embedded flag is not set, + // so we are the topmost container. We respond to + // the focus request message with a focus in + // message. This message will pass on from client + // to container to client until it reaches the + // originator of the XEMBED_REQUEST_FOCUS message. + sendXEmbedMessage(d->client, XEMBED_FOCUS_IN, XEMBED_FOCUS_CURRENT); + } + + break; + } + case XEMBED_FOCUS_NEXT: + // Client sends this event when it received a tab + // forward and was at the end of its focus chain. If + // we are the only widget in the focus chain, we send + // ourselves a FocusIn event. + if (d->focus_next != this) { + focusNextPrevChild(true); + } else { + QFocusEvent event(QEvent::FocusIn, Qt::TabFocusReason); + qApp->sendEvent(this, &event); + } + + break; + case XEMBED_FOCUS_PREV: + // Client sends this event when it received a backtab + // and was at the start of its focus chain. If we are + // the only widget in the focus chain, we send + // ourselves a FocusIn event. + if (d->focus_next != this) { + focusNextPrevChild(false); + } else { + QFocusEvent event(QEvent::FocusIn, Qt::BacktabFocusReason); + qApp->sendEvent(this, &event); + } + + break; + default: + break; + } + } + } + break; + case XCB_BUTTON_PRESS: + if (!d->clientIsXEmbed) { + setFocus(Qt::MouseFocusReason); + XAllowEvents(QX11Info::display(), ReplayPointer, CurrentTime); + return true; + } + break; + case XCB_BUTTON_RELEASE: + if (!d->clientIsXEmbed) + XAllowEvents(QX11Info::display(), SyncPointer, CurrentTime); + break; + default: + break; + } + + return QWidget::nativeEvent("xcb_generic_event_t", e, result); +} + +/*! \internal + + Whenever the container is resized, we need to resize our client. +*/ +void QX11EmbedContainer::resizeEvent(QResizeEvent *) +{ + Q_D(QX11EmbedContainer); + if (d->client) + XResizeWindow(QX11Info::display(), d->client, width(), height()); +} + +/*! \internal + + We use the QShowEvent to signal to our client that we want it to + map itself. We do this by changing its window property + XEMBED_INFO. The client will get an X11 PropertyNotify. +*/ +void QX11EmbedContainer::showEvent(QShowEvent *) +{ + Q_D(QX11EmbedContainer); + if (d->client) { + long data[] = {XEMBED_VERSION, XEMBED_MAPPED}; + XChangeProperty(QX11Info::display(), d->client, ATOM(_XEMBED_INFO), ATOM(_XEMBED_INFO), 32, + PropModeReplace, (unsigned char *) data, 2); + } +} + +/*! \internal + + We use the QHideEvent to signal to our client that we want it to + unmap itself. We do this by changing its window property + XEMBED_INFO. The client will get an X11 PropertyNotify. +*/ +void QX11EmbedContainer::hideEvent(QHideEvent *) +{ + Q_D(QX11EmbedContainer); + if (d->client) { + long data[] = {XEMBED_VERSION, XEMBED_MAPPED}; + XChangeProperty(QX11Info::display(), d->client, ATOM(_XEMBED_INFO), ATOM(_XEMBED_INFO), 32, + PropModeReplace, (unsigned char *) data, 2); + } +} + +/*! + \reimp +*/ +bool QX11EmbedContainer::event(QEvent *event) +{ + if (event->type() == QEvent::ParentChange) { + XSelectInput(QX11Info::display(), internalWinId(), + KeyPressMask | KeyReleaseMask + | ButtonPressMask | ButtonReleaseMask | ButtonMotionMask + | KeymapStateMask + | PointerMotionMask + | EnterWindowMask | LeaveWindowMask + | FocusChangeMask + | ExposureMask + | StructureNotifyMask + | SubstructureNotifyMask); + } + return QWidget::event(event); +} + +/*! \internal + + Rejects a client window by reparenting it to the root window. The + client will receive a reparentnotify, and will most likely assume + that the container has shut down. The XEmbed protocol does not + define any way to reject a client window, but this is a clean way + to do it. +*/ +void QX11EmbedContainerPrivate::rejectClient(WId window) +{ + Q_Q(QX11EmbedContainer); + q->setEnabled(false); + XRemoveFromSaveSet(QX11Info::display(), client); + XReparentWindow(QX11Info::display(), window, QX11Info::appRootWindow(QX11Info::appScreen()), 0, 0); +} + +/*! \internal + + Accepts a client by mapping it, resizing it and optionally + activating and giving it logical focusing through XEMBED messages. +*/ +void QX11EmbedContainerPrivate::acceptClient(WId window) +{ + Q_Q(QX11EmbedContainer); + client = window; + q->setEnabled(true); + + // This tells Qt that we wish to forward DnD messages to + // our client. + if (!extra) + createExtra(); + //TODO + //extraData()->xDndProxy = client; + + unsigned int version = XEmbedVersion(); + + Atom actual_type_return; + int actual_format_return; + unsigned long nitems_return = 0; + unsigned long bytes_after_return; + unsigned char *prop_return = 0; + unsigned int clientversion = 0; + + // Add this client to our saveset, so if we crash, the client window + // doesn't get destroyed. This is useful for containers that restart + // automatically after a crash, because it can simply reembed its clients + // without having to restart them (KDE panel). + XAddToSaveSet(QX11Info::display(), client); + + // XEmbed clients have an _XEMBED_INFO property in which we can + // fetch the version + if (XGetWindowProperty(QX11Info::display(), client, ATOM(_XEMBED_INFO), 0, 2, false, + ATOM(_XEMBED_INFO), &actual_type_return, &actual_format_return, + &nitems_return, &bytes_after_return, &prop_return) == Success) { + + if (actual_type_return != None && actual_format_return != 0) { + // Clients with the _XEMBED_INFO property are XEMBED clients. + clientIsXEmbed = true; + + long *p = (long *)prop_return; + if (nitems_return >= 2) + clientversion = (unsigned int)p[0]; + } + + XFree(prop_return); + } + + // Store client window's original size and placement. + Window root; + int x_return, y_return; + unsigned int width_return, height_return, border_width_return, depth_return; + XGetGeometry(QX11Info::display(), client, &root, &x_return, &y_return, + &width_return, &height_return, &border_width_return, &depth_return); + clientOriginalRect.setCoords(x_return, y_return, + x_return + width_return - 1, + y_return + height_return - 1); + + // Ask the client for its minimum size. + XSizeHints size; + long msize; + if (XGetWMNormalHints(QX11Info::display(), client, &size, &msize) && (size.flags & PMinSize)) { + wmMinimumSizeHint = QSize(size.min_width, size.min_height); + q->updateGeometry(); + } + + // The container should set the data2 field to the lowest of its + // supported version number and that of the client (from + // _XEMBED_INFO property). + unsigned int minversion = version > clientversion ? clientversion : version; + sendXEmbedMessage(client, XEMBED_EMBEDDED_NOTIFY, q->internalWinId(), minversion); + XMapWindow(QX11Info::display(), client); + + // Resize it, but no smaller than its minimum size hint. + XResizeWindow(QX11Info::display(), + client, + qMax(q->width(), wmMinimumSizeHint.width()), + qMax(q->height(), wmMinimumSizeHint.height())); + q->update(); + + // Not mentioned in the protocol is that if the container + // is already active, the client must be activated to work + // properly. + if (q->window()->isActiveWindow()) + sendXEmbedMessage(client, XEMBED_WINDOW_ACTIVATE); + + // Also, if the container already has focus, then it must + // send a focus in message to its new client; otherwise we ask + // it to remove focus. + if (q->focusWidget() == q && q->hasFocus()) + sendXEmbedMessage(client, XEMBED_FOCUS_IN, XEMBED_FOCUS_FIRST); + else + sendXEmbedMessage(client, XEMBED_FOCUS_OUT); + + if (!clientIsXEmbed) { + checkGrab(); + if (q->hasFocus()) { + XSetInputFocus(QX11Info::display(), client, XRevertToParent, x11Time()); + } + } else { + if (!isEmbedded()) + moveInputToProxy(); + } + + emit q->clientIsEmbedded(); +} + +/*! \internal + + Moves X11 keyboard input focus to the focusProxy, unless the focus + is there already. When X11 keyboard input focus is on the + focusProxy, which is a child of the container and a sibling of the + client, X11 keypresses and keyreleases will always go to the proxy + and not to the client. +*/ +void QX11EmbedContainerPrivate::moveInputToProxy() +{ + Q_Q(QX11EmbedContainer); + // Following Owen Taylor's advice from the XEmbed specification to + // always use CurrentTime when no explicit user action is involved. + XSetInputFocus(QX11Info::display(), focusProxy->internalWinId(), XRevertToParent, CurrentTime); +} + +/*! \internal + + Ask the window manager to give us a default minimum size. +*/ +QSize QX11EmbedContainer::minimumSizeHint() const +{ + Q_D(const QX11EmbedContainer); + if (!d->client || !d->wmMinimumSizeHint.isValid()) + return QWidget::minimumSizeHint(); + return d->wmMinimumSizeHint; +} + +/*! \internal + +*/ +void QX11EmbedContainerPrivate::checkGrab() +{ + Q_Q(QX11EmbedContainer); + if (!clientIsXEmbed && q->isActiveWindow() && !q->hasFocus()) { + if (!xgrab) { + XGrabButton(QX11Info::display(), AnyButton, AnyModifier, q->internalWinId(), + true, ButtonPressMask, GrabModeSync, GrabModeAsync, + None, None); + } + xgrab = true; + } else { + if (xgrab) + XUngrabButton(QX11Info::display(), AnyButton, AnyModifier, q->internalWinId()); + xgrab = false; + } +} + +/*! + Detaches the client from the embedder. The client will appear as a + standalone window on the desktop. +*/ +void QX11EmbedContainer::discardClient() +{ + Q_D(QX11EmbedContainer); + if (d->client) { + XResizeWindow(QX11Info::display(), d->client, d->clientOriginalRect.width(), + d->clientOriginalRect.height()); + + d->rejectClient(d->client); + } +} + +QT_END_NAMESPACE diff --git a/src/gui/X11EmbedContainer.h b/src/gui/X11EmbedContainer.h new file mode 100644 index 00000000000..8e261098f30 --- /dev/null +++ b/src/gui/X11EmbedContainer.h @@ -0,0 +1,98 @@ +/**************************************************************************** +** +** Copyright (C) 2015 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the QtGui module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see http://www.qt.io/terms-conditions. For further +** information use the contact form at http://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 or version 3 as published by the Free +** Software Foundation and appearing in the file LICENSE.LGPLv21 and +** LICENSE.LGPLv3 included in the packaging of this file. Please review the +** following information to ensure the GNU Lesser General Public License +** requirements will be met: https://www.gnu.org/licenses/lgpl.html and +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** As a special exception, The Qt Company gives you certain additional +** rights. These rights are described in The Qt Company LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef X11EMBEDCONTAINER_H +#define X11EMBEDCONTAINER_H + +#include + +#if QT_VERSION < 0x050000 +#include +#else + +#include + +class QX11EmbedContainerPrivate; +class Q_GUI_EXPORT QX11EmbedContainer : public QWidget +{ + Q_OBJECT +public: + QX11EmbedContainer(QWidget *parent = 0); + ~QX11EmbedContainer(); + + void embedClient(WId id); + void discardClient(); + + WId clientWinId() const; + + QSize minimumSizeHint() const; + + enum Error { + Unknown, + Internal, + InvalidWindowID + }; + Error error() const; + +Q_SIGNALS: + void clientIsEmbedded(); + void clientClosed(); + void error(QX11EmbedContainer::Error); + +protected: + //bool x11Event(XEvent *); + bool nativeEvent(const QByteArray &eventType, void *message, long *result); + bool eventFilter(QObject *, QEvent *) override; + void paintEvent(QPaintEvent *e) override; + void resizeEvent(QResizeEvent *); + void showEvent(QShowEvent *); + void hideEvent(QHideEvent *); + bool event(QEvent *); + +private: + Q_DECLARE_PRIVATE(QX11EmbedContainer) + Q_DISABLE_COPY(QX11EmbedContainer) + bool x11Event(void *e, long *result); +}; + +#endif // X11EMBEDCONTAINER_H + +#endif From adef05fb71ddf595138e3eee8dddf0514072b8cf Mon Sep 17 00:00:00 2001 From: Lukas W Date: Sat, 8 Apr 2017 17:11:56 +0200 Subject: [PATCH 13/53] X11EmbedContainer: Fix XEMBED protocol implementation --- include/RemotePlugin.h | 16 +- plugins/vst_base/RemoteVstPlugin.cpp | 8 +- plugins/vst_base/VstPlugin.cpp | 24 +- src/core/RemotePlugin.cpp | 14 ++ src/gui/SubWindow.cpp | 5 +- src/gui/X11EmbedContainer.cpp | 330 +++++++++------------------ src/gui/X11EmbedContainer.h | 10 +- 7 files changed, 153 insertions(+), 254 deletions(-) diff --git a/include/RemotePlugin.h b/include/RemotePlugin.h index d016c5acd21..977f041a50c 100644 --- a/include/RemotePlugin.h +++ b/include/RemotePlugin.h @@ -787,19 +787,6 @@ class EXPORT RemotePlugin : public QObject, public RemotePluginBase unlock(); } - void showUI() - { - lock(); - sendMessage( IdShowUI ); - unlock(); - } - - void hideUI() - { - lock(); - sendMessage( IdHideUI ); - unlock(); - } inline bool failed() const { @@ -816,6 +803,9 @@ class EXPORT RemotePlugin : public QObject, public RemotePluginBase m_commMutex.unlock(); } +public slots: + void showUI(); + void hideUI(); protected: inline void setSplittedChannels( bool _on ) diff --git a/plugins/vst_base/RemoteVstPlugin.cpp b/plugins/vst_base/RemoteVstPlugin.cpp index 03e160a8dd7..c0d4b9b9237 100644 --- a/plugins/vst_base/RemoteVstPlugin.cpp +++ b/plugins/vst_base/RemoteVstPlugin.cpp @@ -556,6 +556,11 @@ bool RemoteVstPlugin::processMessage( const message & _m ) break; } + case IdShowUI: + ShowWindow( m_window, SW_SHOWNORMAL ); + UpdateWindow( m_window ); + break; + default: return RemotePluginClient::processMessage( _m ); } @@ -687,9 +692,6 @@ void RemoteVstPlugin::initEditor() 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" ); #endif diff --git a/plugins/vst_base/VstPlugin.cpp b/plugins/vst_base/VstPlugin.cpp index 010774d8574..83d82e75e58 100644 --- a/plugins/vst_base/VstPlugin.cpp +++ b/plugins/vst_base/VstPlugin.cpp @@ -229,35 +229,31 @@ void VstPlugin::showEditor( QWidget * _parent, bool isEffect ) return; } - m_pluginWidget = new QWidget( _parent ); + QX11EmbedContainer * xe = new QX11EmbedContainer; + m_pluginWidget = xe; m_pluginWidget->setFixedSize( m_pluginGeometry ); m_pluginWidget->setWindowTitle( name() ); + + connect(xe, SIGNAL(clientIsEmbedded()), this, SLOT(showUI())); + if( _parent == NULL ) { vstSubWin * sw = new vstSubWin( gui->mainWindow()->workspace() ); + sw->setWidget( m_pluginWidget ); + if( isEffect ) { sw->setAttribute( Qt::WA_TranslucentBackground ); sw->setWindowFlags( Qt::FramelessWindowHint ); - sw->setWidget( m_pluginWidget ); - QX11EmbedContainer * xe = new QX11EmbedContainer( sw ); - xe->embedClient( m_pluginWindowID ); - xe->setFixedSize( m_pluginGeometry ); - xe->show(); - } + } else { sw->setWindowFlags( Qt::WindowCloseButtonHint ); - sw->setWidget( m_pluginWidget ); - - QX11EmbedContainer * xe = new QX11EmbedContainer( sw ); - xe->embedClient( m_pluginWindowID ); - xe->setFixedSize( m_pluginGeometry ); - xe->move( 4, 24 ); - xe->show(); } } + xe->embedClient( m_pluginWindowID ); + xe->setFixedSize( m_pluginGeometry ); #endif diff --git a/src/core/RemotePlugin.cpp b/src/core/RemotePlugin.cpp index bb7e39c6afd..064d77b2c33 100644 --- a/src/core/RemotePlugin.cpp +++ b/src/core/RemotePlugin.cpp @@ -392,6 +392,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 e92c4cbcbc7..284e116d808 100644 --- a/src/gui/SubWindow.cpp +++ b/src/gui/SubWindow.cpp @@ -97,7 +97,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() ); diff --git a/src/gui/X11EmbedContainer.cpp b/src/gui/X11EmbedContainer.cpp index f34ddb38a55..f5a22eb8736 100644 --- a/src/gui/X11EmbedContainer.cpp +++ b/src/gui/X11EmbedContainer.cpp @@ -59,8 +59,15 @@ #include #include +#include + +#include +#include +#include //#include -//#include + +#include +#include #define XK_MISCELLANY #define XK_LATIN1 @@ -92,139 +99,6 @@ static const int XRevertToParent = RevertToParent; QT_BEGIN_NAMESPACE -/*! - \class QX11EmbedContainer - \ingroup advanced - - \brief The QX11EmbedContainer class provides an XEmbed container - widget. - - XEmbed is an X11 protocol that supports the embedding of a widget - from one application into another application. - - An XEmbed \e container is the graphical location that embeds an - external \e {client widget}. A client widget is a window that is - embedded into a container. - - When a widget has been embedded and the container receives tab - focus, focus is passed on to the widget. When the widget reaches - the end of its focus chain, focus is passed back to the - container. Window activation, accelerator support, modality and - drag and drop (XDND) are also handled. - - QX11EmbedContainer is commonly used for writing panels or - toolbars that hold applets, or for \e swallowing X11 - applications. When writing a panel application, one container - widget is created on the toolbar, and it can then either swallow - another widget using embed(), or allow an XEmbed widget to be - embedded into itself. The container's X11 window ID, which is - retrieved with winId(), must then be known to the client widget. - After embedding, the client's window ID can be retrieved with - clientWinId(). - - In the following example, a container widget is created as the - main widget. It then invokes an application called "playmovie", - passing its window ID as a command line argument. The "playmovie" - program is an XEmbed client widget. The widget embeds itself into - the container using the container's window ID. - - \snippet doc/src/snippets/qx11embedcontainer/main.cpp 0 - - When the client widget is embedded, the container emits the - signal clientIsEmbedded(). The signal clientClosed() is emitted - when a widget is closed. - - It is possible for QX11EmbedContainer to embed XEmbed widgets - from toolkits other than Qt, such as GTK+. Arbitrary (non-XEmbed) - X11 widgets can also be embedded, but the XEmbed-specific - features such as window activation and focus handling are then - lost. - - The GTK+ equivalent of QX11EmbedContainer is GtkSocket. The - corresponding KDE 3 widget is called QXEmbed. - - \sa QX11EmbedWidget, {XEmbed Specification} -*/ - -/*! \fn void QX11EmbedWidget::embedded() - - This signal is emitted by the widget that has been embedded by an - XEmbed container. -*/ - -/*! \fn void QX11EmbedWidget::containerClosed() - - This signal is emitted by the client widget when the container - closes the widget. This can happen if the container itself - closes, or if the widget is rejected. - - The container can reject a widget for any reason, but the most - common cause of a rejection is when an attempt is made to embed a - widget into a container that already has an embedded widget. -*/ - -/*! \fn void QX11EmbedContainer::clientIsEmbedded() - - This signal is emitted by the container when a client widget has - been embedded. -*/ - -/*! \fn void QX11EmbedContainer::clientClosed() - - This signal is emitted by the container when the client widget - closes. -*/ - -/*! - \fn void QX11EmbedWidget::error(QX11EmbedWidget::Error error) - - This signal is emitted if an error occurred as a result of - embedding into or communicating with a container. The specified - \a error describes the problem that occurred. - - \sa QX11EmbedWidget::Error -*/ - -/*! - \fn QX11EmbedContainer::Error QX11EmbedContainer::error() const - - Returns the last error that occurred. -*/ - -/*! \fn void QX11EmbedContainer::error(QX11EmbedContainer::Error error) - - This signal is emitted if an error occurred when embedding or - communicating with a client. The specified \a error describes the - problem that occurred. - - \sa QX11EmbedContainer::Error -*/ - -/*! - \enum QX11EmbedWidget::Error - - \value Unknown An unrecognized error occurred. - - \value InvalidWindowID The X11 window ID of the container was - invalid. This error is usually triggered by passing an invalid - window ID to embedInto(). - - \omitvalue Internal -*/ - -/*! - \enum QX11EmbedContainer::Error - - \value Unknown An unrecognized error occurred. - - \value InvalidWindowID The X11 window ID of the container was - invalid. This error is usually triggered by passing an invalid - window ID to embed(). - - \omitvalue Internal -*/ - - enum ATOM_ID : int { _XEMBED ,_XEMBED_INFO @@ -235,7 +109,7 @@ enum ATOM_ID : int { static const std::vector> atom_list({ {_XEMBED, "_XEMBED"}, - {_XEMBED, "_XEMBED_INFO"}, + {_XEMBED_INFO, "_XEMBED_INFO"}, {WM_PROTOCOLS, "WM_PROTOCOLS"}, {WM_DELETE_WINDOW, "WM_DELETE_WINDOW"}, {WM_STATE, "WM_STATE"}, @@ -246,26 +120,31 @@ static QMutex atoms_lock; void initAtoms() { - atoms_lock.lock(); - std::vector cookies; - cookies.reserve(atom_list.size()); + QMutexLocker locker(&atoms_lock); Q_UNUSED(locker); + + std::queue cookies; for (const auto& pair : atom_list) { - cookies.push_back(xcb_intern_atom(QX11Info::connection(), false, pair.second.length(), pair.second.data())); + cookies.push(xcb_intern_atom(QX11Info::connection(), false, pair.second.length(), pair.second.data())); } for (const auto& pair : atom_list) { - auto cookie = cookies.back(); + auto cookie = cookies.front(); + cookies.pop(); auto reply = xcb_intern_atom_reply(QX11Info::connection(), cookie, nullptr); atoms[pair.first] = reply->atom; - free(reply); - cookies.pop_back(); + Q_ASSERT(pair.second == XGetAtomName(QX11Info::display(), reply->atom)); + +#ifdef QX11EMBED_DEBUG + qDebug() << "atom" << QString::fromStdString(pair.second) + << XGetAtomName(QX11Info::display(), reply->atom) << reply->atom; +#endif + free(reply); } - atoms_lock.unlock(); } xcb_atom_t ATOM(int atomID) @@ -274,6 +153,33 @@ xcb_atom_t ATOM(int atomID) } +struct xembed_info +{ + uint32_t version; + uint32_t flags; +}; + +xembed_info* get_xembed_info(xcb_window_t window) +{ + auto cookie = xcb_get_property(QX11Info::connection(), 0, window, ATOM(_XEMBED_INFO), ATOM(_XEMBED_INFO), 0, 2); + if (auto reply = xcb_get_property_reply(QX11Info::connection(), cookie, nullptr)) { + auto val_len = xcb_get_property_value_length(reply); + if (val_len < 2) { +#ifdef QX11EMBED_DEBUG + qDebug() << "Client has malformed _XEMBED_INFO property, len is" << val_len; +#endif + free(reply); + return nullptr; + } + + void* result = malloc(sizeof(xembed_info)); + memcpy(result, xcb_get_property_value(reply), sizeof(xembed_info)); + return reinterpret_cast(result); + } + + return nullptr; +} + // This is a hack to move topData() out from QWidgetPrivate to public. We // need to to inspect window()'s embedded state. class QHackWidget : public QWidget @@ -347,6 +253,7 @@ static unsigned int XEmbedVersion() return 0; } + // Sends an XEmbed message. static void sendXEmbedMessage(WId window, long message, long detail = 0, long data1 = 0, long data2 = 0) @@ -356,8 +263,7 @@ static void sendXEmbedMessage(WId window, long message, XClientMessageEvent c; memset(&c, 0, sizeof(c)); c.type = ClientMessage; - - c.type = ATOM(_XEMBED); + c.message_type = ATOM(_XEMBED); c.format = 32; c.display = display; c.window = window; @@ -395,14 +301,6 @@ class X11EventFilter : public QAbstractNativeEventFilter } } static x11EventFilter; -// -struct functorData -{ - xcb_window_t id, rootWindow; - bool clearedWmState; - bool reparentedToRoot; -}; - class QX11EmbedContainerPrivate : public QWidgetPrivate { @@ -424,6 +322,7 @@ class QX11EmbedContainerPrivate : public QWidgetPrivate void rejectClient(WId window); void checkGrab(); + void checkXembedInfo(); WId topLevelParentWinId() const; @@ -482,6 +381,7 @@ QX11EmbedContainer::QX11EmbedContainer(QWidget *parent) // Install X11 event filter. QCoreApplication::instance()->installNativeEventFilter(&x11EventFilter); + QCoreApplication::instance()->installNativeEventFilter(this); XSelectInput(QX11Info::display(), internalWinId(), KeyPressMask | KeyReleaseMask @@ -500,6 +400,7 @@ QX11EmbedContainer::QX11EmbedContainer(QWidget *parent) // Move input to our focusProxy if this widget is active, and not // shaded by a modal dialog (in which case isActiveWindow() would // still return true, but where we must not move input focus). + if (qApp->activeWindow() == window() && !d->isEmbedded()) d->moveInputToProxy(); @@ -529,7 +430,7 @@ QX11EmbedContainer::Error QX11EmbedContainer::error() const { return d_func()->lastError; } -bool QX11EmbedContainer::nativeEvent(const QByteArray &eventType, void *message, long *result) +bool QX11EmbedContainer::nativeEventFilter(const QByteArray &eventType, void *message, long *result) { if (eventType == "xcb_generic_event_t") { return x11Event(message, result); @@ -539,6 +440,7 @@ bool QX11EmbedContainer::nativeEvent(const QByteArray &eventType, void *message, } + /*! \reimp */ void QX11EmbedContainer::paintEvent(QPaintEvent *) @@ -629,6 +531,10 @@ void QX11EmbedContainer::embedClient(WId id) default: break; } + +#ifdef QX11EMBED_DEBUG + qDebug() << "reparented client" << id << "into" << winId(); +#endif } /*! \internal @@ -776,21 +682,27 @@ bool QX11EmbedContainer::eventFilter(QObject *o, QEvent *event) Handles X11 events for the container. */ -bool QX11EmbedContainer::x11Event(void *e, long* result) +bool QX11EmbedContainer::x11Event(void *message, long*) { - xcb_generic_event_t* event = reinterpret_cast(e); + xcb_generic_event_t* e = reinterpret_cast(message); Q_D(QX11EmbedContainer); - switch (event->response_type & ~0x80) { + switch (e->response_type & ~0x80) { case XCB_CREATE_NOTIFY: +#ifdef QX11EMBED_DEBUG + qDebug() << "client created" << reinterpret_cast(e)->window; +#endif // The client created an embedded window. if (d->client) - d->rejectClient(reinterpret_cast(event)->window); + d->rejectClient(reinterpret_cast(e)->window); else - d->acceptClient(reinterpret_cast(event)->window); + d->acceptClient(reinterpret_cast(e)->window); break; case XCB_DESTROY_NOTIFY: - if (reinterpret_cast(event)->window == d->client) { + if (reinterpret_cast(e)->window == d->client) { +#ifdef QX11EMBED_DEBUG + qDebug() << "client died"; +#endif // The client died. d->client = 0; d->clientIsXEmbed = false; @@ -834,8 +746,8 @@ bool QX11EmbedContainer::x11Event(void *e, long* result) // is an XEmbed client. d->clientIsXEmbed = true; - Time msgtime = (Time) event->data.data32[0]; - //TODO + //TODO: Port to Qt5, if needed + //Time msgtime = (Time) event->data.data32[0]; //if (msgtime > X11->time) //X11->time = msgtime; @@ -903,21 +815,50 @@ bool QX11EmbedContainer::x11Event(void *e, long* result) } break; case XCB_BUTTON_PRESS: - if (!d->clientIsXEmbed) { + { + auto event = reinterpret_cast(e); + if (event->child == d->client && !d->clientIsXEmbed) { setFocus(Qt::MouseFocusReason); XAllowEvents(QX11Info::display(), ReplayPointer, CurrentTime); return true; } + } break; case XCB_BUTTON_RELEASE: if (!d->clientIsXEmbed) XAllowEvents(QX11Info::display(), SyncPointer, CurrentTime); break; + case XCB_PROPERTY_NOTIFY: + { + auto event = reinterpret_cast(e); + + if (event->atom == ATOM(_XEMBED_INFO) && event->window == d->client) { + if (auto info = get_xembed_info(d->client)) { + if (info->flags & XEMBED_MAPPED) { +#ifdef QX11EMBED_DEBUG + qDebug() << "mapping client per _xembed_info"; +#endif + XMapWindow(QX11Info::display(), d->client); + XRaiseWindow(QX11Info::display(), d->client); + } else { +#ifdef QX11EMBED_DEBUG + qDebug() << "unmapping client per _xembed_info"; +#endif + XUnmapWindow(QX11Info::display(), d->client); + } + + free(info); + } + } + break; + } + case XCB_CONFIGURE_NOTIFY: + return true; default: break; } - return QWidget::nativeEvent("xcb_generic_event_t", e, result); + return false; } /*! \internal @@ -931,38 +872,6 @@ void QX11EmbedContainer::resizeEvent(QResizeEvent *) XResizeWindow(QX11Info::display(), d->client, width(), height()); } -/*! \internal - - We use the QShowEvent to signal to our client that we want it to - map itself. We do this by changing its window property - XEMBED_INFO. The client will get an X11 PropertyNotify. -*/ -void QX11EmbedContainer::showEvent(QShowEvent *) -{ - Q_D(QX11EmbedContainer); - if (d->client) { - long data[] = {XEMBED_VERSION, XEMBED_MAPPED}; - XChangeProperty(QX11Info::display(), d->client, ATOM(_XEMBED_INFO), ATOM(_XEMBED_INFO), 32, - PropModeReplace, (unsigned char *) data, 2); - } -} - -/*! \internal - - We use the QHideEvent to signal to our client that we want it to - unmap itself. We do this by changing its window property - XEMBED_INFO. The client will get an X11 PropertyNotify. -*/ -void QX11EmbedContainer::hideEvent(QHideEvent *) -{ - Q_D(QX11EmbedContainer); - if (d->client) { - long data[] = {XEMBED_VERSION, XEMBED_MAPPED}; - XChangeProperty(QX11Info::display(), d->client, ATOM(_XEMBED_INFO), ATOM(_XEMBED_INFO), 32, - PropModeReplace, (unsigned char *) data, 2); - } -} - /*! \reimp */ @@ -1010,6 +919,8 @@ void QX11EmbedContainerPrivate::acceptClient(WId window) client = window; q->setEnabled(true); + XSelectInput(QX11Info::display(), client, PropertyChangeMask); + // This tells Qt that we wish to forward DnD messages to // our client. if (!extra) @@ -1018,12 +929,6 @@ void QX11EmbedContainerPrivate::acceptClient(WId window) //extraData()->xDndProxy = client; unsigned int version = XEmbedVersion(); - - Atom actual_type_return; - int actual_format_return; - unsigned long nitems_return = 0; - unsigned long bytes_after_return; - unsigned char *prop_return = 0; unsigned int clientversion = 0; // Add this client to our saveset, so if we crash, the client window @@ -1034,20 +939,10 @@ void QX11EmbedContainerPrivate::acceptClient(WId window) // XEmbed clients have an _XEMBED_INFO property in which we can // fetch the version - if (XGetWindowProperty(QX11Info::display(), client, ATOM(_XEMBED_INFO), 0, 2, false, - ATOM(_XEMBED_INFO), &actual_type_return, &actual_format_return, - &nitems_return, &bytes_after_return, &prop_return) == Success) { - - if (actual_type_return != None && actual_format_return != 0) { - // Clients with the _XEMBED_INFO property are XEMBED clients. - clientIsXEmbed = true; - - long *p = (long *)prop_return; - if (nitems_return >= 2) - clientversion = (unsigned int)p[0]; - } - - XFree(prop_return); + if (auto info = get_xembed_info(client)) { + clientIsXEmbed = true; + clientversion = info->version; + free(info); } // Store client window's original size and placement. @@ -1072,8 +967,7 @@ void QX11EmbedContainerPrivate::acceptClient(WId window) // supported version number and that of the client (from // _XEMBED_INFO property). unsigned int minversion = version > clientversion ? clientversion : version; - sendXEmbedMessage(client, XEMBED_EMBEDDED_NOTIFY, q->internalWinId(), minversion); - XMapWindow(QX11Info::display(), client); + sendXEmbedMessage(client, XEMBED_EMBEDDED_NOTIFY, 0, q->internalWinId(), minversion); // Resize it, but no smaller than its minimum size hint. XResizeWindow(QX11Info::display(), diff --git a/src/gui/X11EmbedContainer.h b/src/gui/X11EmbedContainer.h index 8e261098f30..d91258c28ee 100644 --- a/src/gui/X11EmbedContainer.h +++ b/src/gui/X11EmbedContainer.h @@ -48,10 +48,11 @@ #include #else +#include #include class QX11EmbedContainerPrivate; -class Q_GUI_EXPORT QX11EmbedContainer : public QWidget +class Q_GUI_EXPORT QX11EmbedContainer : public QWidget, public QAbstractNativeEventFilter { Q_OBJECT public: @@ -72,19 +73,18 @@ class Q_GUI_EXPORT QX11EmbedContainer : public QWidget }; Error error() const; +public: + bool nativeEventFilter(const QByteArray &eventType, void *message, long *result); + Q_SIGNALS: void clientIsEmbedded(); void clientClosed(); void error(QX11EmbedContainer::Error); protected: - //bool x11Event(XEvent *); - bool nativeEvent(const QByteArray &eventType, void *message, long *result); bool eventFilter(QObject *, QEvent *) override; void paintEvent(QPaintEvent *e) override; void resizeEvent(QResizeEvent *); - void showEvent(QShowEvent *); - void hideEvent(QHideEvent *); bool event(QEvent *); private: From f097be6c404eacd2f118a7aa9a7b2d54504b27fe Mon Sep 17 00:00:00 2001 From: Lukas W Date: Sat, 8 Apr 2017 17:14:22 +0200 Subject: [PATCH 14/53] RemoteVst: Fix flickering when moving window --- plugins/vst_base/RemoteVstPlugin.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/vst_base/RemoteVstPlugin.cpp b/plugins/vst_base/RemoteVstPlugin.cpp index c0d4b9b9237..15be00606c4 100644 --- a/plugins/vst_base/RemoteVstPlugin.cpp +++ b/plugins/vst_base/RemoteVstPlugin.cpp @@ -648,7 +648,7 @@ void RemoteVstPlugin::initEditor() wc.hInstance = hInst; wc.hIcon = LoadIcon( NULL, IDI_APPLICATION ); wc.hCursor = LoadCursor( NULL, IDC_ARROW ); - wc.hbrBackground = (HBRUSH) GetStockObject( BLACK_BRUSH ); + wc.hbrBackground = NULL; wc.lpszMenuName = NULL; wc.lpszClassName = "LVSL"; From 0e311ffd474d9ae23751dd3e004e5ff06212c20f Mon Sep 17 00:00:00 2001 From: Lukas W Date: Sat, 8 Apr 2017 23:14:23 +0200 Subject: [PATCH 15/53] X11Embed: Remove flag causing embedding to fail --- plugins/vst_base/VstPlugin.cpp | 6 +++--- src/gui/X11EmbedContainer.cpp | 4 ++-- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/plugins/vst_base/VstPlugin.cpp b/plugins/vst_base/VstPlugin.cpp index 83d82e75e58..514146c0982 100644 --- a/plugins/vst_base/VstPlugin.cpp +++ b/plugins/vst_base/VstPlugin.cpp @@ -229,7 +229,9 @@ void VstPlugin::showEditor( QWidget * _parent, bool isEffect ) return; } - QX11EmbedContainer * xe = new QX11EmbedContainer; + vstSubWin * sw = new vstSubWin( gui->mainWindow()->workspace() ); + + QX11EmbedContainer * xe = new QX11EmbedContainer(sw); m_pluginWidget = xe; m_pluginWidget->setFixedSize( m_pluginGeometry ); m_pluginWidget->setWindowTitle( name() ); @@ -238,8 +240,6 @@ void VstPlugin::showEditor( QWidget * _parent, bool isEffect ) if( _parent == NULL ) { - vstSubWin * sw = new vstSubWin( - gui->mainWindow()->workspace() ); sw->setWidget( m_pluginWidget ); if( isEffect ) diff --git a/src/gui/X11EmbedContainer.cpp b/src/gui/X11EmbedContainer.cpp index f5a22eb8736..99e0dc48dea 100644 --- a/src/gui/X11EmbedContainer.cpp +++ b/src/gui/X11EmbedContainer.cpp @@ -356,8 +356,8 @@ QX11EmbedContainer::QX11EmbedContainer(QWidget *parent) Q_D(QX11EmbedContainer); //XSetErrorHandler(x11ErrorHandler); + //setAttribute(Qt::WA_DontCreateNativeAncestors); setAttribute(Qt::WA_NativeWindow); - setAttribute(Qt::WA_DontCreateNativeAncestors); createWinId(); setFocusPolicy(Qt::StrongFocus); @@ -369,8 +369,8 @@ QX11EmbedContainer::QX11EmbedContainer(QWidget *parent) // Everybody gets a focus proxy, but only one toplevel container's // focus proxy is actually in use. d->focusProxy = new QWidget(this); + //d->focusProxy->setAttribute(Qt::WA_DontCreateNativeAncestors); d->focusProxy->setAttribute(Qt::WA_NativeWindow); - d->focusProxy->setAttribute(Qt::WA_DontCreateNativeAncestors); d->focusProxy->createWinId(); d->focusProxy->winId(); d->focusProxy->setGeometry(-1, -1, 1, 1); From 8fdcc6ccac153c33c5cb6bc0e16bcc9a6a8b39dc Mon Sep 17 00:00:00 2001 From: Lukas W Date: Sat, 8 Apr 2017 23:38:45 +0200 Subject: [PATCH 16/53] Fix compile on Windows --- plugins/vst_base/VstPlugin.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/vst_base/VstPlugin.h b/plugins/vst_base/VstPlugin.h index 6d2317c85bd..1b6d62ff715 100644 --- a/plugins/vst_base/VstPlugin.h +++ b/plugins/vst_base/VstPlugin.h @@ -130,7 +130,7 @@ public slots: QString m_plugin; QPointer m_pluginWidget; - WId m_pluginWindowID; + int m_pluginWindowID; QSize m_pluginGeometry; bool m_badDllFormat; From 7ce60c247a0bf325482991566a035f5ba2229380 Mon Sep 17 00:00:00 2001 From: Lukas W Date: Sat, 8 Apr 2017 23:56:07 +0200 Subject: [PATCH 17/53] Travis: Add missing packages --- .travis/linux..install.sh | 6 ++++-- src/gui/CMakeLists.txt | 2 +- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/.travis/linux..install.sh b/.travis/linux..install.sh index f5988a83d70..8174f978637 100644 --- a/.travis/linux..install.sh +++ b/.travis/linux..install.sh @@ -2,11 +2,13 @@ PACKAGES="cmake libsndfile-dev fftw3-dev libvorbis-dev libogg-dev libmp3lame-dev libasound2-dev libjack-dev libsdl-dev libsamplerate0-dev libstk0-dev - 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-util-dev libxcb-keysyms1-dev" + # Help with unmet dependencies -PACKAGES="$PACKAGES libjack0" +PACKAGES="$PACKAGES $VST_PACKAGES libjack0" if [ $QT5 ]; then PACKAGES="$PACKAGES qtbase5-dev qttools5-dev-tools qttools5-dev" diff --git a/src/gui/CMakeLists.txt b/src/gui/CMakeLists.txt index 58fb8e2dddd..3373945eddd 100644 --- a/src/gui/CMakeLists.txt +++ b/src/gui/CMakeLists.txt @@ -91,7 +91,7 @@ SET(LMMS_SRCS PARENT_SCOPE ) -IF(QT5) +IF(QT5 AND LMMS_BUILD_LINUX) add_library(x11embedcontainer STATIC X11EmbedContainer ) From dc4a3875a3a76f519cd5c4225bfe1244b1ae4d31 Mon Sep 17 00:00:00 2001 From: Lukas W Date: Sat, 15 Jul 2017 22:18:49 +0200 Subject: [PATCH 18/53] X11EmbedContainer: Don't grab mouse in acceptClient, more debugging msgs --- src/gui/X11EmbedContainer.cpp | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/src/gui/X11EmbedContainer.cpp b/src/gui/X11EmbedContainer.cpp index 99e0dc48dea..a14735cee21 100644 --- a/src/gui/X11EmbedContainer.cpp +++ b/src/gui/X11EmbedContainer.cpp @@ -274,6 +274,9 @@ static void sendXEmbedMessage(WId window, long message, c.data.l[3] = data1; c.data.l[4] = data2; +#ifdef QX11EMBED_DEBUG + qDebug() << "Sending XEMBED message" << message << detail << data1 << data2; +#endif XSendEvent(display, window, false, NoEventMask, (XEvent *) &c); } @@ -990,6 +993,9 @@ void QX11EmbedContainerPrivate::acceptClient(WId window) else sendXEmbedMessage(client, XEMBED_FOCUS_OUT); + // This is from the original Qt implementation. Disabled for now because it appears + // to cause the mouse being grabbed permanently in some environments + /* if (!clientIsXEmbed) { checkGrab(); if (q->hasFocus()) { @@ -999,6 +1005,7 @@ void QX11EmbedContainerPrivate::acceptClient(WId window) if (!isEmbedded()) moveInputToProxy(); } + */ emit q->clientIsEmbedded(); } From 000fe2da7be575aaa601e50f9a71e203b99e358e Mon Sep 17 00:00:00 2001 From: Lukas W Date: Sun, 16 Jul 2017 10:33:58 +0200 Subject: [PATCH 19/53] Travis: Fix xcb package name --- .travis/linux..install.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.travis/linux..install.sh b/.travis/linux..install.sh index 8174f978637..cf446fb8c76 100644 --- a/.travis/linux..install.sh +++ b/.travis/linux..install.sh @@ -5,7 +5,7 @@ PACKAGES="cmake libsndfile-dev fftw3-dev libvorbis-dev libogg-dev libmp3lame-dev libfluidsynth-dev portaudio19-dev g++-multilib libfltk1.3-dev libgig-dev libsoundio-dev" -VST_PACKAGES="wine-dev libqt5x11extras5-dev qtbase5-private-dev libxcb-util-dev libxcb-keysyms1-dev" +VST_PACKAGES="wine-dev libqt5x11extras5-dev qtbase5-private-dev libxcb-util0-dev libxcb-keysyms1-dev" # Help with unmet dependencies PACKAGES="$PACKAGES $VST_PACKAGES libjack0" From d9626e9e3b74c6fbe49552fb7ee673c96d1b23cc Mon Sep 17 00:00:00 2001 From: Lukas W Date: Wed, 19 Jul 2017 22:52:51 +0200 Subject: [PATCH 20/53] Move X11EmbedContainer.h to include --- {src/gui => include}/X11EmbedContainer.h | 0 src/CMakeLists.txt | 2 +- src/gui/CMakeLists.txt | 9 ++++++--- 3 files changed, 7 insertions(+), 4 deletions(-) rename {src/gui => include}/X11EmbedContainer.h (100%) diff --git a/src/gui/X11EmbedContainer.h b/include/X11EmbedContainer.h similarity index 100% rename from src/gui/X11EmbedContainer.h rename to include/X11EmbedContainer.h diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 154ff4f85ae..8523fc44b58 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -132,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/gui/CMakeLists.txt b/src/gui/CMakeLists.txt index 3373945eddd..345679297e0 100644 --- a/src/gui/CMakeLists.txt +++ b/src/gui/CMakeLists.txt @@ -92,8 +92,10 @@ SET(LMMS_SRCS ) IF(QT5 AND LMMS_BUILD_LINUX) + INCLUDE_DIRECTORIES("${CMAKE_SOURCE_DIR}/include/") add_library(x11embedcontainer STATIC - X11EmbedContainer + "${CMAKE_SOURCE_DIR}/src/gui/X11EmbedContainer.cpp" + "${CMAKE_SOURCE_DIR}/include/X11EmbedContainer.h" ) FIND_PACKAGE(Qt5Core COMPONENTS Private) FIND_PACKAGE(Qt5Widgets COMPONENTS Private) @@ -102,10 +104,11 @@ IF(QT5 AND LMMS_BUILD_LINUX) include_directories(${Qt5Core_PRIVATE_INCLUDE_DIRS}) include_directories(${Qt5Widgets_PRIVATE_INCLUDE_DIRS}) - target_link_Libraries(x11embedcontainer - xcb xcb-util X11-xcb Qt5::X11Extras + xcb xcb-util X11-xcb Qt5::X11Extras X11 ) + + SET(LMMS_REQUIRED_LIBS ${LMMS_REQUIRED_LIBS} x11embedcontainer PARENT_SCOPE) ELSE() add_library(x11embedcontainer STATIC /dev/null) ENDIF() From def2b7d5098ad7a75169ff5f86d38dd9c50b8224 Mon Sep 17 00:00:00 2001 From: Lukas W Date: Sat, 5 Aug 2017 12:33:59 +0200 Subject: [PATCH 21/53] Move X11EmbedContainer to submodule --- .gitmodules | 3 + include/X11EmbedContainer.h | 98 --- plugins/vst_base/CMakeLists.txt | 2 +- src/3rdparty/CMakeLists.txt | 4 + src/3rdparty/qt5-x11embed | 1 + src/CMakeLists.txt | 1 + src/gui/CMakeLists.txt | 22 - src/gui/X11EmbedContainer.cpp | 1076 ------------------------------- 8 files changed, 10 insertions(+), 1197 deletions(-) create mode 100644 .gitmodules delete mode 100644 include/X11EmbedContainer.h create mode 100644 src/3rdparty/CMakeLists.txt create mode 160000 src/3rdparty/qt5-x11embed delete mode 100644 src/gui/X11EmbedContainer.cpp diff --git a/.gitmodules b/.gitmodules new file mode 100644 index 00000000000..4ca030cf527 --- /dev/null +++ b/.gitmodules @@ -0,0 +1,3 @@ +[submodule "src/3rdparty/qt5-x11embed"] + path = src/3rdparty/qt5-x11embed + url = git@github.com:Lukas-W/qt5-x11embed.git diff --git a/include/X11EmbedContainer.h b/include/X11EmbedContainer.h deleted file mode 100644 index d91258c28ee..00000000000 --- a/include/X11EmbedContainer.h +++ /dev/null @@ -1,98 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2015 The Qt Company Ltd. -** Contact: http://www.qt.io/licensing/ -** -** This file is part of the QtGui module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see http://www.qt.io/terms-conditions. For further -** information use the contact form at http://www.qt.io/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 2.1 or version 3 as published by the Free -** Software Foundation and appearing in the file LICENSE.LGPLv21 and -** LICENSE.LGPLv3 included in the packaging of this file. Please review the -** following information to ensure the GNU Lesser General Public License -** requirements will be met: https://www.gnu.org/licenses/lgpl.html and -** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. -** -** As a special exception, The Qt Company gives you certain additional -** rights. These rights are described in The Qt Company LGPL Exception -** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 3.0 as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL included in the -** packaging of this file. Please review the following information to -** ensure the GNU General Public License version 3.0 requirements will be -** met: http://www.gnu.org/copyleft/gpl.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - -#ifndef X11EMBEDCONTAINER_H -#define X11EMBEDCONTAINER_H - -#include - -#if QT_VERSION < 0x050000 -#include -#else - -#include -#include - -class QX11EmbedContainerPrivate; -class Q_GUI_EXPORT QX11EmbedContainer : public QWidget, public QAbstractNativeEventFilter -{ - Q_OBJECT -public: - QX11EmbedContainer(QWidget *parent = 0); - ~QX11EmbedContainer(); - - void embedClient(WId id); - void discardClient(); - - WId clientWinId() const; - - QSize minimumSizeHint() const; - - enum Error { - Unknown, - Internal, - InvalidWindowID - }; - Error error() const; - -public: - bool nativeEventFilter(const QByteArray &eventType, void *message, long *result); - -Q_SIGNALS: - void clientIsEmbedded(); - void clientClosed(); - void error(QX11EmbedContainer::Error); - -protected: - bool eventFilter(QObject *, QEvent *) override; - void paintEvent(QPaintEvent *e) override; - void resizeEvent(QResizeEvent *); - bool event(QEvent *); - -private: - Q_DECLARE_PRIVATE(QX11EmbedContainer) - Q_DISABLE_COPY(QX11EmbedContainer) - bool x11Event(void *e, long *result); -}; - -#endif // X11EMBEDCONTAINER_H - -#endif diff --git a/plugins/vst_base/CMakeLists.txt b/plugins/vst_base/CMakeLists.txt index 0f5db8f5931..19915faa623 100644 --- a/plugins/vst_base/CMakeLists.txt +++ b/plugins/vst_base/CMakeLists.txt @@ -28,7 +28,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 x11embedcontainer) +TARGET_LINK_LIBRARIES(vstbase qx11embedcontainer) IF(LMMS_BUILD_LINUX AND NOT WANT_VST_NOWINE) diff --git a/src/3rdparty/CMakeLists.txt b/src/3rdparty/CMakeLists.txt new file mode 100644 index 00000000000..644fccbb199 --- /dev/null +++ b/src/3rdparty/CMakeLists.txt @@ -0,0 +1,4 @@ +include(ExternalProject) + +set(BUILD_SHARED_LIBS OFF) +add_subdirectory(qt5-x11embed) 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 8523fc44b58..82dd14d7a1e 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) diff --git a/src/gui/CMakeLists.txt b/src/gui/CMakeLists.txt index 345679297e0..5b4050bca70 100644 --- a/src/gui/CMakeLists.txt +++ b/src/gui/CMakeLists.txt @@ -91,28 +91,6 @@ SET(LMMS_SRCS PARENT_SCOPE ) -IF(QT5 AND LMMS_BUILD_LINUX) - INCLUDE_DIRECTORIES("${CMAKE_SOURCE_DIR}/include/") - add_library(x11embedcontainer STATIC - "${CMAKE_SOURCE_DIR}/src/gui/X11EmbedContainer.cpp" - "${CMAKE_SOURCE_DIR}/include/X11EmbedContainer.h" - ) - FIND_PACKAGE(Qt5Core COMPONENTS Private) - FIND_PACKAGE(Qt5Widgets COMPONENTS Private) - FIND_PACKAGE(Qt5X11Extras REQUIRED) - - include_directories(${Qt5Core_PRIVATE_INCLUDE_DIRS}) - include_directories(${Qt5Widgets_PRIVATE_INCLUDE_DIRS}) - - target_link_Libraries(x11embedcontainer - xcb xcb-util X11-xcb Qt5::X11Extras X11 - ) - - SET(LMMS_REQUIRED_LIBS ${LMMS_REQUIRED_LIBS} x11embedcontainer PARENT_SCOPE) -ELSE() - add_library(x11embedcontainer STATIC /dev/null) -ENDIF() - set(LMMS_UIS ${LMMS_UIS} gui/dialogs/about_dialog.ui diff --git a/src/gui/X11EmbedContainer.cpp b/src/gui/X11EmbedContainer.cpp deleted file mode 100644 index a14735cee21..00000000000 --- a/src/gui/X11EmbedContainer.cpp +++ /dev/null @@ -1,1076 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2015 The Qt Company Ltd. -** Copyright (C) 2017 Lukas W -** Contact: http://www.qt.io/licensing/ -** -** This file is part of the QtGui module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see http://www.qt.io/terms-conditions. For further -** information use the contact form at http://www.qt.io/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 2.1 or version 3 as published by the Free -** Software Foundation and appearing in the file LICENSE.LGPLv21 and -** LICENSE.LGPLv3 included in the packaging of this file. Please review the -** following information to ensure the GNU Lesser General Public License -** requirements will be met: https://www.gnu.org/licenses/lgpl.html and -** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. -** -** As a special exception, The Qt Company gives you certain additional -** rights. These rights are described in The Qt Company LGPL Exception -** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 3.0 as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL included in the -** packaging of this file. Please review the following information to -** ensure the GNU General Public License version 3.0 requirements will be -** met: http://www.gnu.org/copyleft/gpl.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - -#include "X11EmbedContainer.h" - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include - -#include -#include -#include - -#include -#include -#include -//#include - -#include -#include - -#define XK_MISCELLANY -#define XK_LATIN1 -#define None 0 -#include -#include -#include -#include -#include -#include -#include - -#ifndef XK_ISO_Left_Tab -#define XK_ISO_Left_Tab 0xFE20 -#endif - -//#define QX11EMBED_DEBUG -#ifdef QX11EMBED_DEBUG -#include -#endif - -#undef KeyPress -#undef KeyRelease -#undef FocusIn -#undef FocusOut - -static const int XRevertToParent = RevertToParent; -#undef RevertToParent - -QT_BEGIN_NAMESPACE - -enum ATOM_ID : int { - _XEMBED - ,_XEMBED_INFO - ,WM_PROTOCOLS - ,WM_DELETE_WINDOW - ,WM_STATE -}; - -static const std::vector> atom_list({ - {_XEMBED, "_XEMBED"}, - {_XEMBED_INFO, "_XEMBED_INFO"}, - {WM_PROTOCOLS, "WM_PROTOCOLS"}, - {WM_DELETE_WINDOW, "WM_DELETE_WINDOW"}, - {WM_STATE, "WM_STATE"}, -}); - -static QMap atoms; -static QMutex atoms_lock; - -void initAtoms() -{ - QMutexLocker locker(&atoms_lock); Q_UNUSED(locker); - - std::queue cookies; - - for (const auto& pair : atom_list) - { - cookies.push(xcb_intern_atom(QX11Info::connection(), false, pair.second.length(), pair.second.data())); - } - - for (const auto& pair : atom_list) - { - auto cookie = cookies.front(); - cookies.pop(); - - auto reply = xcb_intern_atom_reply(QX11Info::connection(), cookie, nullptr); - atoms[pair.first] = reply->atom; - - Q_ASSERT(pair.second == XGetAtomName(QX11Info::display(), reply->atom)); - -#ifdef QX11EMBED_DEBUG - qDebug() << "atom" << QString::fromStdString(pair.second) - << XGetAtomName(QX11Info::display(), reply->atom) << reply->atom; -#endif - free(reply); - } -} - -xcb_atom_t ATOM(int atomID) -{ - return atoms.value(atomID); -} - - -struct xembed_info -{ - uint32_t version; - uint32_t flags; -}; - -xembed_info* get_xembed_info(xcb_window_t window) -{ - auto cookie = xcb_get_property(QX11Info::connection(), 0, window, ATOM(_XEMBED_INFO), ATOM(_XEMBED_INFO), 0, 2); - if (auto reply = xcb_get_property_reply(QX11Info::connection(), cookie, nullptr)) { - auto val_len = xcb_get_property_value_length(reply); - if (val_len < 2) { -#ifdef QX11EMBED_DEBUG - qDebug() << "Client has malformed _XEMBED_INFO property, len is" << val_len; -#endif - free(reply); - return nullptr; - } - - void* result = malloc(sizeof(xembed_info)); - memcpy(result, xcb_get_property_value(reply), sizeof(xembed_info)); - return reinterpret_cast(result); - } - - return nullptr; -} - -// This is a hack to move topData() out from QWidgetPrivate to public. We -// need to to inspect window()'s embedded state. -class QHackWidget : public QWidget -{ - Q_DECLARE_PRIVATE(QWidget) -public: - QTLWExtra* topData() { return d_func()->topData(); } -}; - -static unsigned int XEMBED_VERSION = 0; - -enum QX11EmbedMessageType { - XEMBED_EMBEDDED_NOTIFY = 0, - XEMBED_WINDOW_ACTIVATE = 1, - XEMBED_WINDOW_DEACTIVATE = 2, - XEMBED_REQUEST_FOCUS = 3, - XEMBED_FOCUS_IN = 4, - XEMBED_FOCUS_OUT = 5, - XEMBED_FOCUS_NEXT = 6, - XEMBED_FOCUS_PREV = 7, - XEMBED_MODALITY_ON = 10, - XEMBED_MODALITY_OFF = 11, - XEMBED_REGISTER_ACCELERATOR = 12, - XEMBED_UNREGISTER_ACCELERATOR = 13, - XEMBED_ACTIVATE_ACCELERATOR = 14 -}; - -enum QX11EmbedFocusInDetail { - XEMBED_FOCUS_CURRENT = 0, - XEMBED_FOCUS_FIRST = 1, - XEMBED_FOCUS_LAST = 2 -}; - -enum QX11EmbedFocusInFlags { - XEMBED_FOCUS_OTHER = (0 << 0), - XEMBED_FOCUS_WRAPAROUND = (1 << 0) -}; - -enum QX11EmbedInfoFlags { - XEMBED_MAPPED = (1 << 0) -}; - -enum QX11EmbedAccelModifiers { - XEMBED_MODIFIER_SHIFT = (1 << 0), - XEMBED_MODIFIER_CONTROL = (1 << 1), - XEMBED_MODIFIER_ALT = (1 << 2), - XEMBED_MODIFIER_SUPER = (1 << 3), - XEMBED_MODIFIER_HYPER = (1 << 4) -}; - -enum QX11EmbedAccelFlags { - XEMBED_ACCELERATOR_OVERLOADED = (1 << 0) -}; - -// Silence the default X11 error handler. -/*static int x11ErrorHandler(Display *, xcb_generic_error_t *) -{ - return 0; -}*/ - -// Returns the X11 timestamp. Maintained mainly by qapplication -// internals, but also updated by the XEmbed widgets. -static xcb_timestamp_t x11Time() -{ - return QX11Info::getTimestamp(); -} - -// Gives the version and flags of the supported XEmbed protocol. -static unsigned int XEmbedVersion() -{ - return 0; -} - - -// Sends an XEmbed message. -static void sendXEmbedMessage(WId window, long message, - long detail = 0, long data1 = 0, long data2 = 0) -{ - auto display = QX11Info::display(); - - XClientMessageEvent c; - memset(&c, 0, sizeof(c)); - c.type = ClientMessage; - c.message_type = ATOM(_XEMBED); - c.format = 32; - c.display = display; - c.window = window; - - c.data.l[0] = x11Time(); - c.data.l[1] = message; - c.data.l[2] = detail; - c.data.l[3] = data1; - c.data.l[4] = data2; - -#ifdef QX11EMBED_DEBUG - qDebug() << "Sending XEMBED message" << message << detail << data1 << data2; -#endif - XSendEvent(display, window, false, NoEventMask, (XEvent *) &c); -} - -// From qapplication_x11.cpp -static xcb_key_press_event_t lastKeyEvent; - -// The purpose of this global x11 filter is for one to capture the key -// events in their original state, but most importantly this is the -// only way to get the WM_TAKE_FOCUS message from WM_PROTOCOLS. -class X11EventFilter : public QAbstractNativeEventFilter -{ -public: - bool nativeEventFilter(const QByteArray &eventType, void *message, long *result) - { - if (eventType != "xcb_generic_event_t") { - return false; - } - - xcb_generic_event_t *event = reinterpret_cast(message); - if (event->response_type == XCB_KEY_PRESS || event->response_type == XCB_KEY_RELEASE) { - lastKeyEvent = *reinterpret_cast(message); - } - - return false; - } -} static x11EventFilter; - - -class QX11EmbedContainerPrivate : public QWidgetPrivate -{ - Q_DECLARE_PUBLIC(QX11EmbedContainer) -public: - inline QX11EmbedContainerPrivate() - { - lastError = QX11EmbedContainer::Unknown; - client = 0; - focusProxy = 0; - clientIsXEmbed = false; - xgrab = false; - } - - bool isEmbedded() const; - void moveInputToProxy(); - - void acceptClient(WId window); - void rejectClient(WId window); - - void checkGrab(); - void checkXembedInfo(); - - WId topLevelParentWinId() const; - - void emitError(QX11EmbedContainer::Error error) { - Q_Q(QX11EmbedContainer); - lastError = error; - emit q->error(error); - } - - WId client; - QWidget *focusProxy; - bool clientIsXEmbed; - bool xgrab; - QRect clientOriginalRect; - QSize wmMinimumSizeHint; - - QX11EmbedContainer::Error lastError; - - static QX11EmbedContainer *activeContainer; -}; - -QX11EmbedContainer *QX11EmbedContainerPrivate::activeContainer = 0; - -/*! - Creates a QX11EmbedContainer object with the given \a parent. -*/ -QX11EmbedContainer::QX11EmbedContainer(QWidget *parent) - : QWidget(*new QX11EmbedContainerPrivate, parent, 0) -{ - initAtoms(); - Q_D(QX11EmbedContainer); - //XSetErrorHandler(x11ErrorHandler); - - //setAttribute(Qt::WA_DontCreateNativeAncestors); - setAttribute(Qt::WA_NativeWindow); - createWinId(); - - setFocusPolicy(Qt::StrongFocus); - setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding); - // ### PORT setKeyCompression(false); - setAcceptDrops(true); - setEnabled(false); - - // Everybody gets a focus proxy, but only one toplevel container's - // focus proxy is actually in use. - d->focusProxy = new QWidget(this); - //d->focusProxy->setAttribute(Qt::WA_DontCreateNativeAncestors); - d->focusProxy->setAttribute(Qt::WA_NativeWindow); - d->focusProxy->createWinId(); - d->focusProxy->winId(); - d->focusProxy->setGeometry(-1, -1, 1, 1); - - // We need events from the window (activation status) and - // from qApp (keypress/release). - qApp->installEventFilter(this); - - // Install X11 event filter. - QCoreApplication::instance()->installNativeEventFilter(&x11EventFilter); - QCoreApplication::instance()->installNativeEventFilter(this); - - XSelectInput(QX11Info::display(), internalWinId(), - KeyPressMask | KeyReleaseMask - | ButtonPressMask | ButtonReleaseMask | ButtonMotionMask - | KeymapStateMask - | PointerMotionMask - | EnterWindowMask | LeaveWindowMask - | FocusChangeMask - | ExposureMask - | StructureNotifyMask - | SubstructureNotifyMask); - - // Make sure our new event mask takes effect as soon as possible. - XFlush(QX11Info::display()); - - // Move input to our focusProxy if this widget is active, and not - // shaded by a modal dialog (in which case isActiveWindow() would - // still return true, but where we must not move input focus). - - if (qApp->activeWindow() == window() && !d->isEmbedded()) - d->moveInputToProxy(); - -#ifdef QX11EMBED_DEBUG - qDebug() << "QX11EmbedContainer::QX11EmbedContainer: constructed container" - << (void *)this << "with winId" << winId(); -#endif -} - -/*! - Destructs a QX11EmbedContainer. -*/ -QX11EmbedContainer::~QX11EmbedContainer() -{ - Q_D(QX11EmbedContainer); - if (d->client) { - XUnmapWindow(QX11Info::display(), d->client); - XReparentWindow(QX11Info::display(), d->client, QX11Info::appRootWindow(QX11Info::appScreen()), 0, 0); - } - - if (d->xgrab) - XUngrabButton(QX11Info::display(), AnyButton, AnyModifier, internalWinId()); -} - - -QX11EmbedContainer::Error QX11EmbedContainer::error() const { - return d_func()->lastError; -} - -bool QX11EmbedContainer::nativeEventFilter(const QByteArray &eventType, void *message, long *result) -{ - if (eventType == "xcb_generic_event_t") { - return x11Event(message, result); - } else { - return false; - } -} - - - -/*! \reimp -*/ -void QX11EmbedContainer::paintEvent(QPaintEvent *) -{ -} - -/*! \internal - - Returns whether or not the windows' embedded flag is set. -*/ -bool QX11EmbedContainerPrivate::isEmbedded() const -{ - Q_Q(const QX11EmbedContainer); - return ((QHackWidget *)q->window())->topData()->embedded == 1; -} - -/*! \internal - - Returns the parentWinId of the window. -*/ -WId QX11EmbedContainerPrivate::topLevelParentWinId() const -{ - Q_Q(const QX11EmbedContainer); - return q->window()->effectiveWinId(); - //TODO - //return ((QHackWidget *)q->window())->topData()->parentWinId; -} - -/*! - If the container has an embedded widget, this function returns - the X11 window ID of the client; otherwise it returns 0. -*/ -WId QX11EmbedContainer::clientWinId() const -{ - Q_D(const QX11EmbedContainer); - return d->client; -} - -/*! - Instructs the container to embed the X11 window with window ID \a - id. The client widget will then move on top of the container - window and be resized to fit into the container. - - The \a id should be the ID of a window controlled by an XEmbed - enabled application, but this is not mandatory. If \a id does not - belong to an XEmbed client widget, then focus handling, - activation, accelerators and other features will not work - properly. -*/ -void QX11EmbedContainer::embedClient(WId id) -{ - Q_D(QX11EmbedContainer); - - if (id == 0) { - d->emitError(InvalidWindowID); - return; - } - - // Walk up the tree of parent windows to prevent embedding of ancestors. - WId thisId = internalWinId(); - xcb_window_t rootReturn; - xcb_window_t parentReturn; - do { - auto cookie = xcb_query_tree(QX11Info::connection(), thisId); - xcb_generic_error_t* error = nullptr; - auto reply = xcb_query_tree_reply(QX11Info::connection(), cookie, &error); - - if (error) { - d->emitError(InvalidWindowID); - return; - } - - rootReturn = reply->root; - parentReturn = reply->parent; - - thisId = parentReturn; - if (id == thisId) { - d->emitError(InvalidWindowID); - return; - } - } while (thisId != rootReturn); - - switch (XReparentWindow(QX11Info::display(), id, internalWinId(), 0, 0)) { - case BadWindow: - case BadMatch: - d->emitError(InvalidWindowID); - break; - default: - break; - } - -#ifdef QX11EMBED_DEBUG - qDebug() << "reparented client" << id << "into" << winId(); -#endif -} - -/*! \internal - - Handles key, activation and focus events for the container. -*/ -bool QX11EmbedContainer::eventFilter(QObject *o, QEvent *event) -{ - Q_D(QX11EmbedContainer); - switch (event->type()) { - case QEvent::KeyPress: - // Forward any keypresses to our client. - if (o == this && d->client) { - lastKeyEvent.event = d->client; - xcb_send_event(QX11Info::connection(), false, d->client, KeyPressMask, (char*) &lastKeyEvent); - return true; - } - break; - case QEvent::KeyRelease: - // Forward any keyreleases to our client. - if (o == this && d->client) { - lastKeyEvent.event = d->client; - xcb_send_event(QX11Info::connection(), false, d->client, KeyReleaseMask, (char*) &lastKeyEvent); - return true; - } - break; - - case QEvent::WindowActivate: - // When our container window is activated, we pass the - // activation message on to our client. Note that X input - // focus is set to our focus proxy. We want to intercept all - // keypresses. - if (o == window() && d->client) { - if (d->clientIsXEmbed) { - sendXEmbedMessage(d->client, XEMBED_WINDOW_ACTIVATE); - } else { - d->checkGrab(); - if (hasFocus()) - XSetInputFocus(QX11Info::display(), d->client, XRevertToParent, x11Time()); - } - if (!d->isEmbedded()) - d->moveInputToProxy(); - } - break; - case QEvent::WindowDeactivate: - // When our container window is deactivated, we pass the - // deactivation message to our client. - if (o == window() && d->client) { - if (d->clientIsXEmbed) - sendXEmbedMessage(d->client, XEMBED_WINDOW_DEACTIVATE); - else - d->checkGrab(); - } - break; - case QEvent::FocusIn: - // When receiving FocusIn events generated by Tab or Backtab, - // we pass focus on to our client. Any mouse activity is sent - // directly to the client, and it will ask us for focus with - // XEMBED_REQUEST_FOCUS. - if (o == this && d->client) { - if (!d->isEmbedded()) - d->activeContainer = this; - - if (d->clientIsXEmbed) { - if (!d->isEmbedded()) - d->moveInputToProxy(); - - QFocusEvent *fe = (QFocusEvent *)event; - switch (fe->reason()) { - case Qt::TabFocusReason: - sendXEmbedMessage(d->client, XEMBED_FOCUS_IN, XEMBED_FOCUS_FIRST); - break; - case Qt::BacktabFocusReason: - sendXEmbedMessage(d->client, XEMBED_FOCUS_IN, XEMBED_FOCUS_LAST); - break; - default: - sendXEmbedMessage(d->client, XEMBED_FOCUS_IN, XEMBED_FOCUS_CURRENT); - break; - } - } else { - d->checkGrab(); - XSetInputFocus(QX11Info::display(), d->client, XRevertToParent, x11Time()); - } - } - - break; - case QEvent::FocusOut: { - // When receiving a FocusOut, we ask our client to remove its - // focus. - if (o == this && d->client) { - if (!d->isEmbedded()) { - d->activeContainer = 0; - if (isActiveWindow()) - d->moveInputToProxy(); - } - - if (d->clientIsXEmbed) { - QFocusEvent *fe = (QFocusEvent *)event; - if (o == this && d->client && fe->reason() != Qt::ActiveWindowFocusReason) - sendXEmbedMessage(d->client, XEMBED_FOCUS_OUT); - } else { - d->checkGrab(); - } - } - } - break; - - case QEvent::Close: { - if (o == this && d->client) { - // Unmap the client and reparent it to the root window. - // Wait until the messages have been processed. Then ask - // the window manager to delete the window. - XUnmapWindow(QX11Info::display(), d->client); - XReparentWindow(QX11Info::display(), d->client, QX11Info::appRootWindow(QX11Info::appScreen()), 0, 0); - XSync(QX11Info::display(), false); - - XEvent ev; - memset(&ev, 0, sizeof(ev)); - ev.xclient.type = ClientMessage; - ev.xclient.window = d->client; - ev.xclient.message_type = ATOM(WM_PROTOCOLS); - ev.xclient.format = 32; - ev.xclient.data.s[0] = ATOM(WM_DELETE_WINDOW); - XSendEvent(QX11Info::display(), d->client, false, NoEventMask, &ev); - - XFlush(QX11Info::display()); - d->client = 0; - d->clientIsXEmbed = false; - d->wmMinimumSizeHint = QSize(); - updateGeometry(); - setEnabled(false); - update(); - - emit clientClosed(); - } - } - default: - break; - } - - return QWidget::eventFilter(o, event); -} - -/*! \internal - - Handles X11 events for the container. -*/ -bool QX11EmbedContainer::x11Event(void *message, long*) -{ - xcb_generic_event_t* e = reinterpret_cast(message); - Q_D(QX11EmbedContainer); - - switch (e->response_type & ~0x80) { - case XCB_CREATE_NOTIFY: -#ifdef QX11EMBED_DEBUG - qDebug() << "client created" << reinterpret_cast(e)->window; -#endif - // The client created an embedded window. - if (d->client) - d->rejectClient(reinterpret_cast(e)->window); - else - d->acceptClient(reinterpret_cast(e)->window); - break; - case XCB_DESTROY_NOTIFY: - if (reinterpret_cast(e)->window == d->client) { -#ifdef QX11EMBED_DEBUG - qDebug() << "client died"; -#endif - // The client died. - d->client = 0; - d->clientIsXEmbed = false; - d->wmMinimumSizeHint = QSize(); - updateGeometry(); - update(); - setEnabled(false); - emit clientClosed(); - } - break; - case XCB_REPARENT_NOTIFY: - // The client sends us this if it reparents itself out of our - // widget. - { - auto* event = reinterpret_cast(e); - if (event->window == d->client && event->parent != internalWinId()) { - d->client = 0; - d->clientIsXEmbed = false; - d->wmMinimumSizeHint = QSize(); - updateGeometry(); - update(); - setEnabled(false); - emit clientClosed(); - } else if (event->parent == internalWinId()) { - // The client reparented itself into this window. - if (d->client) - d->rejectClient(event->window); - else - d->acceptClient(event->window); - } - break; - } - case XCB_CLIENT_MESSAGE: { - auto* event = reinterpret_cast(e); - if (event->type == ATOM(_XEMBED)) { - // Ignore XEMBED messages not to ourselves - if (event->window != internalWinId()) - break; - - // Receiving an XEmbed message means the client - // is an XEmbed client. - d->clientIsXEmbed = true; - - //TODO: Port to Qt5, if needed - //Time msgtime = (Time) event->data.data32[0]; - //if (msgtime > X11->time) - //X11->time = msgtime; - - switch (event->data.data32[1]) { - case XEMBED_REQUEST_FOCUS: { - // This typically happens when the client gets focus - // because of a mouse click. - if (!hasFocus()) - setFocus(Qt::OtherFocusReason); - - // The message is passed along to the topmost container - // that eventually responds with a XEMBED_FOCUS_IN - // message. The focus in message is passed all the way - // back until it reaches the original focus - // requestor. In the end, not only the original client - // has focus, but also all its ancestor containers. - if (d->isEmbedded()) { - // If our window's embedded flag is set, then - // that suggests that we are part of a client. The - // parentWinId will then point to an container to whom - // we must pass this message. - sendXEmbedMessage(d->topLevelParentWinId(), XEMBED_REQUEST_FOCUS); - } else { - // Our window's embedded flag is not set, - // so we are the topmost container. We respond to - // the focus request message with a focus in - // message. This message will pass on from client - // to container to client until it reaches the - // originator of the XEMBED_REQUEST_FOCUS message. - sendXEmbedMessage(d->client, XEMBED_FOCUS_IN, XEMBED_FOCUS_CURRENT); - } - - break; - } - case XEMBED_FOCUS_NEXT: - // Client sends this event when it received a tab - // forward and was at the end of its focus chain. If - // we are the only widget in the focus chain, we send - // ourselves a FocusIn event. - if (d->focus_next != this) { - focusNextPrevChild(true); - } else { - QFocusEvent event(QEvent::FocusIn, Qt::TabFocusReason); - qApp->sendEvent(this, &event); - } - - break; - case XEMBED_FOCUS_PREV: - // Client sends this event when it received a backtab - // and was at the start of its focus chain. If we are - // the only widget in the focus chain, we send - // ourselves a FocusIn event. - if (d->focus_next != this) { - focusNextPrevChild(false); - } else { - QFocusEvent event(QEvent::FocusIn, Qt::BacktabFocusReason); - qApp->sendEvent(this, &event); - } - - break; - default: - break; - } - } - } - break; - case XCB_BUTTON_PRESS: - { - auto event = reinterpret_cast(e); - if (event->child == d->client && !d->clientIsXEmbed) { - setFocus(Qt::MouseFocusReason); - XAllowEvents(QX11Info::display(), ReplayPointer, CurrentTime); - return true; - } - } - break; - case XCB_BUTTON_RELEASE: - if (!d->clientIsXEmbed) - XAllowEvents(QX11Info::display(), SyncPointer, CurrentTime); - break; - case XCB_PROPERTY_NOTIFY: - { - auto event = reinterpret_cast(e); - - if (event->atom == ATOM(_XEMBED_INFO) && event->window == d->client) { - if (auto info = get_xembed_info(d->client)) { - if (info->flags & XEMBED_MAPPED) { -#ifdef QX11EMBED_DEBUG - qDebug() << "mapping client per _xembed_info"; -#endif - XMapWindow(QX11Info::display(), d->client); - XRaiseWindow(QX11Info::display(), d->client); - } else { -#ifdef QX11EMBED_DEBUG - qDebug() << "unmapping client per _xembed_info"; -#endif - XUnmapWindow(QX11Info::display(), d->client); - } - - free(info); - } - } - break; - } - case XCB_CONFIGURE_NOTIFY: - return true; - default: - break; - } - - return false; -} - -/*! \internal - - Whenever the container is resized, we need to resize our client. -*/ -void QX11EmbedContainer::resizeEvent(QResizeEvent *) -{ - Q_D(QX11EmbedContainer); - if (d->client) - XResizeWindow(QX11Info::display(), d->client, width(), height()); -} - -/*! - \reimp -*/ -bool QX11EmbedContainer::event(QEvent *event) -{ - if (event->type() == QEvent::ParentChange) { - XSelectInput(QX11Info::display(), internalWinId(), - KeyPressMask | KeyReleaseMask - | ButtonPressMask | ButtonReleaseMask | ButtonMotionMask - | KeymapStateMask - | PointerMotionMask - | EnterWindowMask | LeaveWindowMask - | FocusChangeMask - | ExposureMask - | StructureNotifyMask - | SubstructureNotifyMask); - } - return QWidget::event(event); -} - -/*! \internal - - Rejects a client window by reparenting it to the root window. The - client will receive a reparentnotify, and will most likely assume - that the container has shut down. The XEmbed protocol does not - define any way to reject a client window, but this is a clean way - to do it. -*/ -void QX11EmbedContainerPrivate::rejectClient(WId window) -{ - Q_Q(QX11EmbedContainer); - q->setEnabled(false); - XRemoveFromSaveSet(QX11Info::display(), client); - XReparentWindow(QX11Info::display(), window, QX11Info::appRootWindow(QX11Info::appScreen()), 0, 0); -} - -/*! \internal - - Accepts a client by mapping it, resizing it and optionally - activating and giving it logical focusing through XEMBED messages. -*/ -void QX11EmbedContainerPrivate::acceptClient(WId window) -{ - Q_Q(QX11EmbedContainer); - client = window; - q->setEnabled(true); - - XSelectInput(QX11Info::display(), client, PropertyChangeMask); - - // This tells Qt that we wish to forward DnD messages to - // our client. - if (!extra) - createExtra(); - //TODO - //extraData()->xDndProxy = client; - - unsigned int version = XEmbedVersion(); - unsigned int clientversion = 0; - - // Add this client to our saveset, so if we crash, the client window - // doesn't get destroyed. This is useful for containers that restart - // automatically after a crash, because it can simply reembed its clients - // without having to restart them (KDE panel). - XAddToSaveSet(QX11Info::display(), client); - - // XEmbed clients have an _XEMBED_INFO property in which we can - // fetch the version - if (auto info = get_xembed_info(client)) { - clientIsXEmbed = true; - clientversion = info->version; - free(info); - } - - // Store client window's original size and placement. - Window root; - int x_return, y_return; - unsigned int width_return, height_return, border_width_return, depth_return; - XGetGeometry(QX11Info::display(), client, &root, &x_return, &y_return, - &width_return, &height_return, &border_width_return, &depth_return); - clientOriginalRect.setCoords(x_return, y_return, - x_return + width_return - 1, - y_return + height_return - 1); - - // Ask the client for its minimum size. - XSizeHints size; - long msize; - if (XGetWMNormalHints(QX11Info::display(), client, &size, &msize) && (size.flags & PMinSize)) { - wmMinimumSizeHint = QSize(size.min_width, size.min_height); - q->updateGeometry(); - } - - // The container should set the data2 field to the lowest of its - // supported version number and that of the client (from - // _XEMBED_INFO property). - unsigned int minversion = version > clientversion ? clientversion : version; - sendXEmbedMessage(client, XEMBED_EMBEDDED_NOTIFY, 0, q->internalWinId(), minversion); - - // Resize it, but no smaller than its minimum size hint. - XResizeWindow(QX11Info::display(), - client, - qMax(q->width(), wmMinimumSizeHint.width()), - qMax(q->height(), wmMinimumSizeHint.height())); - q->update(); - - // Not mentioned in the protocol is that if the container - // is already active, the client must be activated to work - // properly. - if (q->window()->isActiveWindow()) - sendXEmbedMessage(client, XEMBED_WINDOW_ACTIVATE); - - // Also, if the container already has focus, then it must - // send a focus in message to its new client; otherwise we ask - // it to remove focus. - if (q->focusWidget() == q && q->hasFocus()) - sendXEmbedMessage(client, XEMBED_FOCUS_IN, XEMBED_FOCUS_FIRST); - else - sendXEmbedMessage(client, XEMBED_FOCUS_OUT); - - // This is from the original Qt implementation. Disabled for now because it appears - // to cause the mouse being grabbed permanently in some environments - /* - if (!clientIsXEmbed) { - checkGrab(); - if (q->hasFocus()) { - XSetInputFocus(QX11Info::display(), client, XRevertToParent, x11Time()); - } - } else { - if (!isEmbedded()) - moveInputToProxy(); - } - */ - - emit q->clientIsEmbedded(); -} - -/*! \internal - - Moves X11 keyboard input focus to the focusProxy, unless the focus - is there already. When X11 keyboard input focus is on the - focusProxy, which is a child of the container and a sibling of the - client, X11 keypresses and keyreleases will always go to the proxy - and not to the client. -*/ -void QX11EmbedContainerPrivate::moveInputToProxy() -{ - Q_Q(QX11EmbedContainer); - // Following Owen Taylor's advice from the XEmbed specification to - // always use CurrentTime when no explicit user action is involved. - XSetInputFocus(QX11Info::display(), focusProxy->internalWinId(), XRevertToParent, CurrentTime); -} - -/*! \internal - - Ask the window manager to give us a default minimum size. -*/ -QSize QX11EmbedContainer::minimumSizeHint() const -{ - Q_D(const QX11EmbedContainer); - if (!d->client || !d->wmMinimumSizeHint.isValid()) - return QWidget::minimumSizeHint(); - return d->wmMinimumSizeHint; -} - -/*! \internal - -*/ -void QX11EmbedContainerPrivate::checkGrab() -{ - Q_Q(QX11EmbedContainer); - if (!clientIsXEmbed && q->isActiveWindow() && !q->hasFocus()) { - if (!xgrab) { - XGrabButton(QX11Info::display(), AnyButton, AnyModifier, q->internalWinId(), - true, ButtonPressMask, GrabModeSync, GrabModeAsync, - None, None); - } - xgrab = true; - } else { - if (xgrab) - XUngrabButton(QX11Info::display(), AnyButton, AnyModifier, q->internalWinId()); - xgrab = false; - } -} - -/*! - Detaches the client from the embedder. The client will appear as a - standalone window on the desktop. -*/ -void QX11EmbedContainer::discardClient() -{ - Q_D(QX11EmbedContainer); - if (d->client) { - XResizeWindow(QX11Info::display(), d->client, d->clientOriginalRect.width(), - d->clientOriginalRect.height()); - - d->rejectClient(d->client); - } -} - -QT_END_NAMESPACE From 0d3aaf667b6623b57884eec07c2a51433fc1a6df Mon Sep 17 00:00:00 2001 From: Lukas W Date: Sat, 5 Aug 2017 12:42:13 +0200 Subject: [PATCH 22/53] .gitmodules: Use https URL --- .gitmodules | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.gitmodules b/.gitmodules index 4ca030cf527..a81bc81747b 100644 --- a/.gitmodules +++ b/.gitmodules @@ -1,3 +1,3 @@ [submodule "src/3rdparty/qt5-x11embed"] path = src/3rdparty/qt5-x11embed - url = git@github.com:Lukas-W/qt5-x11embed.git + url = https://github.com/Lukas-W/qt5-x11embed.git From bd33475534cc85053450156132283e07b14caae9 Mon Sep 17 00:00:00 2001 From: Lukas W Date: Sat, 5 Aug 2017 13:15:15 +0200 Subject: [PATCH 23/53] Fix x11embed for non qt5+linux --- src/3rdparty/CMakeLists.txt | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/src/3rdparty/CMakeLists.txt b/src/3rdparty/CMakeLists.txt index 644fccbb199..f1d7a9ba1a5 100644 --- a/src/3rdparty/CMakeLists.txt +++ b/src/3rdparty/CMakeLists.txt @@ -1,4 +1,8 @@ include(ExternalProject) -set(BUILD_SHARED_LIBS OFF) -add_subdirectory(qt5-x11embed) +IF(QT5 AND LMMS_BUILD_LINUX) + set(BUILD_SHARED_LIBS OFF) + add_subdirectory(qt5-x11embed) +ELSE() + add_library(qx11embedcontainer STATIC /dev/null) +ENDIF() From ddab5346567789a578b12d6f7abedc57dad3acae Mon Sep 17 00:00:00 2001 From: Lukas W Date: Fri, 1 Sep 2017 14:18:55 +0200 Subject: [PATCH 24/53] Allow switching VST embed method at compile time --- plugins/CMakeLists.txt | 7 +++++++ plugins/vestige/CMakeLists.txt | 7 +++++++ plugins/vst_base/CMakeLists.txt | 12 ++++++++++++ 3 files changed, 26 insertions(+) diff --git a/plugins/CMakeLists.txt b/plugins/CMakeLists.txt index 8a464e3886d..b77f3e5f23f 100644 --- a/plugins/CMakeLists.txt +++ b/plugins/CMakeLists.txt @@ -35,6 +35,13 @@ 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) +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) + +IF(NOT LMMS_EMBED_VST OR NOT LMMS_BUILD_LINUX) + SET(LMMS_EMBED_VST_X11 OFF) +ENDIF() + IF("${PLUGIN_LIST}" STREQUAL "") SET(PLUGIN_LIST ${MINIMAL_LIST} 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/vst_base/CMakeLists.txt b/plugins/vst_base/CMakeLists.txt index bf98abfa0c4..8bfdced4804 100644 --- a/plugins/vst_base/CMakeLists.txt +++ b/plugins/vst_base/CMakeLists.txt @@ -2,6 +2,16 @@ IF(LMMS_SUPPORT_VST) INCLUDE(BuildPlugin) +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() + +ADD_DEFINITIONS(${EMBED_FLAGS}) + + IF(LMMS_BUILD_WIN32) ADD_DEFINITIONS(-DPTW32_STATIC_LIB) ADD_EXECUTABLE(RemoteVstPlugin "${CMAKE_CURRENT_SOURCE_DIR}/RemoteVstPlugin.cpp") @@ -49,6 +59,7 @@ ENDIF(LMMS_HOST_X86_64) SET(WINE_CXX_FLAGS "" CACHE STRING "Extra flags passed to wineg++") 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} @@ -62,6 +73,7 @@ ADD_CUSTOM_COMMAND( -std=c++0x -mwindows -lpthread ${EXTRA_FLAGS} -fno-omit-frame-pointer ${WINE_BUILD_FLAGS} + ${EMBED_FLAGS} -o ../RemoteVstPlugin COMMAND sh -c "mv ../RemoteVstPlugin.exe ../RemoteVstPlugin || true" TARGET vstbase From e7d06759b50f78a5b7dc96801e42760b22270e4d Mon Sep 17 00:00:00 2001 From: Lukas W Date: Fri, 1 Sep 2017 14:26:46 +0200 Subject: [PATCH 25/53] Fix Qt4 VST embed option --- plugins/CMakeLists.txt | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/plugins/CMakeLists.txt b/plugins/CMakeLists.txt index b77f3e5f23f..e010b11e882 100644 --- a/plugins/CMakeLists.txt +++ b/plugins/CMakeLists.txt @@ -36,12 +36,21 @@ IF(LMMS_MINIMAL) ENDIF() OPTION(LMMS_EMBED_VST "Turn on to embed VSTs as subwindows. Turn off to open VSTs in separate windows." ON) -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) + +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("${PLUGIN_LIST}" STREQUAL "") SET(PLUGIN_LIST ${MINIMAL_LIST} From ffa1b63f0538ffd86365f02a4cf022ae276234e4 Mon Sep 17 00:00:00 2001 From: Lukas W Date: Fri, 22 Sep 2017 12:06:19 +0200 Subject: [PATCH 26/53] Fix VST effects embedding --- plugins/CMakeLists.txt | 7 +++++++ plugins/VstEffect/CMakeLists.txt | 2 ++ plugins/VstEffect/VstEffectControlDialog.cpp | 2 -- plugins/vst_base/CMakeLists.txt | 8 -------- 4 files changed, 9 insertions(+), 10 deletions(-) diff --git a/plugins/CMakeLists.txt b/plugins/CMakeLists.txt index 6a60d73cf0f..dfd8dfbe4ac 100644 --- a/plugins/CMakeLists.txt +++ b/plugins/CMakeLists.txt @@ -50,6 +50,13 @@ 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 diff --git a/plugins/VstEffect/CMakeLists.txt b/plugins/VstEffect/CMakeLists.txt index 4a1c03196a8..2fb2183ec1a 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 "${CMAKE_CURRENT_SOURCE_DIR}/*.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 ce65912574a..54260c2784e 100644 --- a/plugins/VstEffect/VstEffectControlDialog.cpp +++ b/plugins/VstEffect/VstEffectControlDialog.cpp @@ -240,9 +240,7 @@ VstEffectControlDialog::VstEffectControlDialog( VstEffectControls * _ctl ) : QSizePolicy::Fixed ), 1, 0 ); l->addWidget( resize, 2, 0, 1, 1, Qt::AlignCenter ); #ifdef LMMS_EMBED_VST -#if QT_VERSION < 0x050000 l->addWidget( m_pluginWidget, 3, 0, 1, 1, Qt::AlignCenter ); -#endif #endif l->setRowStretch( 5, 1 ); l->setColumnStretch( 1, 1 ); diff --git a/plugins/vst_base/CMakeLists.txt b/plugins/vst_base/CMakeLists.txt index 8bfdced4804..c7033c23860 100644 --- a/plugins/vst_base/CMakeLists.txt +++ b/plugins/vst_base/CMakeLists.txt @@ -2,16 +2,8 @@ IF(LMMS_SUPPORT_VST) INCLUDE(BuildPlugin) -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() - ADD_DEFINITIONS(${EMBED_FLAGS}) - IF(LMMS_BUILD_WIN32) ADD_DEFINITIONS(-DPTW32_STATIC_LIB) ADD_EXECUTABLE(RemoteVstPlugin "${CMAKE_CURRENT_SOURCE_DIR}/RemoteVstPlugin.cpp") From 6839746344fd3512a2170cdd86f9174574baffc0 Mon Sep 17 00:00:00 2001 From: Lukas W Date: Mon, 6 Nov 2017 11:17:10 +0100 Subject: [PATCH 27/53] VstEmbed: Allow selecting method at runtime --- include/ConfigManager.h | 2 + include/RemotePlugin.h | 8 +- include/SetupDialog.h | 3 +- plugins/VstEffect/VstEffectControlDialog.cpp | 92 +++---- plugins/VstEffect/VstEffectControlDialog.h | 2 - plugins/vestige/vestige.cpp | 8 - plugins/vst_base/RemoteVstPlugin.cpp | 92 +++++-- plugins/vst_base/VstPlugin.cpp | 275 +++++++++++-------- plugins/vst_base/VstPlugin.h | 27 +- plugins/vst_base/communication.h | 2 - src/core/ConfigManager.cpp | 16 ++ src/core/RemotePlugin.cpp | 5 +- src/gui/SetupDialog.cpp | 33 ++- 13 files changed, 338 insertions(+), 227 deletions(-) diff --git a/include/ConfigManager.h b/include/ConfigManager.h index 4c0a73e056a..ad3a12373dd 100644 --- a/include/ConfigManager.h +++ b/include/ConfigManager.h @@ -210,6 +210,8 @@ class EXPORT ConfigManager return m_recentlyOpenedProjects; } + QString vstEmbedMethod() const; + // returns true if the working dir (e.g. ~/lmms) exists on disk bool hasWorkingDir() const; diff --git a/include/RemotePlugin.h b/include/RemotePlugin.h index 3493a9e6f47..58dcbc706ee 100644 --- a/include/RemotePlugin.h +++ b/include/RemotePlugin.h @@ -771,7 +771,7 @@ class EXPORT RemotePlugin : public QObject, public RemotePluginBase #endif } - bool init( const QString &pluginExecutable, bool waitForInitDoneMsg ); + bool init( const QString &pluginExecutable, bool waitForInitDoneMsg, QStringList extraArgs = {} ); inline void waitForHostInfoGotten() { @@ -798,7 +798,7 @@ class EXPORT RemotePlugin : public QObject, public RemotePluginBase } - void toggleUI() + virtual void toggleUI() { lock(); sendMessage( IdToggleUI ); @@ -830,8 +830,8 @@ class EXPORT RemotePlugin : public QObject, public RemotePluginBase } public slots: - void showUI(); - void hideUI(); + virtual void showUI(); + virtual void hideUI(); protected: inline void setSplittedChannels( bool _on ) diff --git a/include/SetupDialog.h b/include/SetupDialog.h index e34035d2ec1..ebaa90f0151 100644 --- a/include/SetupDialog.h +++ b/include/SetupDialog.h @@ -204,7 +204,8 @@ private slots: MswMap m_midiIfaceSetupWidgets; trMap m_midiIfaceNames; - + QComboBox* m_vstEmbedComboBox; + QString m_vstEmbedMethod; } ; diff --git a/plugins/VstEffect/VstEffectControlDialog.cpp b/plugins/VstEffect/VstEffectControlDialog.cpp index 54260c2784e..3320b05ce40 100644 --- a/plugins/VstEffect/VstEffectControlDialog.cpp +++ b/plugins/VstEffect/VstEffectControlDialog.cpp @@ -30,6 +30,7 @@ #include "VstEffectControlDialog.h" #include "VstEffect.h" +#include "ConfigManager.h" #include "PixmapButton.h" #include "embed.h" #include "ToolTip.h" @@ -43,9 +44,7 @@ VstEffectControlDialog::VstEffectControlDialog( VstEffectControls * _ctl ) : EffectControlDialog( _ctl ), -#ifdef LMMS_EMBED_VST m_pluginWidget( NULL ), -#endif m_plugin( NULL ), tbLabel( NULL ) { @@ -54,46 +53,43 @@ VstEffectControlDialog::VstEffectControlDialog( VstEffectControls * _ctl ) : l->setVerticalSpacing( 2 ); l->setHorizontalSpacing( 2 ); + bool embed_vst = ConfigManager::inst()->vstEmbedMethod() != "none"; + if( _ctl != NULL && _ctl->m_effect != NULL && _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(); - -#ifdef LMMS_BUILD_WIN32 - if( !m_pluginWidget ) - { + if (embed_vst) { + m_plugin->createUI( nullptr, true ); m_pluginWidget = m_plugin->pluginWidget( false ); - } -#endif -#else // LMMS_EMBED_VST - m_plugin->showUI(); +#ifdef LMMS_BUILD_WIN32 + if( !m_pluginWidget ) + { + m_pluginWidget = m_plugin->pluginWidget( false ); + } #endif + + } } -#ifdef LMMS_EMBED_VST - if( m_pluginWidget ) -#else - if( m_plugin ) -#endif + if ( m_plugin && (!embed_vst || m_pluginWidget) ) { 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 ) ), - SLOT( togglePluginUI( bool ) ) ); - emit btn->click(); -#else - connect( btn, SIGNAL( clicked( bool ) ), - SLOT( togglePluginUI( bool ) ) ); -#endif + + if (embed_vst) { + btn->setCheckable( true ); + btn->setChecked( true ); + connect( btn, SIGNAL( toggled( bool ) ), + SLOT( togglePluginUI( bool ) ) ); + } else { + connect( btn, SIGNAL( clicked( bool ) ), + SLOT( togglePluginUI( bool ) ) ); + } btn->setMinimumWidth( 78 ); btn->setMaximumWidth( 78 ); @@ -222,12 +218,15 @@ 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 + int newSize; + + if (embed_vst) { + newSize = m_pluginWidget->width() + 20; + newSize = (newSize < 250) ? 250 : newSize; + } else { + newSize = 250; + } + QWidget* resize = new QWidget(this); resize->resize( newSize, 10 ); QWidget* space0 = new QWidget(this); @@ -239,9 +238,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 + if (embed_vst) { + l->addWidget( m_pluginWidget, 3, 0, 1, 1, Qt::AlignCenter ); + } l->setRowStretch( 5, 1 ); l->setColumnStretch( 1, 1 ); @@ -291,20 +290,17 @@ VstEffectControlDialog::~VstEffectControlDialog() void VstEffectControlDialog::togglePluginUI( bool checked ) { - if( m_plugin ) + if( !m_plugin ) { + return; + } + + if( ConfigManager::inst()->vstEmbedMethod() != "none" ) + { + m_pluginWidget->setVisible( checked ); + } + else { -#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 69263a80452..ae86315abbe 100644 --- a/plugins/VstEffect/VstEffectControlDialog.h +++ b/plugins/VstEffect/VstEffectControlDialog.h @@ -51,9 +51,7 @@ class VstEffectControlDialog : public EffectControlDialog virtual void paintEvent( QPaintEvent * _pe ); private: -#ifdef LMMS_EMBED_VST QWidget * m_pluginWidget; -#endif PixmapButton * m_openPresetButton; PixmapButton * m_rolLPresetButton; diff --git a/plugins/vestige/vestige.cpp b/plugins/vestige/vestige.cpp index 59f29be9e35..4fa3bd4e988 100644 --- a/plugins/vestige/vestige.cpp +++ b/plugins/vestige/vestige.cpp @@ -262,11 +262,7 @@ 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 ) { @@ -743,11 +739,7 @@ void VestigeInstrumentView::toggleGUI( void ) { return; } -#ifdef LMMS_EMBED_VST - m_vi->m_plugin->toggleEditor(); -#else m_vi->m_plugin->toggleUI(); -#endif } diff --git a/plugins/vst_base/RemoteVstPlugin.cpp b/plugins/vst_base/RemoteVstPlugin.cpp index 8b594a4b2ce..3a5eeed006a 100644 --- a/plugins/vst_base/RemoteVstPlugin.cpp +++ b/plugins/vst_base/RemoteVstPlugin.cpp @@ -66,7 +66,7 @@ #include #include #include - +#include #include @@ -103,8 +103,12 @@ struct ERect #include #endif +using namespace std; + static VstHostLanguages hlang = LanguageEnglish; +static bool EMBED = false; +static bool EMBED_X11 = false; class RemoteVstPlugin; @@ -521,24 +525,17 @@ RemoteVstPlugin::~RemoteVstPlugin() bool RemoteVstPlugin::processMessage( const message & _m ) { - switch( _m.id ) + if (! EMBED) { - -#ifdef LMMS_EMBED_VST_X11 - case IdShowUI: - ShowWindow( m_window, SW_SHOWNORMAL ); - UpdateWindow( m_window ); - break; -#endif - -#ifndef LMMS_EMBED_VST + switch( _m.id ) + { case IdShowUI: initEditor(); - break; + return true; case IdHideUI: destroyEditor(); - break; + return true; case IdToggleUI: if( m_window ) @@ -549,14 +546,23 @@ bool RemoteVstPlugin::processMessage( const message & _m ) { initEditor(); } - break; + return true; case IdIsUIVisible: sendMessage( message( IdIsUIVisible ) - .addInt( m_window ? 1 : 0 ) ); - break; -#endif + .addInt( m_window ? 1 : 0 ) ); + return true; + } + } + else if (EMBED && _m.id == IdShowUI) + { + ShowWindow( m_window, SW_SHOWNORMAL ); + UpdateWindow( m_window ); + return true; + } + switch( _m.id ) + { case IdVstLoadPlugin: init( _m.getString() ); break; @@ -757,12 +763,15 @@ void RemoteVstPlugin::initEditor() m_registeredWindowClass = true; } + DWORD dwStyle; + if (EMBED) { + dwStyle = WS_POPUP | WS_SYSMENU | WS_BORDER; + } else { + dwStyle = WS_OVERLAPPEDWINDOW & ~WS_MAXIMIZEBOX; + } + m_window = CreateWindowEx( 0, "LVSL", pluginName(), -#ifdef LMMS_EMBED_VST - WS_POPUP | WS_SYSMENU | WS_BORDER, -#else - WS_OVERLAPPEDWINDOW & ~WS_MAXIMIZEBOX, -#endif + dwStyle, 0, 0, 10, 10, NULL, NULL, hInst, NULL ); if( m_window == NULL ) { @@ -784,9 +793,9 @@ void RemoteVstPlugin::initEditor() SWP_NOMOVE | SWP_NOZORDER ); pluginDispatch( effEditTop ); -#ifndef LMMS_EMBED_VST_X11 - ShowWindow( m_window, SW_SHOWNORMAL ); -#endif + if (! EMBED) { + ShowWindow( m_window, SW_SHOWNORMAL ); + } #ifdef LMMS_BUILD_LINUX m_windowID = (intptr_t) GetProp( m_window, "__wine_x11_whole_window" ); @@ -2030,9 +2039,9 @@ LRESULT CALLBACK RemoteVstPlugin::messageWndProc( HWND hwnd, UINT uMsg, int main( int _argc, char * * _argv ) { #ifdef SYNC_WITH_SHM_FIFO - if( _argc < 3 ) + if( _argc < 4 ) #else - if( _argc < 2 ) + if( _argc < 3 ) #endif { fprintf( stderr, "not enough arguments\n" ); @@ -2064,6 +2073,35 @@ int main( int _argc, char * * _argv ) } #endif + { + #ifdef SYNC_WITH_SHM_FIFO + int embedMethodIndex = 3; + #else + int embedMethodIndex = 2; + #endif + std::string embedMethod = _argv[embedMethodIndex]; + + if ( embedMethod == "none" ) + { + cerr << "Starting detached." << endl; + EMBED = EMBED_X11 = false; + } + else if ( embedMethod == "qt" ) + { + cerr << "Starting using Qt-native embedding." << endl; + EMBED = true; EMBED_X11 = false; + } + else if ( embedMethod == "xembed" ) + { + cerr << "Starting using X11Embed protocol." << endl; + EMBED = true; EMBED_X11 = true; + } + else + { + cerr << "Unknown embed method " << embedMethod << ". Starting detached instead." << endl; + } + } + // constructor automatically will process messages until it receives // a IdVstLoadPlugin message and processes it #ifdef SYNC_WITH_SHM_FIFO diff --git a/plugins/vst_base/VstPlugin.cpp b/plugins/vst_base/VstPlugin.cpp index bb4116cc6e4..10863ad59c8 100644 --- a/plugins/vst_base/VstPlugin.cpp +++ b/plugins/vst_base/VstPlugin.cpp @@ -2,7 +2,7 @@ * VstPlugin.cpp - implementation of VstPlugin class * * Copyright (c) 2005-2014 Tobias Doerffel - * + * * This file is part of LMMS - https://lmms.io * * This program is free software; you can redistribute it and/or @@ -24,6 +24,7 @@ #include "VstPlugin.h" +#include #include #include #include @@ -56,7 +57,6 @@ #include "templates.h" #include "FileDialog.h" -#ifdef LMMS_EMBED_VST class vstSubWin : public QMdiSubWindow { public: @@ -78,9 +78,6 @@ class vstSubWin : public QMdiSubWindow e->ignore(); } } ; -#endif - - VstPlugin::VstPlugin( const QString & _plugin ) : @@ -136,7 +133,7 @@ VstPlugin::~VstPlugin() void VstPlugin::tryLoad( const QString &remoteVstPluginExecutable ) { - init( remoteVstPluginExecutable, false ); + init( remoteVstPluginExecutable, false, {ConfigManager::inst()->vstEmbedMethod()} ); waitForHostInfoGotten(); if( failed() ) @@ -175,79 +172,6 @@ void VstPlugin::tryLoad( const QString &remoteVstPluginExecutable ) } -#ifdef LMMS_EMBED_VST -void VstPlugin::showEditor( QWidget * _parent, bool isEffect ) -{ - QWidget * w = pluginWidget(); - if( w ) - { -#ifdef LMMS_BUILD_WIN32 - // hide sw, plugin window wrapper on win32 - // this is obtained from pluginWidget() - if( isEffect ) - { - w->setWindowFlags( Qt::FramelessWindowHint ); - w->setAttribute( Qt::WA_TranslucentBackground ); - } - else - { - w->setWindowFlags( Qt::WindowCloseButtonHint ); - } -#endif - w->show(); - return; - } - -#ifdef LMMS_BUILD_LINUX - if( m_pluginWindowID == 0 ) - { - return; - } - - 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 ) - { - m_pluginWidget = container; - - sw->setWidget(container); - - if( isEffect ) - { - sw->setAttribute( Qt::WA_TranslucentBackground ); - sw->setWindowFlags( Qt::FramelessWindowHint ); - } - else - { - sw->setWindowFlags( Qt::WindowCloseButtonHint ); - } - }; - -#ifdef LMMS_EMBED_VST_X11 -#endif - container->setFixedSize( m_pluginGeometry ); -#endif - - //m_pluginWidget->show(); -} - - void VstPlugin::hideEditor() @@ -270,26 +194,12 @@ void VstPlugin::toggleEditor() w->setVisible( !w->isVisible() ); } } -#endif void VstPlugin::loadSettings( const QDomElement & _this ) { -#ifdef LMMS_EMBED_VST - if( pluginWidget() != NULL ) - { - if( _this.attribute( "guivisible" ).toInt() ) - { - showEditor( NULL, false ); - } - else - { - hideEditor(); - } - } -#else if( _this.attribute( "guivisible" ).toInt() ) { showUI(); @@ -298,7 +208,6 @@ void VstPlugin::loadSettings( const QDomElement & _this ) { hideUI(); } -#endif const int num_params = _this.attribute( "numparams" ).toInt(); // if it exists try to load settings chunk @@ -331,18 +240,21 @@ void VstPlugin::loadSettings( const QDomElement & _this ) void VstPlugin::saveSettings( QDomDocument & _doc, QDomElement & _this ) { -#ifdef LMMS_EMBED_VST - if( pluginWidget() != NULL ) + if ( ConfigManager::inst()->vstEmbedMethod() != "none" ) { - _this.setAttribute( "guivisible", pluginWidget()->isVisible() ); + if( pluginWidget() != NULL ) + { + _this.setAttribute( "guivisible", pluginWidget()->isVisible() ); + } } -#else - int visible = isUIVisible(); - if ( visible != -1 ) + else { - _this.setAttribute( "guivisible", visible ); + int visible = isUIVisible(); + if ( visible != -1 ) + { + _this.setAttribute( "guivisible", visible ); + } } -#endif // try to save all settings in a chunk QByteArray chunk = saveChunk(); @@ -366,6 +278,18 @@ void VstPlugin::saveSettings( QDomDocument & _doc, QDomElement & _this ) _this.setAttribute( "program", currentProgram() ); } +void VstPlugin::toggleUI() +{ + if ( ConfigManager::inst()->vstEmbedMethod() == "none" ) + { + RemotePlugin::toggleUI(); + } + else if (pluginWidget()) + { + toggleEditor(); + } +} + @@ -437,6 +361,21 @@ void VstPlugin::setParameterDump( const QMap & _pdump ) unlock(); } +QWidget *VstPlugin::pluginWidget(bool _top_widget) +{ + if ( ConfigManager::inst()->vstEmbedMethod() != "none" ) + { + if( _top_widget && m_pluginWidget ) + { + if( m_pluginWidget->parentWidget() ) + { + return m_pluginWidget->parentWidget(); + } + } + } + return m_pluginWidget; +} + @@ -444,17 +383,17 @@ bool VstPlugin::processMessage( const message & _m ) { switch( _m.id ) { - case IdVstBadDllFormat: - m_badDllFormat = true; - break; + case IdVstBadDllFormat: + m_badDllFormat = true; + break; - case IdVstPluginWindowID: - m_pluginWindowID = _m.getInt(); - break; + case IdVstPluginWindowID: + m_pluginWindowID = _m.getInt(); + break; - case IdVstPluginEditorGeometry: - m_pluginGeometry = QSize( _m.getInt( 0 ), - _m.getInt( 1 ) ); + case IdVstPluginEditorGeometry: + m_pluginGeometry = QSize( _m.getInt( 0 ), + _m.getInt( 1 ) ); break; case IdVstPluginName: @@ -629,6 +568,60 @@ void VstPlugin::idleUpdate() unlock(); } +void VstPlugin::showUI() +{ + QString embedMethod = ConfigManager::inst()->vstEmbedMethod(); + if ( embedMethod == "none" ) + { + RemotePlugin::showUI(); + } + else + { + if (! pluginWidget()) { + createUI( NULL, false ); + } + + QWidget * w = pluginWidget(); + if( w ) + { +# ifdef LMMS_BUILD_WIN32 + // hide sw, plugin window wrapper on win32 + // this is obtained from pluginWidget() + if( isEffect ) + { + w->setWindowFlags( Qt::FramelessWindowHint ); + w->setAttribute( Qt::WA_TranslucentBackground ); + } + else + { + w->setWindowFlags( Qt::WindowCloseButtonHint ); + } +# endif + w->show(); + } + } +} + +void VstPlugin::hideUI() +{ + RemotePlugin::hideUI(); + if ( ConfigManager::inst()->vstEmbedMethod() == "none" ) + { + } + else if ( pluginWidget() != nullptr ) + { + hideEditor(); + } +} + +// X11Embed only +void VstPlugin::handleClientEmbed() +{ + lock(); + sendMessage( IdShowUI ); + unlock(); +} + void VstPlugin::loadChunk( const QByteArray & _chunk ) @@ -672,6 +665,66 @@ QByteArray VstPlugin::saveChunk() return a; } +void VstPlugin::createUI( QWidget * parent, bool isEffect ) +{ + if( m_pluginWindowID == 0 ) + { + return; + } + + QWidget* container = nullptr; + m_pluginSubWindow = new vstSubWin( gui->mainWindow()->workspace() ); + auto sw = m_pluginSubWindow.data(); + + QString embedMethod = ConfigManager::inst()->vstEmbedMethod(); + if (embedMethod == "qt" ) + { + QWindow* vw = QWindow::fromWinId(m_pluginWindowID); + container = QWidget::createWindowContainer(vw, sw ); + RemotePlugin::showUI(); + // TODO: Synchronize show + // Tell remote that it is embedded + // Wait for remote reply + } +#ifdef LMMS_BUILD_LINUX + else if (embedMethod == "xembed" ) + { + QX11EmbedContainer * embedContainer = new QX11EmbedContainer( sw ); + connect(embedContainer, SIGNAL(clientIsEmbedded()), this, SLOT(handleClientEmbed())); + embedContainer->embedClient( m_pluginWindowID ); + container = embedContainer; + } +#endif + else + { + qCritical() << "Unknown embed method" << embedMethod; + delete m_pluginSubWindow; + return; + } + + container->setFixedSize( m_pluginGeometry ); + container->setWindowTitle( name() ); + + if( parent == NULL ) + { + m_pluginWidget = container; + + sw->setWidget(container); + + if( isEffect ) + { + sw->setAttribute( Qt::WA_TranslucentBackground ); + sw->setWindowFlags( Qt::FramelessWindowHint ); + } + else + { + sw->setWindowFlags( Qt::WindowCloseButtonHint ); + } + }; + + container->setFixedSize( m_pluginGeometry ); +} + diff --git a/plugins/vst_base/VstPlugin.h b/plugins/vst_base/VstPlugin.h index f30335bdab9..09b00157917 100644 --- a/plugins/vst_base/VstPlugin.h +++ b/plugins/vst_base/VstPlugin.h @@ -35,6 +35,8 @@ #include "JournallingObject.h" #include "communication.h" +class vstSubWin; + class PLUGIN_EXPORT VstPlugin : public RemotePlugin, public JournallingObject { @@ -52,11 +54,8 @@ 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 { @@ -94,19 +93,7 @@ class PLUGIN_EXPORT VstPlugin : public RemotePlugin, public JournallingObject void setParameterDump( const QMap & _pdump ); - inline QWidget * pluginWidget( bool _top_widget = true ) - { -#ifdef LMMS_EMBED_VST - if( _top_widget && m_pluginWidget ) - { - if( m_pluginWidget->parentWidget() ) - { - return m_pluginWidget->parentWidget(); - } - } -#endif - return m_pluginWidget; - } + QWidget * pluginWidget( bool _top_widget = true ); virtual void loadSettings( const QDomElement & _this ); virtual void saveSettings( QDomDocument & _doc, QDomElement & _this ); @@ -116,6 +103,9 @@ class PLUGIN_EXPORT VstPlugin : public RemotePlugin, public JournallingObject return "vstplugin"; } + void toggleUI() override; + + void createUI( QWidget *parent, bool isEffect ); public slots: void setTempo( bpm_t _bpm ); @@ -128,6 +118,10 @@ public slots: void setParam( int i, float f ); void idleUpdate(); + void showUI() override; + void hideUI() override; + + void handleClientEmbed(); private: void loadChunk( const QByteArray & _chunk ); @@ -135,6 +129,7 @@ public slots: QString m_plugin; QPointer m_pluginWidget; + QPointer m_pluginSubWindow; int m_pluginWindowID; QSize m_pluginGeometry; diff --git a/plugins/vst_base/communication.h b/plugins/vst_base/communication.h index ab7a97dfefe..3e85007e711 100644 --- a/plugins/vst_base/communication.h +++ b/plugins/vst_base/communication.h @@ -30,8 +30,6 @@ #include "RemotePlugin.h" -//#define LMMS_EMBED_VST - struct VstParameterDumpItem { diff --git a/src/core/ConfigManager.cpp b/src/core/ConfigManager.cpp index 5ccb4d345d4..aceb57bc22c 100644 --- a/src/core/ConfigManager.cpp +++ b/src/core/ConfigManager.cpp @@ -35,6 +35,10 @@ #include "lmmsversion.h" +#ifdef LMMS_BUILD_LINUX +#include +#endif + static inline QString ensureTrailingSlash( const QString & s ) { if( ! s.isEmpty() && !s.endsWith('/') && !s.endsWith('\\') ) @@ -186,6 +190,18 @@ QString ConfigManager::defaultVersion() const return LMMS_VERSION; } +QString ConfigManager::vstEmbedMethod() const +{ + QString defaultMethod = "qt"; +#ifdef LMMS_BUILD_LINUX + if (QX11Info::isPlatformX11()) { + defaultMethod = "xembed"; + } +#endif + + return value( "ui", "vstembedmethod", defaultMethod ); +} + bool ConfigManager::hasWorkingDir() const { return QDir( m_workingDir ).exists(); diff --git a/src/core/RemotePlugin.cpp b/src/core/RemotePlugin.cpp index ac2cf3f91ac..d0eafbfa35f 100644 --- a/src/core/RemotePlugin.cpp +++ b/src/core/RemotePlugin.cpp @@ -164,8 +164,8 @@ RemotePlugin::~RemotePlugin() -bool RemotePlugin::init( const QString &pluginExecutable, - bool waitForInitDoneMsg ) +bool RemotePlugin::init(const QString &pluginExecutable, + bool waitForInitDoneMsg , QStringList extraArgs) { lock(); if( m_failed ) @@ -208,6 +208,7 @@ bool RemotePlugin::init( const QString &pluginExecutable, #else args << m_socketFile; #endif + args << extraArgs; #ifndef DEBUG_REMOTE_PLUGIN m_process.setProcessChannelMode( QProcess::ForwardedChannels ); m_process.setWorkingDirectory( QCoreApplication::applicationDirPath() ); diff --git a/src/gui/SetupDialog.cpp b/src/gui/SetupDialog.cpp index 879c1b7b9d9..9a4851b4973 100644 --- a/src/gui/SetupDialog.cpp +++ b/src/gui/SetupDialog.cpp @@ -67,7 +67,9 @@ #include "MidiApple.h" #include "MidiDummy.h" - +#ifdef LMMS_BUILD_LINUX +#include +#endif inline void labelWidget( QWidget * _w, const QString & _txt ) { @@ -137,12 +139,14 @@ SetupDialog::SetupDialog( ConfigTabs _tab_to_open ) : m_displayWaveform(ConfigManager::inst()->value( "ui", "displaywaveform").toInt() ), m_disableAutoQuit(ConfigManager::inst()->value( "ui", - "disableautoquit").toInt() ) + "disableautoquit").toInt() ), + m_vstEmbedMethod(ConfigManager::inst()->value( "ui", + "vstembedmethod", "xembed")) { setWindowIcon( embed::getIconPixmap( "setup_general" ) ); setWindowTitle( tr( "Setup LMMS" ) ); setModal( true ); - setFixedSize( 452, 520 ); + setFixedSize( 452, 570 ); Engine::projectJournal()->setJournalling( false ); @@ -159,7 +163,7 @@ SetupDialog::SetupDialog( ConfigTabs _tab_to_open ) : m_tabBar->setFixedWidth( 72 ); QWidget * ws = new QWidget( settings ); - int wsHeight = 370; + int wsHeight = 420; #ifdef LMMS_HAVE_STK wsHeight += 50; #endif @@ -168,7 +172,7 @@ SetupDialog::SetupDialog( ConfigTabs _tab_to_open ) : #endif ws->setFixedSize( 360, wsHeight ); QWidget * general = new QWidget( ws ); - general->setFixedSize( 360, 240 ); + general->setFixedSize( 360, 290 ); QVBoxLayout * gen_layout = new QVBoxLayout( general ); gen_layout->setSpacing( 0 ); gen_layout->setMargin( 0 ); @@ -335,6 +339,19 @@ SetupDialog::SetupDialog( ConfigTabs _tab_to_open ) : misc_tw->setFixedHeight( YDelta*labelNumber + HeaderSize ); + TabWidget* embed_tw = new TabWidget( tr( "PLUGIN EMBEDDING" ), general); + embed_tw->setFixedHeight( 48 ); + m_vstEmbedComboBox = new QComboBox( embed_tw ); + m_vstEmbedComboBox->move( XDelta, YDelta ); + m_vstEmbedComboBox->addItem( tr( "No embedding" ), "none" ); + m_vstEmbedComboBox->addItem( tr( "Embed using Qt API" ), "qt" ); +#ifdef LMMS_BUILD_LINUX + if ( QX11Info::isPlatformX11() ) { + m_vstEmbedComboBox->addItem( tr( "Embed using XEmbed protocol" ), "xembed" ); + } +#endif + m_vstEmbedComboBox->setCurrentIndex( m_vstEmbedComboBox->findData( m_vstEmbedMethod ) ); + TabWidget * lang_tw = new TabWidget( tr( "LANGUAGE" ), general ); lang_tw->setFixedHeight( 48 ); QComboBox * changeLang = new QComboBox( lang_tw ); @@ -380,13 +397,15 @@ SetupDialog::SetupDialog( ConfigTabs _tab_to_open ) : gen_layout->addSpacing( 10 ); gen_layout->addWidget( misc_tw ); gen_layout->addSpacing( 10 ); + gen_layout->addWidget( embed_tw ); + gen_layout->addSpacing( 10 ); gen_layout->addWidget( lang_tw ); gen_layout->addStretch(); QWidget * paths = new QWidget( ws ); - int pathsHeight = 370; + int pathsHeight = 420; #ifdef LMMS_HAVE_STK pathsHeight += 55; #endif @@ -1044,6 +1063,8 @@ void SetupDialog::accept() ConfigManager::inst()->setValue( "ui", "disableautoquit", QString::number( m_disableAutoQuit ) ); ConfigManager::inst()->setValue( "app", "language", m_lang ); + ConfigManager::inst()->setValue( "ui", "vstembedmethod", + m_vstEmbedComboBox->currentData().toString() ); ConfigManager::inst()->setWorkingDir(QDir::fromNativeSeparators(m_workingDir)); From 415316f722f9e5c3daa77febda98196f3d88bd5e Mon Sep 17 00:00:00 2001 From: Lukas W Date: Mon, 6 Nov 2017 11:39:19 +0100 Subject: [PATCH 28/53] VstEmbed: Remove obsolete CMake flags --- plugins/CMakeLists.txt | 23 ----------------------- plugins/VstEffect/CMakeLists.txt | 2 -- plugins/vst_base/CMakeLists.txt | 3 --- 3 files changed, 28 deletions(-) diff --git a/plugins/CMakeLists.txt b/plugins/CMakeLists.txt index dfd8dfbe4ac..24c15e391bd 100644 --- a/plugins/CMakeLists.txt +++ b/plugins/CMakeLists.txt @@ -35,29 +35,6 @@ 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 2fb2183ec1a..4a1c03196a8 100644 --- a/plugins/VstEffect/CMakeLists.txt +++ b/plugins/VstEffect/CMakeLists.txt @@ -10,8 +10,6 @@ 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 "${CMAKE_CURRENT_SOURCE_DIR}/*.png") SET_TARGET_PROPERTIES(vsteffect PROPERTIES COMPILE_FLAGS "-Wno-attributes") TARGET_LINK_LIBRARIES(vsteffect -lvstbase) diff --git a/plugins/vst_base/CMakeLists.txt b/plugins/vst_base/CMakeLists.txt index c7033c23860..9024452db82 100644 --- a/plugins/vst_base/CMakeLists.txt +++ b/plugins/vst_base/CMakeLists.txt @@ -2,8 +2,6 @@ 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") @@ -65,7 +63,6 @@ ADD_CUSTOM_COMMAND( -std=c++0x -mwindows -lpthread ${EXTRA_FLAGS} -fno-omit-frame-pointer ${WINE_BUILD_FLAGS} - ${EMBED_FLAGS} -o ../RemoteVstPlugin COMMAND sh -c "mv ../RemoteVstPlugin.exe ../RemoteVstPlugin || true" TARGET vstbase From 134dae80d582da4f4e50d71b1e3d2e0798605af8 Mon Sep 17 00:00:00 2001 From: Lukas W Date: Mon, 6 Nov 2017 11:39:39 +0100 Subject: [PATCH 29/53] VstEmbed: Add missing find_package instruction for Qt5X11Extras --- CMakeLists.txt | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/CMakeLists.txt b/CMakeLists.txt index a0306c5c273..dec8f479cce 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -152,6 +152,11 @@ IF(WANT_QT5) Qt5::Xml ) + IF(LMMS_BUILD_LINUX) + FIND_PACKAGE(Qt5X11Extras REQUIRED) + LIST(APPEND QT_LIBRARIES Qt5::X11Extras) + ENDIF() + FIND_PACKAGE(Qt5Test) SET(QT_QTTEST_LIBRARY Qt5::Test) ELSE() From 472a74d2529862894504d992acb21d688e4fb876 Mon Sep 17 00:00:00 2001 From: Lukas W Date: Mon, 6 Nov 2017 16:31:12 +0100 Subject: [PATCH 30/53] Minor fixes --- plugins/vst_base/VstPlugin.cpp | 13 ------------- src/gui/SetupDialog.cpp | 3 +-- 2 files changed, 1 insertion(+), 15 deletions(-) diff --git a/plugins/vst_base/VstPlugin.cpp b/plugins/vst_base/VstPlugin.cpp index 10863ad59c8..be407a5bd59 100644 --- a/plugins/vst_base/VstPlugin.cpp +++ b/plugins/vst_base/VstPlugin.cpp @@ -584,19 +584,6 @@ void VstPlugin::showUI() QWidget * w = pluginWidget(); if( w ) { -# ifdef LMMS_BUILD_WIN32 - // hide sw, plugin window wrapper on win32 - // this is obtained from pluginWidget() - if( isEffect ) - { - w->setWindowFlags( Qt::FramelessWindowHint ); - w->setAttribute( Qt::WA_TranslucentBackground ); - } - else - { - w->setWindowFlags( Qt::WindowCloseButtonHint ); - } -# endif w->show(); } } diff --git a/src/gui/SetupDialog.cpp b/src/gui/SetupDialog.cpp index 9a4851b4973..78b9c0d2351 100644 --- a/src/gui/SetupDialog.cpp +++ b/src/gui/SetupDialog.cpp @@ -140,8 +140,7 @@ SetupDialog::SetupDialog( ConfigTabs _tab_to_open ) : "displaywaveform").toInt() ), m_disableAutoQuit(ConfigManager::inst()->value( "ui", "disableautoquit").toInt() ), - m_vstEmbedMethod(ConfigManager::inst()->value( "ui", - "vstembedmethod", "xembed")) + m_vstEmbedMethod( ConfigManager::inst()->vstEmbedMethod() ) { setWindowIcon( embed::getIconPixmap( "setup_general" ) ); setWindowTitle( tr( "Setup LMMS" ) ); From e95fdcc37d9dda014b0607cfe3dfbec18b098c52 Mon Sep 17 00:00:00 2001 From: Lukas W Date: Mon, 6 Nov 2017 18:29:09 +0100 Subject: [PATCH 31/53] Add missing includes --- plugins/vst_base/VstPlugin.cpp | 22 +++++++++++++--------- 1 file changed, 13 insertions(+), 9 deletions(-) diff --git a/plugins/vst_base/VstPlugin.cpp b/plugins/vst_base/VstPlugin.cpp index be407a5bd59..35621bb9853 100644 --- a/plugins/vst_base/VstPlugin.cpp +++ b/plugins/vst_base/VstPlugin.cpp @@ -32,21 +32,25 @@ #include #include #include + #ifdef LMMS_BUILD_LINUX -#if QT_VERSION < 0x050000 -#include -#include -#else -#include "X11EmbedContainer.h" -#include +# if QT_VERSION < 0x050000 +# include +# include +# else +# include "X11EmbedContainer.h" +# include +# endif #endif -#else -#include + +#if QT_VERSION >= 0x050000 +# include #endif + #include #ifdef LMMS_BUILD_WIN32 -#include +# include #endif #include "ConfigManager.h" From 421a85d2e1755917fdb08a0575a2c24df282ee00 Mon Sep 17 00:00:00 2001 From: Lukas W Date: Mon, 6 Nov 2017 18:32:56 +0100 Subject: [PATCH 32/53] Update qt5-x11embed submodule --- src/3rdparty/qt5-x11embed | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/3rdparty/qt5-x11embed b/src/3rdparty/qt5-x11embed index dad35c07cdb..ec77d4ba40b 160000 --- a/src/3rdparty/qt5-x11embed +++ b/src/3rdparty/qt5-x11embed @@ -1 +1 @@ -Subproject commit dad35c07cdb704f3e9306e6301f7eb4c098552a2 +Subproject commit ec77d4ba40bebc0c53a016adb646b6fa65dd5faf From e3ba9ba9be38c8c64198be24af99c8b17a564db3 Mon Sep 17 00:00:00 2001 From: Lukas W Date: Mon, 6 Nov 2017 19:12:52 +0100 Subject: [PATCH 33/53] Update qt5-x11embed submodule for CMake changes --- src/3rdparty/qt5-x11embed | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/3rdparty/qt5-x11embed b/src/3rdparty/qt5-x11embed index ec77d4ba40b..ca3bf973f80 160000 --- a/src/3rdparty/qt5-x11embed +++ b/src/3rdparty/qt5-x11embed @@ -1 +1 @@ -Subproject commit ec77d4ba40bebc0c53a016adb646b6fa65dd5faf +Subproject commit ca3bf973f80eaa324d4d3da0eb7a96241339d424 From 2b6f3660636d1440b0aecb9320ffa299440f7b6d Mon Sep 17 00:00:00 2001 From: Lukas W Date: Tue, 7 Nov 2017 11:03:37 +0100 Subject: [PATCH 34/53] CMake: Only link qx11embedcontainer on Linux --- plugins/vst_base/CMakeLists.txt | 5 ++++- src/3rdparty/CMakeLists.txt | 2 -- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/plugins/vst_base/CMakeLists.txt b/plugins/vst_base/CMakeLists.txt index fc3eb8416ab..af58ffb1a98 100644 --- a/plugins/vst_base/CMakeLists.txt +++ b/plugins/vst_base/CMakeLists.txt @@ -28,7 +28,10 @@ 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) + TARGET_LINK_LIBRARIES(vstbase qx11embedcontainer) +ENDIF() IF(LMMS_BUILD_LINUX AND NOT WANT_VST_NOWINE) diff --git a/src/3rdparty/CMakeLists.txt b/src/3rdparty/CMakeLists.txt index 23362b1c08c..5dec2890c45 100644 --- a/src/3rdparty/CMakeLists.txt +++ b/src/3rdparty/CMakeLists.txt @@ -4,8 +4,6 @@ set(CMAKE_CXX_FLAGS "") IF(QT5 AND LMMS_BUILD_LINUX) set(BUILD_SHARED_LIBS OFF) add_subdirectory(qt5-x11embed) -ELSE() - add_library(qx11embedcontainer STATIC /dev/null) ENDIF() ADD_SUBDIRECTORY(rpmalloc) From 6fd38fee9f19ab4e845be72bfd4d414b1253c840 Mon Sep 17 00:00:00 2001 From: Lukas W Date: Tue, 7 Nov 2017 11:21:00 +0100 Subject: [PATCH 35/53] Fix qt5-x11embed submodule reference --- src/3rdparty/qt5-x11embed | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/3rdparty/qt5-x11embed b/src/3rdparty/qt5-x11embed index ca3bf973f80..022b39a1d49 160000 --- a/src/3rdparty/qt5-x11embed +++ b/src/3rdparty/qt5-x11embed @@ -1 +1 @@ -Subproject commit ca3bf973f80eaa324d4d3da0eb7a96241339d424 +Subproject commit 022b39a1d496d72eb3e5b5188e5559f66afca957 From a8311a7b4945055c62f9730f5a601e992473ca58 Mon Sep 17 00:00:00 2001 From: Lukas W Date: Fri, 10 Nov 2017 08:02:29 +0100 Subject: [PATCH 36/53] Vst: Fix widget deletion --- plugins/VstEffect/VstEffect.cpp | 29 ++++------------------ plugins/VstEffect/VstEffect.h | 7 +++--- plugins/VstEffect/VstEffectControlDialog.h | 3 ++- plugins/vst_base/VstPlugin.cpp | 2 ++ 4 files changed, 13 insertions(+), 28 deletions(-) diff --git a/plugins/VstEffect/VstEffect.cpp b/plugins/VstEffect/VstEffect.cpp index 88e37ea925f..494613e26df 100644 --- a/plugins/VstEffect/VstEffect.cpp +++ b/plugins/VstEffect/VstEffect.cpp @@ -55,7 +55,6 @@ Plugin::Descriptor PLUGIN_EXPORT vsteffect_plugin_descriptor = VstEffect::VstEffect( Model * _parent, const Descriptor::SubPluginFeatures::Key * _key ) : Effect( &vsteffect_plugin_descriptor, _parent, _key ), - m_plugin( NULL ), m_pluginMutex(), m_key( *_key ), m_vstControls( this ) @@ -73,7 +72,6 @@ VstEffect::VstEffect( Model * _parent, VstEffect::~VstEffect() { - closePlugin(); } @@ -128,22 +126,20 @@ void VstEffect::openPlugin( const QString & _plugin ) VstPlugin::tr( "Loading plugin" ), VstPlugin::tr( "Please wait while loading VST plugin..." ), PLUGIN_NAME::getIconPixmap( "logo", 24, 24 ), 0 ); - m_pluginMutex.lock(); - m_plugin = new VstPlugin( _plugin ); + + QMutexLocker ml( &m_pluginMutex ); Q_UNUSED( ml ); + m_plugin.reset(new VstPlugin( _plugin )); if( m_plugin->failed() ) { - m_pluginMutex.unlock(); - closePlugin(); + m_plugin.reset(nullptr); delete tf; collectErrorForUI( VstPlugin::tr( "The VST plugin %1 could not be loaded." ).arg( _plugin ) ); return; } - VstPlugin::connect( Engine::getSong(), SIGNAL( tempoChanged( bpm_t ) ), m_plugin, SLOT( setTempo( bpm_t ) ) ); + VstPlugin::connect( Engine::getSong(), SIGNAL( tempoChanged( bpm_t ) ), m_plugin.data(), SLOT( setTempo( bpm_t ) ) ); m_plugin->setTempo( Engine::getSong()->getTempo() ); - m_pluginMutex.unlock(); - delete tf; m_key.attributes["file"] = _plugin; @@ -151,21 +147,6 @@ void VstEffect::openPlugin( const QString & _plugin ) -void VstEffect::closePlugin() -{ - m_pluginMutex.lock(); - if( m_plugin && m_plugin->pluginWidget() != NULL ) - { - delete m_plugin->pluginWidget(); - } - delete m_plugin; - m_plugin = NULL; - m_pluginMutex.unlock(); -} - - - - extern "C" { diff --git a/plugins/VstEffect/VstEffect.h b/plugins/VstEffect/VstEffect.h index b678367d5b4..95834e86294 100644 --- a/plugins/VstEffect/VstEffect.h +++ b/plugins/VstEffect/VstEffect.h @@ -25,13 +25,14 @@ #ifndef _VST_EFFECT_H #define _VST_EFFECT_H -#include +#include +#include #include "Effect.h" -#include "VstPlugin.h" #include "VstEffectControlDialog.h" #include "VstEffectControls.h" +class VstPlugin; class VstEffect : public Effect { @@ -58,7 +59,7 @@ class VstEffect : public Effect void openPlugin( const QString & _plugin ); void closePlugin(); - VstPlugin * m_plugin; + QSharedPointer m_plugin; QMutex m_pluginMutex; EffectKey m_key; diff --git a/plugins/VstEffect/VstEffectControlDialog.h b/plugins/VstEffect/VstEffectControlDialog.h index ae86315abbe..ddbbef8786a 100644 --- a/plugins/VstEffect/VstEffectControlDialog.h +++ b/plugins/VstEffect/VstEffectControlDialog.h @@ -31,6 +31,7 @@ #include #include #include +#include class VstEffectControls; @@ -59,7 +60,7 @@ class VstEffectControlDialog : public EffectControlDialog PixmapButton * m_managePluginButton; PixmapButton * m_savePresetButton; - VstPlugin * m_plugin; + QSharedPointer m_plugin; QLabel * tbLabel; diff --git a/plugins/vst_base/VstPlugin.cpp b/plugins/vst_base/VstPlugin.cpp index 35621bb9853..518494eabfc 100644 --- a/plugins/vst_base/VstPlugin.cpp +++ b/plugins/vst_base/VstPlugin.cpp @@ -130,6 +130,8 @@ VstPlugin::VstPlugin( const QString & _plugin ) : VstPlugin::~VstPlugin() { + delete m_pluginSubWindow; + delete m_pluginWidget; } From f9f4d0cb9c431006e07b7e4e8cfb51a68fd163a3 Mon Sep 17 00:00:00 2001 From: Lukas W Date: Fri, 10 Nov 2017 08:12:20 +0100 Subject: [PATCH 37/53] VstEmbed: Store embed method on plugin start Avoids bugs when embed method is changed in setup dialog while VSTs are running. --- plugins/VstEffect/VstEffectControlDialog.cpp | 3 +- plugins/vst_base/VstPlugin.cpp | 38 +++++++++----------- plugins/vst_base/VstPlugin.h | 3 ++ 3 files changed, 21 insertions(+), 23 deletions(-) diff --git a/plugins/VstEffect/VstEffectControlDialog.cpp b/plugins/VstEffect/VstEffectControlDialog.cpp index 3320b05ce40..b571eb1aa16 100644 --- a/plugins/VstEffect/VstEffectControlDialog.cpp +++ b/plugins/VstEffect/VstEffectControlDialog.cpp @@ -53,12 +53,13 @@ VstEffectControlDialog::VstEffectControlDialog( VstEffectControls * _ctl ) : l->setVerticalSpacing( 2 ); l->setHorizontalSpacing( 2 ); - bool embed_vst = ConfigManager::inst()->vstEmbedMethod() != "none"; + bool embed_vst = false; if( _ctl != NULL && _ctl->m_effect != NULL && _ctl->m_effect->m_plugin != NULL ) { m_plugin = _ctl->m_effect->m_plugin; + embed_vst = m_plugin->embedMethod() != "none"; if (embed_vst) { m_plugin->createUI( nullptr, true ); diff --git a/plugins/vst_base/VstPlugin.cpp b/plugins/vst_base/VstPlugin.cpp index 518494eabfc..2b1cbcd0969 100644 --- a/plugins/vst_base/VstPlugin.cpp +++ b/plugins/vst_base/VstPlugin.cpp @@ -85,21 +85,12 @@ class vstSubWin : public QMdiSubWindow VstPlugin::VstPlugin( const QString & _plugin ) : - RemotePlugin(), - JournallingObject(), m_plugin( _plugin ), - m_pluginWidget( NULL ), m_pluginWindowID( 0 ), + m_embedMethod( ConfigManager::inst()->vstEmbedMethod() ), m_badDllFormat( false ), - m_name(), m_version( 0 ), - m_vendorString(), - m_productString(), - m_currentProgramName(), - m_allProgramNames(), - p_name(), - m_currentProgram(), - m_idleTimer() + m_currentProgram() { setSplittedChannels( true ); @@ -139,7 +130,7 @@ VstPlugin::~VstPlugin() void VstPlugin::tryLoad( const QString &remoteVstPluginExecutable ) { - init( remoteVstPluginExecutable, false, {ConfigManager::inst()->vstEmbedMethod()} ); + init( remoteVstPluginExecutable, false, {m_embedMethod} ); waitForHostInfoGotten(); if( failed() ) @@ -246,7 +237,7 @@ void VstPlugin::loadSettings( const QDomElement & _this ) void VstPlugin::saveSettings( QDomDocument & _doc, QDomElement & _this ) { - if ( ConfigManager::inst()->vstEmbedMethod() != "none" ) + if ( m_embedMethod != "none" ) { if( pluginWidget() != NULL ) { @@ -286,7 +277,7 @@ void VstPlugin::saveSettings( QDomDocument & _doc, QDomElement & _this ) void VstPlugin::toggleUI() { - if ( ConfigManager::inst()->vstEmbedMethod() == "none" ) + if ( m_embedMethod == "none" ) { RemotePlugin::toggleUI(); } @@ -369,7 +360,7 @@ void VstPlugin::setParameterDump( const QMap & _pdump ) QWidget *VstPlugin::pluginWidget(bool _top_widget) { - if ( ConfigManager::inst()->vstEmbedMethod() != "none" ) + if ( m_embedMethod != "none" ) { if( _top_widget && m_pluginWidget ) { @@ -576,8 +567,7 @@ void VstPlugin::idleUpdate() void VstPlugin::showUI() { - QString embedMethod = ConfigManager::inst()->vstEmbedMethod(); - if ( embedMethod == "none" ) + if ( m_embedMethod == "none" ) { RemotePlugin::showUI(); } @@ -598,7 +588,7 @@ void VstPlugin::showUI() void VstPlugin::hideUI() { RemotePlugin::hideUI(); - if ( ConfigManager::inst()->vstEmbedMethod() == "none" ) + if ( m_embedMethod == "none" ) { } else if ( pluginWidget() != nullptr ) @@ -669,8 +659,7 @@ void VstPlugin::createUI( QWidget * parent, bool isEffect ) m_pluginSubWindow = new vstSubWin( gui->mainWindow()->workspace() ); auto sw = m_pluginSubWindow.data(); - QString embedMethod = ConfigManager::inst()->vstEmbedMethod(); - if (embedMethod == "qt" ) + if (m_embedMethod == "qt" ) { QWindow* vw = QWindow::fromWinId(m_pluginWindowID); container = QWidget::createWindowContainer(vw, sw ); @@ -680,7 +669,7 @@ void VstPlugin::createUI( QWidget * parent, bool isEffect ) // Wait for remote reply } #ifdef LMMS_BUILD_LINUX - else if (embedMethod == "xembed" ) + else if (m_embedMethod == "xembed" ) { QX11EmbedContainer * embedContainer = new QX11EmbedContainer( sw ); connect(embedContainer, SIGNAL(clientIsEmbedded()), this, SLOT(handleClientEmbed())); @@ -690,7 +679,7 @@ void VstPlugin::createUI( QWidget * parent, bool isEffect ) #endif else { - qCritical() << "Unknown embed method" << embedMethod; + qCritical() << "Unknown embed method" << m_embedMethod; delete m_pluginSubWindow; return; } @@ -718,6 +707,11 @@ void VstPlugin::createUI( QWidget * parent, bool isEffect ) container->setFixedSize( m_pluginGeometry ); } +QString VstPlugin::embedMethod() const +{ + return m_embedMethod; +} + diff --git a/plugins/vst_base/VstPlugin.h b/plugins/vst_base/VstPlugin.h index 09b00157917..4984d6849b7 100644 --- a/plugins/vst_base/VstPlugin.h +++ b/plugins/vst_base/VstPlugin.h @@ -107,6 +107,8 @@ class PLUGIN_EXPORT VstPlugin : public RemotePlugin, public JournallingObject void createUI( QWidget *parent, bool isEffect ); + QString embedMethod() const; + public slots: void setTempo( bpm_t _bpm ); void updateSampleRate(); @@ -132,6 +134,7 @@ public slots: QPointer m_pluginSubWindow; int m_pluginWindowID; QSize m_pluginGeometry; + const QString m_embedMethod; bool m_badDllFormat; From 358a251cff34be116b2ac2e35a1be1ee0ec47aa2 Mon Sep 17 00:00:00 2001 From: Lukas W Date: Fri, 10 Nov 2017 09:18:40 +0100 Subject: [PATCH 38/53] VstEmbed: Support changing embed method without restart --- include/ConfigManager.h | 8 +++++--- plugins/vestige/vestige.cpp | 23 +++++++++++++++++++---- plugins/vestige/vestige.h | 2 ++ plugins/vst_base/VstPlugin.cpp | 2 +- src/core/ConfigManager.cpp | 11 +++++++---- src/gui/SetupDialog.cpp | 24 ++++++++++++++---------- 6 files changed, 48 insertions(+), 22 deletions(-) diff --git a/include/ConfigManager.h b/include/ConfigManager.h index ad3a12373dd..3e5c0578f0b 100644 --- a/include/ConfigManager.h +++ b/include/ConfigManager.h @@ -32,9 +32,9 @@ #include #include #include +#include #include "export.h" -#include "MemoryManager.h" class LmmsCore; @@ -51,9 +51,9 @@ const QString TRACK_ICON_PATH = "track_icons/"; const QString LOCALE_PATH = "locale/"; -class EXPORT ConfigManager +class EXPORT ConfigManager : public QObject { - MM_OPERATORS + Q_OBJECT public: static inline ConfigManager * inst() { @@ -244,6 +244,8 @@ class EXPORT ConfigManager // creates the working directory & subdirectories on disk. void createWorkingDir(); +signals: + void valueChanged( QString cls, QString attribute, QString value ); private: static ConfigManager * s_instanceOfMe; diff --git a/plugins/vestige/vestige.cpp b/plugins/vestige/vestige.cpp index 4fa3bd4e988..211b363a91a 100644 --- a/plugins/vestige/vestige.cpp +++ b/plugins/vestige/vestige.cpp @@ -32,6 +32,7 @@ #include #include +#include "ConfigManager.h" #include "BufferManager.h" #include "Engine.h" #include "gui_templates.h" @@ -89,6 +90,10 @@ vestigeInstrument::vestigeInstrument( InstrumentTrack * _instrument_track ) : // now we need a play-handle which cares for calling play() InstrumentPlayHandle * iph = new InstrumentPlayHandle( this, _instrument_track ); Engine::mixer()->addPlayHandle( iph ); + + connect( ConfigManager::inst(), SIGNAL( valueChanged(QString,QString,QString) ), + this, SLOT( handleConfigChange(QString, QString, QString) ), + Qt::QueuedConnection ); } @@ -170,6 +175,20 @@ void vestigeInstrument::setParameter( void ) } } +void vestigeInstrument::handleConfigChange(QString cls, QString attr, QString value) +{ + if ( cls == "ui" && attr == "vstembedmethod" ) + { + reloadPlugin(); + } +} + +void vestigeInstrument::reloadPlugin() +{ + closePlugin(); + loadFile( m_pluginDLL ); +} + @@ -366,10 +385,6 @@ void vestigeInstrument::closePlugin( void ) } m_pluginMutex.lock(); - if( m_plugin ) - { - delete m_plugin->pluginWidget(); - } delete m_plugin; m_plugin = NULL; m_pluginMutex.unlock(); diff --git a/plugins/vestige/vestige.h b/plugins/vestige/vestige.h index 5faa606c236..df076bbb9cb 100644 --- a/plugins/vestige/vestige.h +++ b/plugins/vestige/vestige.h @@ -74,6 +74,8 @@ class vestigeInstrument : public Instrument protected slots: void setParameter( void ); + void handleConfigChange( QString cls, QString attr, QString value ); + void reloadPlugin(); private: void closePlugin( void ); diff --git a/plugins/vst_base/VstPlugin.cpp b/plugins/vst_base/VstPlugin.cpp index 2b1cbcd0969..8b514770809 100644 --- a/plugins/vst_base/VstPlugin.cpp +++ b/plugins/vst_base/VstPlugin.cpp @@ -587,9 +587,9 @@ void VstPlugin::showUI() void VstPlugin::hideUI() { - RemotePlugin::hideUI(); if ( m_embedMethod == "none" ) { + RemotePlugin::hideUI(); } else if ( pluginWidget() != nullptr ) { diff --git a/src/core/ConfigManager.cpp b/src/core/ConfigManager.cpp index 1fb8be187b1..734c39c1f0f 100644 --- a/src/core/ConfigManager.cpp +++ b/src/core/ConfigManager.cpp @@ -352,12 +352,15 @@ void ConfigManager::setValue( const QString & cls, { if( m_settings.contains( cls ) ) { - for( stringPairVector::iterator it = m_settings[cls].begin(); - it != m_settings[cls].end(); ++it ) + for( QPair& pair : m_settings[cls]) { - if( ( *it ).first == attribute ) + if( pair.first == attribute ) { - ( *it ).second = value; + if ( pair.second != value ) + { + pair.second = value; + emit valueChanged( cls, attribute, value ); + } return; } } diff --git a/src/gui/SetupDialog.cpp b/src/gui/SetupDialog.cpp index 78b9c0d2351..0d5f2c454a5 100644 --- a/src/gui/SetupDialog.cpp +++ b/src/gui/SetupDialog.cpp @@ -1019,6 +1019,20 @@ SetupDialog::~SetupDialog() void SetupDialog::accept() { + if( m_warnAfterSetup ) + { + QMessageBox::information( NULL, tr( "Restart LMMS" ), + tr( "Please note that most changes " + "won't take effect until " + "you restart LMMS!" ), + QMessageBox::Ok ); + } + + // Hide dialog before setting values. This prevents an obscure bug + // where non-embedded VST windows would steal focus and prevent LMMS + // from taking mouse input, rendering the application unusable. + QDialog::accept(); + ConfigManager::inst()->setValue( "mixer", "framesperaudiobuffer", QString::number( m_bufferSize ) ); ConfigManager::inst()->setValue( "mixer", "audiodev", @@ -1094,16 +1108,6 @@ void SetupDialog::accept() } ConfigManager::inst()->saveConfigFile(); - - QDialog::accept(); - if( m_warnAfterSetup ) - { - QMessageBox::information( NULL, tr( "Restart LMMS" ), - tr( "Please note that most changes " - "won't take effect until " - "you restart LMMS!" ), - QMessageBox::Ok ); - } } From df3c07bbed6adb5ad3d41089a1c2222f3e10bfd9 Mon Sep 17 00:00:00 2001 From: Hyunin Song Date: Mon, 6 Nov 2017 22:48:22 +0900 Subject: [PATCH 39/53] Fix Qt4 compatibility --- plugins/VstEffect/VstEffect.cpp | 11 +++++++++++ src/gui/SetupDialog.cpp | 4 ++++ 2 files changed, 15 insertions(+) diff --git a/plugins/VstEffect/VstEffect.cpp b/plugins/VstEffect/VstEffect.cpp index 494613e26df..19d08c805b5 100644 --- a/plugins/VstEffect/VstEffect.cpp +++ b/plugins/VstEffect/VstEffect.cpp @@ -128,10 +128,21 @@ void VstEffect::openPlugin( const QString & _plugin ) PLUGIN_NAME::getIconPixmap( "logo", 24, 24 ), 0 ); QMutexLocker ml( &m_pluginMutex ); Q_UNUSED( ml ); +#if QT_VERSION > 0x050000 m_plugin.reset(new VstPlugin( _plugin )); +#else + { + QSharedPointer newPlugin(new VstPlugin( _plugin )); + std::swap(m_plugin, newPlugin); + } +#endif if( m_plugin->failed() ) { +#if QT_VERSION > 0x050000 m_plugin.reset(nullptr); +#else + m_plugin.clear(); +#endif delete tf; collectErrorForUI( VstPlugin::tr( "The VST plugin %1 could not be loaded." ).arg( _plugin ) ); return; diff --git a/src/gui/SetupDialog.cpp b/src/gui/SetupDialog.cpp index 0d5f2c454a5..090de1eb80d 100644 --- a/src/gui/SetupDialog.cpp +++ b/src/gui/SetupDialog.cpp @@ -1077,7 +1077,11 @@ void SetupDialog::accept() QString::number( m_disableAutoQuit ) ); ConfigManager::inst()->setValue( "app", "language", m_lang ); ConfigManager::inst()->setValue( "ui", "vstembedmethod", +#if QT_VERSION >= 0x050000 m_vstEmbedComboBox->currentData().toString() ); +#else + m_vstEmbedComboBox->itemData(m_vstEmbedComboBox->currentIndex()).toString() ); +#endif ConfigManager::inst()->setWorkingDir(QDir::fromNativeSeparators(m_workingDir)); From b0f64dea7f02c5f3f195f08dd197c098c55a7981 Mon Sep 17 00:00:00 2001 From: Hyunin Song Date: Mon, 6 Nov 2017 23:28:45 +0900 Subject: [PATCH 40/53] Restrict "qt" embed method to Qt5 only --- plugins/vst_base/VstPlugin.cpp | 3 +++ src/gui/SetupDialog.cpp | 3 +++ 2 files changed, 6 insertions(+) diff --git a/plugins/vst_base/VstPlugin.cpp b/plugins/vst_base/VstPlugin.cpp index 8b514770809..4a7bed403ed 100644 --- a/plugins/vst_base/VstPlugin.cpp +++ b/plugins/vst_base/VstPlugin.cpp @@ -659,6 +659,7 @@ void VstPlugin::createUI( QWidget * parent, bool isEffect ) m_pluginSubWindow = new vstSubWin( gui->mainWindow()->workspace() ); auto sw = m_pluginSubWindow.data(); +#if QT_VERSION >= 0x050100 if (m_embedMethod == "qt" ) { QWindow* vw = QWindow::fromWinId(m_pluginWindowID); @@ -668,6 +669,8 @@ void VstPlugin::createUI( QWidget * parent, bool isEffect ) // Tell remote that it is embedded // Wait for remote reply } +#endif + #ifdef LMMS_BUILD_LINUX else if (m_embedMethod == "xembed" ) { diff --git a/src/gui/SetupDialog.cpp b/src/gui/SetupDialog.cpp index 090de1eb80d..550f1d93ed8 100644 --- a/src/gui/SetupDialog.cpp +++ b/src/gui/SetupDialog.cpp @@ -343,7 +343,10 @@ SetupDialog::SetupDialog( ConfigTabs _tab_to_open ) : m_vstEmbedComboBox = new QComboBox( embed_tw ); m_vstEmbedComboBox->move( XDelta, YDelta ); m_vstEmbedComboBox->addItem( tr( "No embedding" ), "none" ); +#if QT_VERSION >= 0x050100 m_vstEmbedComboBox->addItem( tr( "Embed using Qt API" ), "qt" ); +#endif + #ifdef LMMS_BUILD_LINUX if ( QX11Info::isPlatformX11() ) { m_vstEmbedComboBox->addItem( tr( "Embed using XEmbed protocol" ), "xembed" ); From 7da7a70d60c90b9cb0754421fd4c36e24e64b370 Mon Sep 17 00:00:00 2001 From: Hyunin Song Date: Tue, 7 Nov 2017 10:47:24 +0900 Subject: [PATCH 41/53] Add Win32 embedding --- plugins/vst_base/RemoteVstPlugin.cpp | 24 +++++++---------- plugins/vst_base/VstPlugin.cpp | 39 +++++++++++++++++++++++++--- plugins/vst_base/communication.h | 2 -- src/core/ConfigManager.cpp | 4 +++ src/gui/SetupDialog.cpp | 4 +++ 5 files changed, 53 insertions(+), 20 deletions(-) diff --git a/plugins/vst_base/RemoteVstPlugin.cpp b/plugins/vst_base/RemoteVstPlugin.cpp index 3a5eeed006a..175c9f5c764 100644 --- a/plugins/vst_base/RemoteVstPlugin.cpp +++ b/plugins/vst_base/RemoteVstPlugin.cpp @@ -109,6 +109,7 @@ static VstHostLanguages hlang = LanguageEnglish; static bool EMBED = false; static bool EMBED_X11 = false; +static bool EMBED_WIN32 = false; class RemoteVstPlugin; @@ -567,17 +568,6 @@ bool RemoteVstPlugin::processMessage( const message & _m ) init( _m.getString() ); break; -// TODO: Drop Windows hack for Qt 4 -#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; @@ -2084,21 +2074,27 @@ int main( int _argc, char * * _argv ) if ( embedMethod == "none" ) { cerr << "Starting detached." << endl; - EMBED = EMBED_X11 = false; + EMBED = EMBED_X11 = EMBED_WIN32 = false; + } + else if ( embedMethod == "win32" ) + { + cerr << "Starting using Win32-native embedding." << endl; + EMBED = EMBED_WIN32 = true; EMBED_X11= false; } else if ( embedMethod == "qt" ) { cerr << "Starting using Qt-native embedding." << endl; - EMBED = true; EMBED_X11 = false; + EMBED = true; EMBED_X11 = EMBED_WIN32 = false; } else if ( embedMethod == "xembed" ) { cerr << "Starting using X11Embed protocol." << endl; - EMBED = true; EMBED_X11 = true; + EMBED = EMBED_X11 = true; EMBED_WIN32 = false; } else { cerr << "Unknown embed method " << embedMethod << ". Starting detached instead." << endl; + EMBED = EMBED_X11 = EMBED_WIN32 = false; } } diff --git a/plugins/vst_base/VstPlugin.cpp b/plugins/vst_base/VstPlugin.cpp index 4a7bed403ed..cada57293c3 100644 --- a/plugins/vst_base/VstPlugin.cpp +++ b/plugins/vst_base/VstPlugin.cpp @@ -51,6 +51,7 @@ #ifdef LMMS_BUILD_WIN32 # include +# include #endif #include "ConfigManager.h" @@ -668,19 +669,49 @@ void VstPlugin::createUI( QWidget * parent, bool isEffect ) // TODO: Synchronize show // Tell remote that it is embedded // Wait for remote reply - } + } else +#endif + +#ifdef LMMS_BUILD_WIN32 + if (m_embedMethod == "win32" ) + { + QWidget * helper = new QWidget; + QHBoxLayout * l = new QHBoxLayout( helper ); + QWidget * target = new QWidget( helper ); + l->setSpacing( 0 ); + l->setMargin( 0 ); + l->addWidget( target ); + + // we've to call that for making sure, Qt created the windows + helper->winId(); + HWND targetHandle = (HWND)target->winId(); + HWND pluginHandle = (HWND)(intptr_t)m_pluginWindowID; + + DWORD style = GetWindowLong(pluginHandle, GWL_STYLE); + style = style & ~(WS_POPUP); + style = style | WS_CHILD; + SetWindowLong(pluginHandle, GWL_STYLE, style); + SetParent(pluginHandle, targetHandle); + + DWORD threadId = GetWindowThreadProcessId(pluginHandle, NULL); + DWORD currentThreadId = GetCurrentThreadId(); + AttachThreadInput(currentThreadId, threadId, true); + + container = helper; + RemotePlugin::showUI(); + + } else #endif #ifdef LMMS_BUILD_LINUX - else if (m_embedMethod == "xembed" ) + if (m_embedMethod == "xembed" ) { QX11EmbedContainer * embedContainer = new QX11EmbedContainer( sw ); connect(embedContainer, SIGNAL(clientIsEmbedded()), this, SLOT(handleClientEmbed())); embedContainer->embedClient( m_pluginWindowID ); container = embedContainer; - } + } else #endif - else { qCritical() << "Unknown embed method" << m_embedMethod; delete m_pluginSubWindow; diff --git a/plugins/vst_base/communication.h b/plugins/vst_base/communication.h index 3e85007e711..bacf43c1edc 100644 --- a/plugins/vst_base/communication.h +++ b/plugins/vst_base/communication.h @@ -57,8 +57,6 @@ enum VstRemoteMessageIDs { // vstPlugin -> remoteVstPlugin IdVstLoadPlugin = IdUserBase, - // TODO: Drop IdVstPluginWindowInformation, Windows hack for Qt 4 - IdVstPluginWindowInformation, IdVstClosePlugin, IdVstSetTempo, IdVstSetLanguage, diff --git a/src/core/ConfigManager.cpp b/src/core/ConfigManager.cpp index 734c39c1f0f..8cbee6acfe4 100644 --- a/src/core/ConfigManager.cpp +++ b/src/core/ConfigManager.cpp @@ -199,6 +199,10 @@ QString ConfigManager::vstEmbedMethod() const } #endif +#ifdef LMMS_BUILD_WIN32 + defaultMethod = "win32"; +#endif + return value( "ui", "vstembedmethod", defaultMethod ); } diff --git a/src/gui/SetupDialog.cpp b/src/gui/SetupDialog.cpp index 550f1d93ed8..14355f894e6 100644 --- a/src/gui/SetupDialog.cpp +++ b/src/gui/SetupDialog.cpp @@ -347,6 +347,10 @@ SetupDialog::SetupDialog( ConfigTabs _tab_to_open ) : m_vstEmbedComboBox->addItem( tr( "Embed using Qt API" ), "qt" ); #endif +#ifdef LMMS_BUILD_WIN32 + m_vstEmbedComboBox->addItem( tr( "Embed using native Win32 API" ), "win32" ); +#endif + #ifdef LMMS_BUILD_LINUX if ( QX11Info::isPlatformX11() ) { m_vstEmbedComboBox->addItem( tr( "Embed using XEmbed protocol" ), "xembed" ); From 463d02e2997576ea5cca20a34f8d9dbc722f9b30 Mon Sep 17 00:00:00 2001 From: Hyunin Song Date: Wed, 8 Nov 2017 23:44:57 +0900 Subject: [PATCH 42/53] Move SC_CLOSE handling code --- plugins/vst_base/RemoteVstPlugin.cpp | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/plugins/vst_base/RemoteVstPlugin.cpp b/plugins/vst_base/RemoteVstPlugin.cpp index 175c9f5c764..f3a7ebc16b7 100644 --- a/plugins/vst_base/RemoteVstPlugin.cpp +++ b/plugins/vst_base/RemoteVstPlugin.cpp @@ -1967,13 +1967,6 @@ 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 ); } @@ -2020,6 +2013,12 @@ LRESULT CALLBACK RemoteVstPlugin::messageWndProc( HWND hwnd, UINT uMsg, break; } } + else if( uMsg == WM_SYSCOMMAND && wParam == SC_CLOSE ) + { + __plugin->destroyEditor(); + return 0; + } + return DefWindowProc( hwnd, uMsg, wParam, lParam ); } From 55ce90ec00ca82891580408e83cac23fbb9ea9d7 Mon Sep 17 00:00:00 2001 From: Hyunin Song Date: Sat, 18 Nov 2017 15:16:44 +0900 Subject: [PATCH 43/53] Try to improve embedding method logic --- include/ConfigManager.h | 1 + src/core/ConfigManager.cpp | 31 +++++++++++++++++++++++-------- src/gui/SetupDialog.cpp | 23 ++++++++++++----------- 3 files changed, 36 insertions(+), 19 deletions(-) diff --git a/include/ConfigManager.h b/include/ConfigManager.h index 3e5c0578f0b..74cb29c17fc 100644 --- a/include/ConfigManager.h +++ b/include/ConfigManager.h @@ -210,6 +210,7 @@ class EXPORT ConfigManager : public QObject return m_recentlyOpenedProjects; } + static QStringList availabeVstEmbedMethods(); QString vstEmbedMethod() const; // returns true if the working dir (e.g. ~/lmms) exists on disk diff --git a/src/core/ConfigManager.cpp b/src/core/ConfigManager.cpp index 8cbee6acfe4..4819799605a 100644 --- a/src/core/ConfigManager.cpp +++ b/src/core/ConfigManager.cpp @@ -190,19 +190,34 @@ QString ConfigManager::defaultVersion() const return LMMS_VERSION; } -QString ConfigManager::vstEmbedMethod() const +QStringList ConfigManager::availabeVstEmbedMethods() { - QString defaultMethod = "qt"; -#ifdef LMMS_BUILD_LINUX - if (QX11Info::isPlatformX11()) { - defaultMethod = "xembed"; - } + QStringList methods; + methods.append("none"); +#if QT_VERSION >= 0x050100 + methods.append("qt"); #endif - #ifdef LMMS_BUILD_WIN32 - defaultMethod = "win32"; + methods.append("win32"); +#endif +#ifdef LMMS_BUILD_LINUX +#if QT_VERSION >= 0x050000 + if (static_cast(QApplication::instance())-> + platformName() == "xcb") +#else + if (qgetenv("QT_QPA_PLATFORM").isNull() + || qgetenv("QT_QPA_PLATFORM") == "xcb") #endif + { + methods.append("xembed"); + } +#endif + return methods; +} +QString ConfigManager::vstEmbedMethod() const +{ + QString defaultMethod = *(availabeVstEmbedMethods().end() - 1); return value( "ui", "vstembedmethod", defaultMethod ); } diff --git a/src/gui/SetupDialog.cpp b/src/gui/SetupDialog.cpp index 14355f894e6..e248b40a975 100644 --- a/src/gui/SetupDialog.cpp +++ b/src/gui/SetupDialog.cpp @@ -342,20 +342,21 @@ SetupDialog::SetupDialog( ConfigTabs _tab_to_open ) : embed_tw->setFixedHeight( 48 ); m_vstEmbedComboBox = new QComboBox( embed_tw ); m_vstEmbedComboBox->move( XDelta, YDelta ); - m_vstEmbedComboBox->addItem( tr( "No embedding" ), "none" ); -#if QT_VERSION >= 0x050100 - m_vstEmbedComboBox->addItem( tr( "Embed using Qt API" ), "qt" ); -#endif -#ifdef LMMS_BUILD_WIN32 - m_vstEmbedComboBox->addItem( tr( "Embed using native Win32 API" ), "win32" ); -#endif - -#ifdef LMMS_BUILD_LINUX - if ( QX11Info::isPlatformX11() ) { + QStringList embedMethods = ConfigManager::availabeVstEmbedMethods(); + m_vstEmbedComboBox->addItem( tr( "No embedding" ), "none" ); + if( embedMethods.contains("qt") ) + { + m_vstEmbedComboBox->addItem( tr( "Embed using Qt API" ), "qt" ); + } + if( embedMethods.contains("win32") ) + { + m_vstEmbedComboBox->addItem( tr( "Embed using native Win32 API" ), "win32" ); + } + if( embedMethods.contains("xembed") ) + { m_vstEmbedComboBox->addItem( tr( "Embed using XEmbed protocol" ), "xembed" ); } -#endif m_vstEmbedComboBox->setCurrentIndex( m_vstEmbedComboBox->findData( m_vstEmbedMethod ) ); TabWidget * lang_tw = new TabWidget( tr( "LANGUAGE" ), general ); From 72e882af887f99b4cf2ea62d07f0c6d2caa3da97 Mon Sep 17 00:00:00 2001 From: Hyunin Song Date: Mon, 20 Nov 2017 15:57:29 +0900 Subject: [PATCH 44/53] Add a fallback logic for VST embedding methods --- src/core/ConfigManager.cpp | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/core/ConfigManager.cpp b/src/core/ConfigManager.cpp index 4819799605a..e75e2413e33 100644 --- a/src/core/ConfigManager.cpp +++ b/src/core/ConfigManager.cpp @@ -217,8 +217,10 @@ QStringList ConfigManager::availabeVstEmbedMethods() QString ConfigManager::vstEmbedMethod() const { - QString defaultMethod = *(availabeVstEmbedMethods().end() - 1); - return value( "ui", "vstembedmethod", defaultMethod ); + QStringList methods = availabeVstEmbedMethods(); + QString defaultMethod = *(methods.end() - 1); + QString currentMethod = value( "ui", "vstembedmethod", defaultMethod ); + return methods.contains(currentMethod) ? currentMethod : defaultMethod; } bool ConfigManager::hasWorkingDir() const From a8aa3e153f1b838ac3908dd8ea36dd4d719ea988 Mon Sep 17 00:00:00 2001 From: Lukas W Date: Fri, 24 Nov 2017 12:35:39 +0100 Subject: [PATCH 45/53] VST: Fix Qt4 compilation --- plugins/vst_base/CMakeLists.txt | 2 +- src/core/ConfigManager.cpp | 4 ---- src/gui/SetupDialog.cpp | 4 ---- 3 files changed, 1 insertion(+), 9 deletions(-) diff --git a/plugins/vst_base/CMakeLists.txt b/plugins/vst_base/CMakeLists.txt index af58ffb1a98..ed956e2181a 100644 --- a/plugins/vst_base/CMakeLists.txt +++ b/plugins/vst_base/CMakeLists.txt @@ -29,7 +29,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) -IF(LMMS_BUILD_LINUX) +IF(LMMS_BUILD_LINUX AND WANT_QT5) TARGET_LINK_LIBRARIES(vstbase qx11embedcontainer) ENDIF() diff --git a/src/core/ConfigManager.cpp b/src/core/ConfigManager.cpp index e75e2413e33..985d5e08cd7 100644 --- a/src/core/ConfigManager.cpp +++ b/src/core/ConfigManager.cpp @@ -35,10 +35,6 @@ #include "lmmsversion.h" -#ifdef LMMS_BUILD_LINUX -#include -#endif - static inline QString ensureTrailingSlash( const QString & s ) { if( ! s.isEmpty() && !s.endsWith('/') && !s.endsWith('\\') ) diff --git a/src/gui/SetupDialog.cpp b/src/gui/SetupDialog.cpp index e248b40a975..f88e1dcf6ce 100644 --- a/src/gui/SetupDialog.cpp +++ b/src/gui/SetupDialog.cpp @@ -67,10 +67,6 @@ #include "MidiApple.h" #include "MidiDummy.h" -#ifdef LMMS_BUILD_LINUX -#include -#endif - inline void labelWidget( QWidget * _w, const QString & _txt ) { QLabel * title = new QLabel( _txt, _w ); From 3d4734429728390586152810fa168e71c070800b Mon Sep 17 00:00:00 2001 From: Lukas W Date: Fri, 24 Nov 2017 12:58:44 +0100 Subject: [PATCH 46/53] Travis: Enable Linux/Qt4 builds --- .travis.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.travis.yml b/.travis.yml index 773aed79f6e..fa331f58197 100644 --- a/.travis.yml +++ b/.travis.yml @@ -12,6 +12,7 @@ matrix: - env: TARGET_OS=win64 - os: osx osx_image: xcode8.2 + - env: QT5= - env: QT5=True - env: QT5=True TARGET_OS=win32 - env: QT5=True TARGET_OS=win64 From 23cb3852acc551ef15731ac4efd863d453e0ffa6 Mon Sep 17 00:00:00 2001 From: Lukas W Date: Fri, 24 Nov 2017 15:39:53 +0100 Subject: [PATCH 47/53] VstEmbed: Remove obsolete CMake flags --- plugins/vestige/CMakeLists.txt | 7 ------- 1 file changed, 7 deletions(-) diff --git a/plugins/vestige/CMakeLists.txt b/plugins/vestige/CMakeLists.txt index 2457462f0c8..21803a92433 100644 --- a/plugins/vestige/CMakeLists.txt +++ b/plugins/vestige/CMakeLists.txt @@ -1,10 +1,3 @@ -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") From 238d6b627cec4642c45859ee92060365c83059a3 Mon Sep 17 00:00:00 2001 From: Lukas W Date: Sat, 25 Nov 2017 14:48:15 +0100 Subject: [PATCH 48/53] CMake: Fix RemoteVstPlugin build when winebuild is not in PATH --- plugins/vst_base/CMakeLists.txt | 39 +++++++++++++++++++++++++-------- 1 file changed, 30 insertions(+), 9 deletions(-) diff --git a/plugins/vst_base/CMakeLists.txt b/plugins/vst_base/CMakeLists.txt index ed956e2181a..cd2f32fbda0 100644 --- a/plugins/vst_base/CMakeLists.txt +++ b/plugins/vst_base/CMakeLists.txt @@ -48,22 +48,43 @@ 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}) +SET(WINE_CXX_ARGS + -I${CMAKE_BINARY_DIR} + -I${CMAKE_SOURCE_DIR}/include + -I${WINE_INCLUDE_BASE_DIR} + -L${WINE_LIBRARY_DIR} + ${CMAKE_CURRENT_SOURCE_DIR}/RemoteVstPlugin.cpp + -std=c++0x + -mwindows -lpthread ${EXTRA_FLAGS} -fno-omit-frame-pointer + ${WINE_BUILD_FLAGS} + -o ../RemoteVstPlugin +) + +# winegcc fails if winebuild is not in path +GET_FILENAME_COMPONENT(WINE_BINDIR ${WINE_CXX} PATH) +FIND_PROGRAM(WINEBUILD winebuild NO_CMAKE_PATH NO_CMAKE_ENVIRONMENT_PATH NO_CMAKE_SYSTEM_PATH) +IF(NOT WINEBUILD) + IF(CMAKE_VERSION VERSION_LESS 3.1) + MESSAGE(WARNING "winebuild is not in PATH. Building RemoteVstPlugin may fail.") + ELSE() + SET(WINE_CXX_ARGS -E env PATH=$ENV{PATH}:${WINE_BINDIR} ${WINE_CXX} ${WINE_CXX_ARGS}) + SET(WINE_CXX "${CMAKE_COMMAND}") + ENDIF() +ENDIF() + +set(ENV{PATH} "$ENV{PATH}:${WINE_BINDIR}") + ADD_CUSTOM_COMMAND( SOURCE "${CMAKE_CURRENT_SOURCE_DIR}/RemoteVstPlugin.cpp" + COMMAND export + ARGS "PATH=$PATH:${WINE_BINDIR}" COMMAND ${WINE_CXX} - ARGS -I${CMAKE_BINARY_DIR} - -I${CMAKE_SOURCE_DIR}/include - -I${WINE_INCLUDE_BASE_DIR} - -L${WINE_LIBRARY_DIR} - ${CMAKE_CURRENT_SOURCE_DIR}/RemoteVstPlugin.cpp - -std=c++0x - -mwindows -lpthread ${EXTRA_FLAGS} -fno-omit-frame-pointer - ${WINE_BUILD_FLAGS} - -o ../RemoteVstPlugin + ARGS ${WINE_CXX_ARGS} # Ensure correct file extension COMMAND sh -c "mv ../RemoteVstPlugin.exe ../RemoteVstPlugin || true" TARGET vstbase OUTPUTS ../RemoteVstPlugin + VERBATIM ) SET_DIRECTORY_PROPERTIES(PROPERTIES ADDITIONAL_MAKE_CLEAN_FILES ../RemoteVstPlugin.exe.so) From 8c02495f27a19612d5a1e6a997c604802deaea1f Mon Sep 17 00:00:00 2001 From: Lukas W Date: Sat, 25 Nov 2017 15:25:27 +0100 Subject: [PATCH 49/53] Qt4 compat simplified --- plugins/VstEffect/VstEffect.cpp | 13 +------------ 1 file changed, 1 insertion(+), 12 deletions(-) diff --git a/plugins/VstEffect/VstEffect.cpp b/plugins/VstEffect/VstEffect.cpp index 19d08c805b5..178aa045619 100644 --- a/plugins/VstEffect/VstEffect.cpp +++ b/plugins/VstEffect/VstEffect.cpp @@ -128,21 +128,10 @@ void VstEffect::openPlugin( const QString & _plugin ) PLUGIN_NAME::getIconPixmap( "logo", 24, 24 ), 0 ); QMutexLocker ml( &m_pluginMutex ); Q_UNUSED( ml ); -#if QT_VERSION > 0x050000 - m_plugin.reset(new VstPlugin( _plugin )); -#else - { - QSharedPointer newPlugin(new VstPlugin( _plugin )); - std::swap(m_plugin, newPlugin); - } -#endif + m_plugin = QSharedPointer(new VstPlugin( _plugin )); if( m_plugin->failed() ) { -#if QT_VERSION > 0x050000 - m_plugin.reset(nullptr); -#else m_plugin.clear(); -#endif delete tf; collectErrorForUI( VstPlugin::tr( "The VST plugin %1 could not be loaded." ).arg( _plugin ) ); return; From ae488b89cd6cc824e1e92fc3c105676be964741f Mon Sep 17 00:00:00 2001 From: Lukas W Date: Wed, 29 Nov 2017 14:44:53 +0100 Subject: [PATCH 50/53] Fix VstEffect width issue --- plugins/VstEffect/VstEffectControlDialog.cpp | 1 - 1 file changed, 1 deletion(-) diff --git a/plugins/VstEffect/VstEffectControlDialog.cpp b/plugins/VstEffect/VstEffectControlDialog.cpp index b571eb1aa16..39c00fc1d0e 100644 --- a/plugins/VstEffect/VstEffectControlDialog.cpp +++ b/plugins/VstEffect/VstEffectControlDialog.cpp @@ -78,7 +78,6 @@ VstEffectControlDialog::VstEffectControlDialog( VstEffectControls * _ctl ) : if ( m_plugin && (!embed_vst || m_pluginWidget) ) { setWindowTitle( m_plugin->name() ); - setMinimumWidth( 250 ); QPushButton * btn = new QPushButton( tr( "Show/hide" ) ); From a446775c80c70ab3e92281e3de887d6c6cf5c03f Mon Sep 17 00:00:00 2001 From: Lukas W Date: Wed, 29 Nov 2017 14:45:29 +0100 Subject: [PATCH 51/53] VST: Fix Qt-embedded VSTs not always appearing --- plugins/vst_base/VstPlugin.cpp | 19 +++++++++++++++---- plugins/vst_base/VstPlugin.h | 1 + 2 files changed, 16 insertions(+), 4 deletions(-) diff --git a/plugins/vst_base/VstPlugin.cpp b/plugins/vst_base/VstPlugin.cpp index cada57293c3..31f17196946 100644 --- a/plugins/vst_base/VstPlugin.cpp +++ b/plugins/vst_base/VstPlugin.cpp @@ -665,10 +665,7 @@ void VstPlugin::createUI( QWidget * parent, bool isEffect ) { QWindow* vw = QWindow::fromWinId(m_pluginWindowID); container = QWidget::createWindowContainer(vw, sw ); - RemotePlugin::showUI(); - // TODO: Synchronize show - // Tell remote that it is embedded - // Wait for remote reply + container->installEventFilter(this); } else #endif @@ -741,6 +738,20 @@ void VstPlugin::createUI( QWidget * parent, bool isEffect ) container->setFixedSize( m_pluginGeometry ); } +bool VstPlugin::eventFilter(QObject *obj, QEvent *event) +{ +#if QT_VERSION >= 0x050100 + if (embedMethod() == "qt" && obj == m_pluginWidget) + { + if (event->type() == QEvent::Show) { + RemotePlugin::showUI(); + } + qDebug() << obj << event; + } +#endif + return false; +} + QString VstPlugin::embedMethod() const { return m_embedMethod; diff --git a/plugins/vst_base/VstPlugin.h b/plugins/vst_base/VstPlugin.h index 4984d6849b7..f3d6bea8e83 100644 --- a/plugins/vst_base/VstPlugin.h +++ b/plugins/vst_base/VstPlugin.h @@ -106,6 +106,7 @@ class PLUGIN_EXPORT VstPlugin : public RemotePlugin, public JournallingObject void toggleUI() override; void createUI( QWidget *parent, bool isEffect ); + bool eventFilter(QObject *obj, QEvent *event); QString embedMethod() const; From 7ae348eb10f99392d589f2dd99acd7d5e458c8c3 Mon Sep 17 00:00:00 2001 From: Lukas W Date: Wed, 29 Nov 2017 15:33:44 +0100 Subject: [PATCH 52/53] Vst refactorings --- plugins/VstEffect/VstEffectControlDialog.cpp | 15 +++------------ plugins/vst_base/VstPlugin.cpp | 20 +++++++++++--------- 2 files changed, 14 insertions(+), 21 deletions(-) diff --git a/plugins/VstEffect/VstEffectControlDialog.cpp b/plugins/VstEffect/VstEffectControlDialog.cpp index 39c00fc1d0e..021fae4900b 100644 --- a/plugins/VstEffect/VstEffectControlDialog.cpp +++ b/plugins/VstEffect/VstEffectControlDialog.cpp @@ -218,14 +218,12 @@ VstEffectControlDialog::VstEffectControlDialog( VstEffectControls * _ctl ) : m_savePresetButton->setMinimumHeight( 21 ); m_savePresetButton->setMaximumHeight( 21 ); - int newSize; + int newSize = 0; if (embed_vst) { newSize = m_pluginWidget->width() + 20; - newSize = (newSize < 250) ? 250 : newSize; - } else { - newSize = 250; } + newSize = std::max(newSize, 250); QWidget* resize = new QWidget(this); resize->resize( newSize, 10 ); @@ -294,13 +292,6 @@ void VstEffectControlDialog::togglePluginUI( bool checked ) return; } - if( ConfigManager::inst()->vstEmbedMethod() != "none" ) - { - m_pluginWidget->setVisible( checked ); - } - else - { - m_plugin->toggleUI(); - } + m_plugin->toggleUI(); } diff --git a/plugins/vst_base/VstPlugin.cpp b/plugins/vst_base/VstPlugin.cpp index 31f17196946..e291e15bae8 100644 --- a/plugins/vst_base/VstPlugin.cpp +++ b/plugins/vst_base/VstPlugin.cpp @@ -361,17 +361,19 @@ void VstPlugin::setParameterDump( const QMap & _pdump ) QWidget *VstPlugin::pluginWidget(bool _top_widget) { - if ( m_embedMethod != "none" ) + if ( m_embedMethod == "none" || !m_pluginWidget ) { - if( _top_widget && m_pluginWidget ) - { - if( m_pluginWidget->parentWidget() ) - { - return m_pluginWidget->parentWidget(); - } - } + return nullptr; + } + + if ( _top_widget && m_pluginWidget->parentWidget() == m_pluginSubWindow ) + { + return m_pluginSubWindow; + } + else + { + return m_pluginWidget; } - return m_pluginWidget; } From 19f48898a705f22b063036565ac5dd67f480fabe Mon Sep 17 00:00:00 2001 From: Lukas W Date: Mon, 18 Dec 2017 12:05:29 +0100 Subject: [PATCH 53/53] VST: Disable plugin reload on embed method change --- plugins/vestige/vestige.cpp | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/plugins/vestige/vestige.cpp b/plugins/vestige/vestige.cpp index 211b363a91a..22cc804f25e 100644 --- a/plugins/vestige/vestige.cpp +++ b/plugins/vestige/vestige.cpp @@ -177,10 +177,12 @@ void vestigeInstrument::setParameter( void ) void vestigeInstrument::handleConfigChange(QString cls, QString attr, QString value) { - if ( cls == "ui" && attr == "vstembedmethod" ) - { - reloadPlugin(); - } + Q_UNUSED(cls); Q_UNUSED(attr); Q_UNUSED(value); + // Disabled for consistency with VST effects that don't implement this. (#3786) + // if ( cls == "ui" && attr == "vstembedmethod" ) + // { + // reloadPlugin(); + // } } void vestigeInstrument::reloadPlugin()