Embed in a separate window
This commit is contained in:
committed by
Lukas W
parent
a81f4ca8e9
commit
878dd94e8d
@@ -414,6 +414,7 @@ private:
|
||||
enum RemoteMessageIDs
|
||||
{
|
||||
IdUndefined,
|
||||
IdHostInfoGotten,
|
||||
IdInitDone,
|
||||
IdQuit,
|
||||
IdSampleRateInformation,
|
||||
@@ -426,6 +427,7 @@ enum RemoteMessageIDs
|
||||
IdChangeOutputCount,
|
||||
IdShowUI,
|
||||
IdHideUI,
|
||||
IdToggleUI,
|
||||
IdSaveSettingsToString,
|
||||
IdSaveSettingsToFile,
|
||||
IdLoadSettingsFromString,
|
||||
@@ -769,6 +771,12 @@ public:
|
||||
|
||||
bool init( const QString &pluginExecutable, bool waitForInitDoneMsg );
|
||||
|
||||
inline void waitForHostInfoGotten()
|
||||
{
|
||||
m_failed = waitForMessage( IdHostInfoGotten ).id
|
||||
!= IdHostInfoGotten;
|
||||
}
|
||||
|
||||
inline void waitForInitDone( bool _busyWaiting = true )
|
||||
{
|
||||
m_failed = waitForMessage( IdInitDone, _busyWaiting ).id != IdInitDone;
|
||||
@@ -801,6 +809,13 @@ public:
|
||||
unlock();
|
||||
}
|
||||
|
||||
void toggleUI()
|
||||
{
|
||||
lock();
|
||||
sendMessage( IdToggleUI );
|
||||
unlock();
|
||||
}
|
||||
|
||||
inline bool failed() const
|
||||
{
|
||||
return m_failed;
|
||||
@@ -1155,6 +1170,7 @@ RemotePluginClient::RemotePluginClient( const char * socketPath ) :
|
||||
m_vstSyncData = (VstSyncData *) m_shmQtID.data();
|
||||
m_bufferSize = m_vstSyncData->m_bufferSize;
|
||||
m_sampleRate = m_vstSyncData->m_sampleRate;
|
||||
sendMessage( IdHostInfoGotten );
|
||||
return;
|
||||
}
|
||||
#else
|
||||
@@ -1182,6 +1198,7 @@ RemotePluginClient::RemotePluginClient( const char * socketPath ) :
|
||||
{
|
||||
m_bufferSize = m_vstSyncData->m_bufferSize;
|
||||
m_sampleRate = m_vstSyncData->m_sampleRate;
|
||||
sendMessage( IdHostInfoGotten );
|
||||
|
||||
// detach segment
|
||||
if( shmdt(m_vstSyncData) == -1 )
|
||||
@@ -1197,6 +1214,12 @@ RemotePluginClient::RemotePluginClient( const char * socketPath ) :
|
||||
// if attaching shared memory fails
|
||||
sendMessage( IdSampleRateInformation );
|
||||
sendMessage( IdBufferSizeInformation );
|
||||
if( waitForMessage( IdBufferSizeInformation ).id
|
||||
!= IdBufferSizeInformation )
|
||||
{
|
||||
fprintf( stderr, "Could not get buffer size information.\n" );
|
||||
}
|
||||
sendMessage( IdHostInfoGotten );
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -62,6 +62,9 @@ private:
|
||||
VstPlugin * m_plugin;
|
||||
|
||||
QLabel * tbLabel;
|
||||
|
||||
private slots:
|
||||
void togglePluginUI( bool checked );
|
||||
} ;
|
||||
|
||||
#endif
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -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)
|
||||
|
||||
82
plugins/vst_base/EmbedderApplication.h
Normal file
82
plugins/vst_base/EmbedderApplication.h
Normal file
@@ -0,0 +1,82 @@
|
||||
/*
|
||||
* EmbedderApplication.h - simple application that embeds an external window
|
||||
*
|
||||
* Copyright (c) 2016 Javier Serrano Polo <javier@jasp.net>
|
||||
*
|
||||
* 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 <QApplication>
|
||||
#include <QMainWindow>
|
||||
|
||||
#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
|
||||
@@ -52,6 +52,7 @@
|
||||
#include <sched.h>
|
||||
#endif
|
||||
|
||||
#include <sys/wait.h>
|
||||
#include <wine/exception.h>
|
||||
|
||||
#endif
|
||||
@@ -106,6 +107,12 @@ struct ERect
|
||||
#include <sys/shm.h>
|
||||
#endif
|
||||
|
||||
#define EMBEDDER_NAME "embed-window"
|
||||
|
||||
#ifdef LMMS_BUILD_LINUX
|
||||
#define USE_LINUX_EMBEDDER
|
||||
#endif
|
||||
|
||||
static VstHostLanguages hlang = LanguageEnglish;
|
||||
|
||||
|
||||
@@ -115,6 +122,10 @@ RemoteVstPlugin * __plugin = NULL;
|
||||
|
||||
DWORD __GuiThreadID = 0;
|
||||
|
||||
#ifdef USE_LINUX_EMBEDDER
|
||||
static char * s_embedderPath;
|
||||
#endif
|
||||
|
||||
|
||||
|
||||
class RemoteVstPlugin : public RemotePluginClient
|
||||
@@ -131,6 +142,7 @@ public:
|
||||
|
||||
void init( const std::string & _plugin_file );
|
||||
void initEditor();
|
||||
void destroyEditor();
|
||||
|
||||
virtual void process( const sampleFrame * _in, sampleFrame * _out );
|
||||
|
||||
@@ -299,8 +311,16 @@ private:
|
||||
intptr_t m_windowID;
|
||||
int m_windowWidth;
|
||||
int m_windowHeight;
|
||||
#ifdef USE_LINUX_EMBEDDER
|
||||
pid_t m_embedderPid;
|
||||
int m_embedderStdin;
|
||||
int m_embedderStdout;
|
||||
#else
|
||||
PROCESS_INFORMATION m_processInfo;
|
||||
#endif
|
||||
|
||||
bool m_initialized;
|
||||
bool m_registeredWindowClass;
|
||||
|
||||
pthread_mutex_t m_pluginLock;
|
||||
|
||||
@@ -347,6 +367,7 @@ RemoteVstPlugin::RemoteVstPlugin( const char * socketPath ) :
|
||||
m_windowWidth( 0 ),
|
||||
m_windowHeight( 0 ),
|
||||
m_initialized( false ),
|
||||
m_registeredWindowClass( false ),
|
||||
m_pluginLock(),
|
||||
m_inputs( NULL ),
|
||||
m_outputs( NULL ),
|
||||
@@ -357,7 +378,6 @@ RemoteVstPlugin::RemoteVstPlugin( const char * socketPath ) :
|
||||
m_in( NULL ),
|
||||
m_shmID( -1 ),
|
||||
m_vstSyncData( NULL )
|
||||
|
||||
{
|
||||
pthread_mutex_init( &m_pluginLock, NULL );
|
||||
|
||||
@@ -423,14 +443,7 @@ RemoteVstPlugin::RemoteVstPlugin( const char * socketPath ) :
|
||||
|
||||
RemoteVstPlugin::~RemoteVstPlugin()
|
||||
{
|
||||
if( m_window != NULL )
|
||||
{
|
||||
pluginDispatch( effEditClose );
|
||||
#ifdef LMMS_BUILD_LINUX
|
||||
CloseWindow( m_window );
|
||||
#endif
|
||||
m_window = NULL;
|
||||
}
|
||||
destroyEditor();
|
||||
pluginDispatch( effMainsChanged, 0, 0 );
|
||||
pluginDispatch( effClose );
|
||||
#ifndef USE_QT_SHMEM
|
||||
@@ -464,24 +477,103 @@ RemoteVstPlugin::~RemoteVstPlugin()
|
||||
|
||||
|
||||
|
||||
#ifdef USE_LINUX_EMBEDDER
|
||||
static void checkExitStatus( int status )
|
||||
{
|
||||
if( WIFEXITED( status ) && WEXITSTATUS( status ) == EXIT_SUCCESS )
|
||||
{
|
||||
return;
|
||||
}
|
||||
fprintf( stderr, "Child process did not exit properly\n" );
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
|
||||
|
||||
bool RemoteVstPlugin::processMessage( const message & _m )
|
||||
{
|
||||
switch( _m.id )
|
||||
{
|
||||
case IdShowUI:
|
||||
#ifdef USE_LINUX_EMBEDDER
|
||||
if( m_window )
|
||||
{
|
||||
int status;
|
||||
pid_t pid = waitpid( m_embedderPid, &status,
|
||||
WNOHANG );
|
||||
switch( pid )
|
||||
{
|
||||
case -1:
|
||||
perror( "waitpid" );
|
||||
break;
|
||||
|
||||
case 0:
|
||||
break;
|
||||
|
||||
default:
|
||||
checkExitStatus( status );
|
||||
m_embedderPid = -1;
|
||||
destroyEditor();
|
||||
}
|
||||
}
|
||||
#endif
|
||||
initEditor();
|
||||
break;
|
||||
|
||||
case IdHideUI:
|
||||
destroyEditor();
|
||||
break;
|
||||
|
||||
case IdToggleUI:
|
||||
#ifdef USE_LINUX_EMBEDDER
|
||||
if( m_window )
|
||||
{
|
||||
bool restart = false;
|
||||
int status;
|
||||
pid_t pid = waitpid( m_embedderPid, &status,
|
||||
WNOHANG );
|
||||
switch( pid )
|
||||
{
|
||||
case -1:
|
||||
perror( "waitpid" );
|
||||
break;
|
||||
|
||||
case 0:
|
||||
break;
|
||||
|
||||
default:
|
||||
checkExitStatus( status );
|
||||
m_embedderPid = -1;
|
||||
restart = true;
|
||||
}
|
||||
destroyEditor();
|
||||
|
||||
if( !restart )
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
initEditor();
|
||||
break;
|
||||
#else
|
||||
// Temporary implementation, not using an embedder
|
||||
if( m_window )
|
||||
{
|
||||
destroyEditor();
|
||||
}
|
||||
else
|
||||
{
|
||||
initEditor();
|
||||
}
|
||||
break;
|
||||
#endif
|
||||
|
||||
case IdVstLoadPlugin:
|
||||
init( _m.getString() );
|
||||
break;
|
||||
|
||||
#ifdef LMMS_BUILD_WIN32
|
||||
case IdVstPluginWindowInformation:
|
||||
{
|
||||
HWND top = FindWindowEx( NULL, NULL, NULL,
|
||||
_m.getString().c_str() );
|
||||
m_window = FindWindowEx( top, NULL, NULL, NULL );
|
||||
break;
|
||||
}
|
||||
#endif
|
||||
|
||||
case IdVstSetTempo:
|
||||
setBPM( _m.getInt() );
|
||||
break;
|
||||
@@ -619,9 +711,32 @@ void RemoteVstPlugin::init( const std::string & _plugin_file )
|
||||
|
||||
|
||||
|
||||
static void assert_dup2( int oldfd, int newfd )
|
||||
{
|
||||
if( dup2( oldfd, newfd ) == -1 )
|
||||
{
|
||||
perror( "dup2" );
|
||||
exit( EXIT_FAILURE );
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
static void close_check( int fd )
|
||||
{
|
||||
if( close( fd ) )
|
||||
{
|
||||
perror( "close" );
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
void RemoteVstPlugin::initEditor()
|
||||
{
|
||||
if( !( m_plugin->flags & effFlagsHasEditor ) )
|
||||
if( m_window || !( m_plugin->flags & effFlagsHasEditor ) )
|
||||
{
|
||||
return;
|
||||
}
|
||||
@@ -635,45 +750,40 @@ void RemoteVstPlugin::initEditor()
|
||||
}
|
||||
|
||||
|
||||
WNDCLASS wc;
|
||||
wc.style = CS_HREDRAW | CS_VREDRAW;
|
||||
wc.lpfnWndProc = DefWindowProc;
|
||||
wc.cbClsExtra = 0;
|
||||
wc.cbWndExtra = 0;
|
||||
wc.hInstance = hInst;
|
||||
wc.hIcon = LoadIcon( NULL, IDI_APPLICATION );
|
||||
wc.hCursor = LoadCursor( NULL, IDC_ARROW );
|
||||
wc.hbrBackground = (HBRUSH) GetStockObject( BLACK_BRUSH );
|
||||
wc.lpszMenuName = NULL;
|
||||
wc.lpszClassName = "LVSL";
|
||||
|
||||
if( !RegisterClass( &wc ) )
|
||||
if( !m_registeredWindowClass )
|
||||
{
|
||||
return;
|
||||
WNDCLASS wc;
|
||||
wc.style = CS_HREDRAW | CS_VREDRAW;
|
||||
wc.lpfnWndProc = DefWindowProc;
|
||||
wc.cbClsExtra = 0;
|
||||
wc.cbWndExtra = 0;
|
||||
wc.hInstance = hInst;
|
||||
wc.hIcon = LoadIcon( NULL, IDI_APPLICATION );
|
||||
wc.hCursor = LoadCursor( NULL, IDC_ARROW );
|
||||
wc.hbrBackground = (HBRUSH) GetStockObject( BLACK_BRUSH );
|
||||
wc.lpszMenuName = NULL;
|
||||
wc.lpszClassName = "LVSL";
|
||||
|
||||
if( !RegisterClass( &wc ) )
|
||||
{
|
||||
return;
|
||||
}
|
||||
m_registeredWindowClass = true;
|
||||
}
|
||||
|
||||
#ifdef LMMS_BUILD_LINUX
|
||||
//m_window = CreateWindowEx( 0, "LVSL", m_shortName.c_str(),
|
||||
// ( WS_OVERLAPPEDWINDOW | WS_THICKFRAME ) & ~WS_MAXIMIZEBOX,
|
||||
// 0, 0, 10, 10, NULL, NULL, hInst, NULL );
|
||||
|
||||
m_window = CreateWindowEx( 0 , "LVSL", m_shortName.c_str(),
|
||||
WS_POPUP | WS_SYSMENU | WS_BORDER , 0, 0, 10, 10, NULL, NULL, hInst, NULL);
|
||||
#else
|
||||
m_windowID = 1; // arbitrary value on win32 to signal
|
||||
// vstPlugin-class that we have an editor
|
||||
|
||||
m_window = CreateWindowEx( 0, "LVSL", m_shortName.c_str(),
|
||||
WS_CHILD, 0, 0, 10, 10,
|
||||
m_window, NULL, hInst, NULL );
|
||||
#if QT_VERSION < 0x050000 && defined( LMMS_BUILD_LINUX )
|
||||
WS_POPUP | WS_SYSMENU | WS_BORDER,
|
||||
#else
|
||||
( WS_OVERLAPPEDWINDOW | WS_THICKFRAME ) & ~WS_MAXIMIZEBOX,
|
||||
#endif
|
||||
0, 0, 10, 10, NULL, NULL, hInst, NULL );
|
||||
if( m_window == NULL )
|
||||
{
|
||||
debugMessage( "initEditor(): cannot create editor window\n" );
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
pluginDispatch( effEditOpen, 0, 0, m_window );
|
||||
|
||||
ERect * er;
|
||||
@@ -682,17 +792,202 @@ void RemoteVstPlugin::initEditor()
|
||||
m_windowWidth = er->right - er->left;
|
||||
m_windowHeight = er->bottom - er->top;
|
||||
|
||||
#ifdef USE_LINUX_EMBEDDER
|
||||
m_windowID = (intptr_t) GetProp( m_window, "__wine_x11_whole_window" );
|
||||
|
||||
m_embedderPid = -1;
|
||||
m_embedderStdin = -1;
|
||||
m_embedderStdout = -1;
|
||||
|
||||
int infd[2];
|
||||
int outfd[2];
|
||||
if( pipe( infd ) )
|
||||
{
|
||||
perror( "pipe" );
|
||||
destroyEditor();
|
||||
return;
|
||||
}
|
||||
if( pipe( outfd ) )
|
||||
{
|
||||
perror( "pipe" );
|
||||
close_check( infd[0] );
|
||||
close_check( infd[1] );
|
||||
destroyEditor();
|
||||
return;
|
||||
}
|
||||
|
||||
m_embedderPid = fork();
|
||||
switch ( m_embedderPid )
|
||||
{
|
||||
case -1:
|
||||
perror( "fork" );
|
||||
close_check( infd[0] );
|
||||
close_check( infd[1] );
|
||||
close_check( outfd[0] );
|
||||
close_check( outfd[1] );
|
||||
destroyEditor();
|
||||
return;
|
||||
|
||||
case 0:
|
||||
assert_dup2( infd[0], STDIN_FILENO );
|
||||
assert_dup2( outfd[1], STDOUT_FILENO );
|
||||
|
||||
close_check( infd[0] );
|
||||
close_check( infd[1] );
|
||||
close_check( outfd[0] );
|
||||
close_check( outfd[1] );
|
||||
|
||||
char * widStr = new char[2 * sizeof m_windowID + 1];
|
||||
sprintf( widStr, "%x", m_windowID );
|
||||
char * widthStr
|
||||
= new char[2 * sizeof m_windowWidth + 1];
|
||||
sprintf( widthStr, "%x", m_windowWidth );
|
||||
char * heightStr
|
||||
= new char[2 * sizeof m_windowHeight + 1];
|
||||
sprintf( heightStr, "%x", m_windowHeight );
|
||||
execl( s_embedderPath, s_embedderPath,
|
||||
m_shortName.c_str(), widStr, widthStr,
|
||||
heightStr, (char *) NULL );
|
||||
perror( "execl" );
|
||||
exit( EXIT_FAILURE );
|
||||
}
|
||||
|
||||
close_check( infd[0] );
|
||||
close_check( outfd[1] );
|
||||
m_embedderStdin = infd[1];
|
||||
m_embedderStdout = outfd[0];
|
||||
#else
|
||||
// Pending: Check that Qt 5 embedding works on Windows
|
||||
// Should wait until Qt 4 support is dropped
|
||||
|
||||
// TODO: Set to native window ID
|
||||
m_windowID = 1;
|
||||
|
||||
const char * name = m_shortName.c_str();
|
||||
char * commandLine = new char[sizeof EMBEDDER_NAME " " + strlen( name )
|
||||
+ 1 + 2 * sizeof m_windowID + 1 + 2 * sizeof m_windowWidth + 1
|
||||
+ 2 * sizeof m_windowHeight];
|
||||
sprintf( commandLine, EMBEDDER_NAME " %s %x %x %x", name, m_windowID,
|
||||
m_windowWidth, m_windowHeight );
|
||||
|
||||
// Set up the pipes
|
||||
/*
|
||||
SECURITY_ATTRIBUTES saAttr;
|
||||
|
||||
saAttr.nLength = sizeof(SECURITY_ATTRIBUTES);
|
||||
saAttr.bInheritHandle = TRUE;
|
||||
saAttr.lpSecurityDescriptor = NULL;
|
||||
|
||||
if ( !CreatePipe(&g_hChildStd_OUT_Rd, &g_hChildStd_OUT_Wr, &saAttr, 0) )
|
||||
ErrorExit(TEXT("StdoutRd CreatePipe"));
|
||||
|
||||
if ( !SetHandleInformation(g_hChildStd_OUT_Rd, HANDLE_FLAG_INHERIT, 0) )
|
||||
ErrorExit(TEXT("Stdout SetHandleInformation"));
|
||||
|
||||
if (! CreatePipe(&g_hChildStd_IN_Rd, &g_hChildStd_IN_Wr, &saAttr, 0))
|
||||
ErrorExit(TEXT("Stdin CreatePipe"));
|
||||
|
||||
if ( ! SetHandleInformation(g_hChildStd_IN_Wr, HANDLE_FLAG_INHERIT, 0) )
|
||||
ErrorExit(TEXT("Stdin SetHandleInformation"));
|
||||
*/
|
||||
|
||||
STARTUPINFO si;
|
||||
ZeroMemory( &si, sizeof si );
|
||||
si.cb = sizeof si;
|
||||
// si.hStdOutput = g_hChildStd_OUT_Wr;
|
||||
// si.hStdInput = g_hChildStd_IN_Rd;
|
||||
// si.dwFlags |= STARTF_USESTDHANDLES;
|
||||
ZeroMemory( &m_processInfo, sizeof m_processInfo );
|
||||
|
||||
// Pending: Create new process when the embedder is ready
|
||||
// bool ok = CreateProcess( NULL, commandLine, NULL, NULL, TRUE, 0, NULL,
|
||||
// NULL, &si, &m_processInfo );
|
||||
delete[] commandLine;
|
||||
/*
|
||||
if ( !ok )
|
||||
{
|
||||
fprintf( stderr, "CreateProcess failed (%d)\n",
|
||||
GetLastError() );
|
||||
destroyEditor();
|
||||
return;
|
||||
}
|
||||
*/
|
||||
// close unused pipe handles if possible
|
||||
// if ( ! CloseHandle(g_hChildStd_IN_Rd) ) error
|
||||
#endif
|
||||
|
||||
|
||||
SetWindowPos( m_window, 0, 0, 0, m_windowWidth + 8,
|
||||
m_windowHeight + 26, SWP_NOACTIVATE |
|
||||
SWP_NOMOVE | SWP_NOZORDER );
|
||||
pluginDispatch( effEditTop );
|
||||
|
||||
ShowWindow( m_window, SW_SHOWNORMAL );
|
||||
UpdateWindow( m_window );
|
||||
|
||||
#ifdef LMMS_BUILD_LINUX
|
||||
m_windowID = (intptr_t) GetProp( m_window, "__wine_x11_whole_window" );
|
||||
#ifdef USE_LINUX_EMBEDDER
|
||||
char c;
|
||||
if( ::read( m_embedderStdout, &c, 1 ) != 1 )
|
||||
{
|
||||
fprintf( stderr, "Could not read from embedder\n" );
|
||||
destroyEditor();
|
||||
return;
|
||||
}
|
||||
#else
|
||||
// bSuccess = ReadFile(g_hChildStd_OUT_Rd, chBuf, BUFSIZE, &dwRead, NULL);
|
||||
// if ( ! bSuccess || dwRead == 0 ) break;
|
||||
#endif
|
||||
ShowWindow( m_window, SW_SHOWNORMAL );
|
||||
#ifdef USE_LINUX_EMBEDDER
|
||||
if( ::write( m_embedderStdin, "", 1 ) != 1 )
|
||||
{
|
||||
fprintf( stderr, "Could not write to embedder\n" );
|
||||
destroyEditor();
|
||||
return;
|
||||
}
|
||||
#else
|
||||
// bSuccess = WriteFile(g_hChildStd_IN_Wr, chBuf, dwRead, &dwWritten, NULL);
|
||||
// if ( ! bSuccess ) break;
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
void RemoteVstPlugin::destroyEditor()
|
||||
{
|
||||
if( m_window == NULL )
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
#ifdef USE_LINUX_EMBEDDER
|
||||
close_check( m_embedderStdin );
|
||||
close_check( m_embedderStdout );
|
||||
if( m_embedderPid != -1 )
|
||||
{
|
||||
int status;
|
||||
pid_t pid = waitpid( m_embedderPid, &status, 0 );
|
||||
if( pid == -1 )
|
||||
{
|
||||
perror( "waitpid" );
|
||||
}
|
||||
else
|
||||
{
|
||||
checkExitStatus( status );
|
||||
}
|
||||
}
|
||||
#else
|
||||
// Close pipes
|
||||
// Wait until child process exits.
|
||||
// WaitForSingleObject( m_processInfo.hProcess, INFINITE );
|
||||
|
||||
// Close process and thread handles.
|
||||
// CloseHandle( m_processInfo.hProcess );
|
||||
// CloseHandle( m_processInfo.hThread );
|
||||
#endif
|
||||
|
||||
pluginDispatch( effEditClose );
|
||||
// Destroying the window takes some time in Wine 1.8.5
|
||||
DestroyWindow( m_window );
|
||||
m_window = NULL;
|
||||
}
|
||||
|
||||
|
||||
@@ -989,7 +1284,7 @@ void RemoteVstPlugin::saveChunkToFile( const std::string & _file )
|
||||
fprintf( stderr,
|
||||
"Error saving chunk to file.\n" );
|
||||
}
|
||||
close( fd );
|
||||
close_check( fd );
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1327,7 +1622,7 @@ void RemoteVstPlugin::loadChunkFromFile( const std::string & _file, int _len )
|
||||
{
|
||||
fprintf( stderr, "Error loading chunk from file.\n" );
|
||||
}
|
||||
close( fd );
|
||||
close_check( fd );
|
||||
pluginDispatch( 24, 0, _len, chunk );
|
||||
|
||||
delete[] buf;
|
||||
@@ -1807,6 +2102,13 @@ DWORD WINAPI RemoteVstPlugin::guiEventLoop( LPVOID _param )
|
||||
while( quit == false && GetMessage( &msg, NULL, 0, 0 ) )
|
||||
{
|
||||
TranslateMessage( &msg );
|
||||
|
||||
if( msg.message == WM_SYSCOMMAND && msg.wParam == SC_CLOSE )
|
||||
{
|
||||
_this->destroyEditor();
|
||||
continue;
|
||||
}
|
||||
|
||||
DispatchMessage( &msg );
|
||||
|
||||
if( msg.message == WM_TIMER && _this->isInitialized() )
|
||||
@@ -1846,6 +2148,63 @@ DWORD WINAPI RemoteVstPlugin::guiEventLoop( LPVOID _param )
|
||||
|
||||
|
||||
|
||||
#ifdef USE_LINUX_EMBEDDER
|
||||
static char * findMyPath( const char * argv0 )
|
||||
{
|
||||
// TODO: escape ' or use execlp
|
||||
std::string command = "winepath '";
|
||||
command += argv0;
|
||||
command += '\'';
|
||||
FILE * stream = popen( command.c_str(), "r" );
|
||||
if( !stream )
|
||||
{
|
||||
perror( "popen" );
|
||||
return NULL;
|
||||
}
|
||||
char * unixPath = (char *) malloc( BUFSIZ );
|
||||
char * s = fgets( unixPath, BUFSIZ, stream );
|
||||
if( !s )
|
||||
{
|
||||
perror( "fgets" );
|
||||
free( unixPath );
|
||||
return NULL;
|
||||
}
|
||||
if( strlen( s ) == BUFSIZ - 1 )
|
||||
{
|
||||
//TODO: Read a longer line
|
||||
fprintf( stderr, "findMyPath: Buffer too small\n" );
|
||||
}
|
||||
char * eol = strchr( unixPath, '\n' );
|
||||
if( eol )
|
||||
{
|
||||
*eol = '\0';
|
||||
}
|
||||
if( pclose( stream ) == -1 )
|
||||
{
|
||||
perror( "pclose" );
|
||||
}
|
||||
return unixPath;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
static char * findEmbedderPath( const char * argv0 )
|
||||
{
|
||||
char * myPath = findMyPath( argv0 );
|
||||
const char * slash = myPath ? strrchr( myPath, '/' ) : NULL;
|
||||
size_t prefixLength = slash ? slash - myPath + 1 : 0;
|
||||
char * path = new char[prefixLength + sizeof EMBEDDER_NAME];
|
||||
memcpy( path, myPath, prefixLength );
|
||||
memcpy( path + prefixLength, EMBEDDER_NAME, sizeof EMBEDDER_NAME );
|
||||
free( myPath );
|
||||
return path;
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
|
||||
|
||||
int main( int _argc, char * * _argv )
|
||||
{
|
||||
#ifdef SYNC_WITH_SHM_FIFO
|
||||
@@ -1883,6 +2242,10 @@ int main( int _argc, char * * _argv )
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef USE_LINUX_EMBEDDER
|
||||
s_embedderPath = findEmbedderPath( _argv[0] );
|
||||
#endif
|
||||
|
||||
// constructor automatically will process messages until it receives
|
||||
// a IdVstLoadPlugin message and processes it
|
||||
#ifdef SYNC_WITH_SHM_FIFO
|
||||
@@ -1907,6 +2270,10 @@ int main( int _argc, char * * _argv )
|
||||
|
||||
delete __plugin;
|
||||
|
||||
#ifdef USE_LINUX_EMBEDDER
|
||||
delete[] s_embedderPath;
|
||||
#endif
|
||||
|
||||
|
||||
#ifdef LMMS_BUILD_WIN32
|
||||
#ifndef __WINPTHREADS_VERSION
|
||||
|
||||
@@ -31,16 +31,6 @@
|
||||
#include <QCloseEvent>
|
||||
#include <QMdiArea>
|
||||
#include <QMdiSubWindow>
|
||||
#ifdef LMMS_BUILD_LINUX
|
||||
#if QT_VERSION < 0x050000
|
||||
#include <QX11EmbedContainer>
|
||||
#include <QX11Info>
|
||||
#else
|
||||
#include <QWindow>
|
||||
#endif
|
||||
#else
|
||||
#include <QLayout>
|
||||
#endif
|
||||
#include <QDomDocument>
|
||||
|
||||
#ifdef LMMS_BUILD_WIN32
|
||||
@@ -54,32 +44,6 @@
|
||||
#include "Song.h"
|
||||
#include "templates.h"
|
||||
#include "FileDialog.h"
|
||||
#include <QLayout>
|
||||
|
||||
|
||||
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() );
|
||||
|
||||
@@ -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,
|
||||
|
||||
151
plugins/vst_base/embed-window.cpp
Normal file
151
plugins/vst_base/embed-window.cpp
Normal file
@@ -0,0 +1,151 @@
|
||||
/*
|
||||
* embed-window.cpp - simple application that embeds an external window
|
||||
*
|
||||
* Copyright (c) 2016 Javier Serrano Polo <javier@jasp.net>
|
||||
*
|
||||
* 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 <QSocketNotifier>
|
||||
#include <QTimer>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#if QT_VERSION < 0x050000
|
||||
#include <QX11EmbedContainer>
|
||||
#include <QX11Info>
|
||||
#include <X11/Xlib.h>
|
||||
#else
|
||||
#include <QWindow>
|
||||
#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;
|
||||
}
|
||||
Reference in New Issue
Block a user