/* vst~ - VST plugin object for PD based on the work of Jarno Seppänen and Mark Williamson Copyright (c)2003-2005 Thomas Grill (gr@grrrr.org) For information on usage and redistribution, and for a DISCLAIMER OF ALL WARRANTIES, see the file, "license.txt," in this distribution. */ #if FLEXT_OS != FLEXT_OS_WIN // only Windows code is situated in this file #error Wrong implementation #endif #include #include class ThrCmp { public: inline bool operator()(const flext::thrid_t &a,const flext::thrid_t &b) const { if(sizeof(a) == sizeof(size_t)) return *(size_t *)&a < *(size_t *)&b; else return memcmp(&a,&b,sizeof(a)) < 0; } }; typedef std::map WndMap; static WndMap wndmap; static flext::ThrMutex mapmutex; #define TIMER_INTERVAL 25 #define WCLNAME "vst~-class" static LRESULT CALLBACK wndproc(HWND hwnd,UINT msg,WPARAM wp,LPARAM lp) { mapmutex.Lock(); VSTPlugin *plug = wndmap[flext::GetThreadId()]; mapmutex.Unlock(); FLEXT_ASSERT(plug != NULL); LRESULT res = 0; switch(msg) { case WM_CREATE: // Initialize the window. plug->StartEditing(hwnd); break; case WM_CLOSE: #ifdef FLEXT_LOGGING flext::post("WM_CLOSE"); #endif plug->StopEditing(); DestroyWindow(hwnd); break; case WM_DESTROY: #ifdef FLEXT_LOGGING flext::post("WM_DESTROY"); #endif // stop editor thread PostQuitMessage(0); break; case WM_TIMER: // fall through case WM_ENTERIDLE: plug->EditorIdle(); break; #if 0 case WM_WINDOWPOSCHANGED: { // ignore after WM_CLOSE so that x,y positions are preserved if(!plug->IsEdited()) break; WINDOWPOS *w = (WINDOWPOS *)lp; WINDOWINFO winfo; winfo.cbSize = sizeof(winfo); GetWindowInfo(hwnd,&winfo); int cpx = winfo.rcWindow.left-winfo.rcClient.left; int cpy = winfo.rcWindow.top-winfo.rcClient.top; int csx = winfo.rcWindow.right-winfo.rcClient.right-cpx; int csy = winfo.rcWindow.bottom-winfo.rcClient.bottom-cpy; // send normalized coordinates to plugin plug->SetPos(w->x+cpx,w->y+cpy,false); plug->SetSize(w->cx+csx,w->cy+csy,false); return 0; } #else case WM_MOVE: { // ignore after WM_CLOSE so that x,y positions are preserved if(!plug->IsEdited()) break; WORD wx = LOWORD(lp),wy = HIWORD(lp); short x = reinterpret_cast(wx),y = reinterpret_cast(wy); // x and y are the coordinates of the client rect (= actual VST interface) WINDOWINFO winfo; winfo.cbSize = sizeof(winfo); GetWindowInfo(hwnd,&winfo); int px = winfo.rcWindow.left-winfo.rcClient.left; int py = winfo.rcWindow.top-winfo.rcClient.top; // send normalized coordinates to plugin plug->SetPos(x+px,y+py,false); break; } case WM_SIZE: { if(!plug->IsEdited()) break; WORD wx = LOWORD(lp),wy = HIWORD(lp); short x = reinterpret_cast(wx),y = reinterpret_cast(wy); // x and y are the coordinates of the client rect (= actual VST interface) WINDOWINFO winfo; winfo.cbSize = sizeof(winfo); GetWindowInfo(hwnd,&winfo); int px = winfo.rcWindow.left-winfo.rcClient.left; int py = winfo.rcWindow.top-winfo.rcClient.top; int sx = winfo.rcWindow.right-winfo.rcClient.right-px; int sy = winfo.rcWindow.bottom-winfo.rcClient.bottom-py; // send normalized coordinates to plugin plug->SetSize(x+sx,y+sy,false); break; } #endif #if 0 // NOT needed for Windows case WM_PAINT: { // Paint the window's client area. RECT rect; GetUpdateRect(hwnd,&rect,FALSE); ERect erect; erect.left = rect.left; erect.top = rect.top; erect.right = rect.right; erect.bottom = rect.bottom; plug->Paint(erect); break; } #endif case WM_SHOWWINDOW: plug->Visible(wp != FALSE,false); break; default: #ifdef FLEXT_LOGGING // flext::post("WND MSG %i, WP=%i, lp=%i",msg,wp,lp); #endif res = DefWindowProc(hwnd,msg,wp,lp); } return res; } static void windowsize(HWND wnd,int x,int y,int w,int h) { // pre correction WINDOWINFO winfo; winfo.cbSize = sizeof(winfo); GetWindowInfo(wnd,&winfo); int sx1 = (winfo.rcWindow.right-winfo.rcClient.right)-(winfo.rcWindow.left-winfo.rcClient.left); int sy1 = (winfo.rcWindow.bottom-winfo.rcClient.bottom)-(winfo.rcWindow.top-winfo.rcClient.top); // First reflect new state in flags SetWindowPos(wnd,NULL,0,0,0,0,SWP_NOMOVE|SWP_NOSIZE|SWP_NOZORDER|SWP_FRAMECHANGED); // post correction GetWindowInfo(wnd,&winfo); int sx2 = (winfo.rcWindow.right-winfo.rcClient.right)-(winfo.rcWindow.left-winfo.rcClient.left); int sy2 = (winfo.rcWindow.bottom-winfo.rcClient.bottom)-(winfo.rcWindow.top-winfo.rcClient.top); // set pos, size and flags SetWindowPos(wnd,NULL,x,y,w+sx2-sx1,h+sy2-sy1,SWP_NOZORDER); } static void threadfun(flext::thr_params *p) { flext::RelPriority(-2); VSTPlugin *plug = (VSTPlugin *)p; HINSTANCE hinstance = (HINSTANCE)GetModuleHandle(NULL); flext::thrid_t thrid = flext::GetThreadId(); mapmutex.Lock(); wndmap[thrid] = plug; mapmutex.Unlock(); char tmp[256]; sprintf(tmp,"vst~ - %s",plug->GetName()); // Get size from plugin ERect r; plug->GetEditorRect(r); HWND wnd = CreateWindowEx( plug->GetHandle()?WS_EX_APPWINDOW:WS_EX_TOOLWINDOW, WCLNAME,tmp, WS_POPUP|WS_SYSMENU|WS_MINIMIZEBOX, // no border for the beginning to set proper coordinates plug->GetX(),plug->GetY(),r.right-r.left,r.bottom-r.top, NULL,NULL, hinstance,NULL ); if(!wnd) FLEXT_LOG1("wnd == NULL: %i",GetLastError()); else { // idle timer SetTimer(wnd,0,TIMER_INTERVAL,NULL); // set caption style CaptionEditor(plug,plug->GetCaption()); if(plug->IsVisible()) { SetForegroundWindow(wnd); ShowWindow(wnd,1); // notify plugin // plug->Dispatch(effEditTop,0,0,0,0); } else ShowWindow(wnd,0); try { // Message loop MSG msg; BOOL bRet; while( (bRet = GetMessage( &msg, NULL, 0, 0 )) != 0) { if(bRet == -1) { // handle the error and possibly exit FLEXT_LOG1("GetMessage error: %i",GetLastError()); } else { TranslateMessage(&msg); // double tm1 = flext::GetOSTime(); DispatchMessage(&msg); // double tm2 = flext::GetOSTime(); // if(tm2-tm1 > 0.01) FLEXT_LOG1("halt %lf",(tm2-tm1)*1000); } } } catch(exception &e) { flext::post("vst~ - exception caught, exiting: %s",e.what()); } catch(...) { flext::post("vst~ - exception caught, exiting"); } if(plug) plug->EditingEnded(); } mapmutex.Lock(); wndmap.erase(thrid); mapmutex.Unlock(); } void SetupEditor() { HINSTANCE hinstance = (HINSTANCE)GetModuleHandle(NULL); // Fill in the window class structure with parameters that describe the main window. WNDCLASS wcx; wcx.style = CS_DBLCLKS; // | CS_HREDRAW | CS_VREDRAW; // redraw if size changes wcx.lpfnWndProc = wndproc; // points to window procedure wcx.cbClsExtra = 0; // no extra class memory wcx.cbWndExtra = 0; // no extra window memory wcx.hInstance = hinstance; // handle to instance wcx.hIcon = NULL; //LoadIcon(NULL, IDI_APPLICATION); // predefined app. icon wcx.hCursor = LoadCursor(NULL, IDC_ARROW); // predefined arrow wcx.hbrBackground = NULL; //GetStockObject(WHITE_BRUSH); // white background brush wcx.lpszMenuName = NULL; // name of menu resource wcx.lpszClassName = WCLNAME; // name of window class ATOM at = RegisterClass(&wcx); FLEXT_ASSERT(at); } void StartEditor(VSTPlugin *p) { #ifdef FLEXT_LOGGING flext::post("Start editor 1"); #endif flext::LaunchThread(threadfun,reinterpret_cast(p)); #ifdef FLEXT_LOGGING flext::post("Start editor 2"); #endif } void StopEditor(VSTPlugin *p) { #ifdef FLEXT_LOGGING flext::post("Stop editor 1"); #endif PostMessage(p->EditorHandle(),WM_CLOSE,0,0); // flext::StopThread(threadfun,reinterpret_cast(p)); #ifdef FLEXT_LOGGING flext::post("Stop editor 2"); #endif } void ShowEditor(VSTPlugin *p,bool show) { ShowWindow(p->EditorHandle(),show); } void MoveEditor(VSTPlugin *p,int x,int y) { HWND wnd = p->EditorHandle(); SetWindowPos(wnd,NULL,x,y,0,0,SWP_NOSIZE|SWP_NOZORDER); } void SizeEditor(VSTPlugin *p,int x,int y) { HWND wnd = p->EditorHandle(); SetWindowPos(wnd,NULL,0,0,x,y,SWP_NOMOVE|SWP_NOZORDER); } void FrontEditor(VSTPlugin *p) { SetForegroundWindow(p->EditorHandle()); } void CaptionEditor(VSTPlugin *plug,bool c) { HWND wnd = plug->EditorHandle(); LONG ns,style = GetWindowLong(wnd,GWL_STYLE); if(c) ns = style|WS_BORDER|WS_CAPTION; else ns = style&~(WS_BORDER|WS_CAPTION); if(ns != style) { SetWindowLong(wnd,GWL_STYLE,ns); windowsize(wnd,plug->GetX(),plug->GetY(),plug->GetW(),plug->GetH()); } } void HandleEditor(VSTPlugin *plug,bool h) { HWND wnd = plug->EditorHandle(); bool v = plug->IsVisible(); if(v) ShowWindow(wnd,FALSE); SetWindowLong(wnd,GWL_EXSTYLE,h?WS_EX_APPWINDOW:WS_EX_TOOLWINDOW); if(v) ShowWindow(wnd,TRUE); } void TitleEditor(VSTPlugin *p,const char *t) { SetWindowText(p->EditorHandle(),t); } /* bool IsEditorShown(const VSTPlugin *p) { return IsWindowVisible(p->EditorHandle()) != FALSE; } */