fix crash when copy'ing latent plugins
[ardour.git] / libs / fst / vstwin.c
1 #include <stdio.h>
2 #include <string.h>
3 #include <windows.h>
4
5 #define fst_error(...) fprintf(stderr, __VA_ARGS__)
6
7 #ifdef PLATFORM_WINDOWS
8
9 #include <pthread.h>
10 static UINT_PTR idle_timer_id   = 0;
11
12 #else /* linux + wine */
13
14 #include <linux/limits.h> // PATH_MAX
15 #include <winnt.h>
16 #include <wine/exception.h>
17 #include <pthread.h>
18 static int gui_quit = 0;
19 static unsigned int idle_id = 0;
20
21 #endif
22
23 #ifndef COMPILER_MSVC
24 extern char * strdup (const char *);
25 #endif
26
27 #include <glib.h>
28 #include "fst.h"
29
30 struct ERect {
31         short top;
32         short left;
33         short bottom;
34         short right;
35 };
36
37 static pthread_mutex_t  plugin_mutex;
38 static VSTState*        fst_first        = NULL; /**< Head of linked list of all FSTs */
39 static int              host_initialized = 0;
40 static const char       magic[]          =  "FST Plugin State v002";
41
42
43 static LRESULT WINAPI
44 vstedit_wndproc (HWND w, UINT msg, WPARAM wp, LPARAM lp)
45 {
46         switch (msg) {
47                 case WM_KEYUP:
48                 case WM_KEYDOWN:
49                         break;
50
51                 case WM_CLOSE:
52                         /* we don't care about windows closing ...
53                          * WM_CLOSE is used for minimizing the window.
54                          * Our window has no frame so it shouldn't ever
55                          * get sent - but if it does, we don't want our
56                          * window to get minimized!
57                          */
58                         return 0;
59                         break;
60
61                 case WM_DESTROY:
62                 case WM_NCDESTROY:
63                         /* we don't care about windows being destroyed ... */
64                         return 0;
65                         break;
66
67                 default:
68                         break;
69         }
70
71         return DefWindowProcA (w, msg, wp, lp );
72 }
73
74
75 static void
76 maybe_set_program (VSTState* fst)
77 {
78         if (fst->want_program != -1) {
79                 if (fst->vst_version >= 2) {
80                         fst->plugin->dispatcher (fst->plugin, effBeginSetProgram, 0, 0, NULL, 0);
81                 }
82
83                 fst->plugin->dispatcher (fst->plugin, effSetProgram, 0, fst->want_program, NULL, 0);
84
85                 if (fst->vst_version >= 2) {
86                         fst->plugin->dispatcher (fst->plugin, effEndSetProgram, 0, 0, NULL, 0);
87                 }
88                 fst->want_program = -1;
89         }
90
91         if (fst->want_chunk == 1) {
92                 // XXX check
93                 // 24 == audioMasterGetAutomationState,
94                 // 48 == audioMasterGetChunkFile
95                 fst->plugin->dispatcher (fst->plugin, 24 /* effSetChunk */, 1, fst->wanted_chunk_size, fst->wanted_chunk, 0);
96                 fst->want_chunk = 0;
97         }
98 }
99
100 static VOID CALLBACK
101 idle_hands(
102                 HWND hwnd,        // handle to window for timer messages
103                 UINT message,     // WM_TIMER message
104                 UINT idTimer,     // timer identifier
105                 DWORD dwTime)     // current system time
106 {
107         VSTState* fst;
108
109         pthread_mutex_lock (&plugin_mutex);
110
111         for (fst = fst_first; fst; fst = fst->next) {
112                 if (fst->gui_shown) {
113                         // this seems insane, but some plugins will not draw their meters if you don't
114                         // call this every time.  Example Ambience by Magnus @ Smartelectron:x
115                         fst->plugin->dispatcher (fst->plugin, effEditIdle, 0, 0, NULL, 0);
116
117                         if (fst->wantIdle) {
118                                 fst->wantIdle = fst->plugin->dispatcher (fst->plugin, effIdle, 0, 0, NULL, 0);
119                         }
120                 }
121
122                 pthread_mutex_lock (&fst->lock);
123 #ifndef PLATFORM_WINDOWS /* linux + wine */
124                 /* Dispatch messages to send keypresses to the plugin */
125                 int i;
126
127                 for (i = 0; i < fst->n_pending_keys; ++i) {
128                         MSG msg;
129                         /* I'm not quite sure what is going on here; it seems
130                          * `special' keys must be delivered with WM_KEYDOWN,
131                          * but that alphanumerics etc. must use WM_CHAR or
132                          * they will be ignored.  Ours is not to reason why ...
133                          */
134                         if (fst->pending_keys[i].special != 0) {
135                                 msg.message = WM_KEYDOWN;
136                                 msg.wParam = fst->pending_keys[i].special;
137                         } else {
138                                 msg.message = WM_CHAR;
139                                 msg.wParam = fst->pending_keys[i].character;
140                         }
141                         msg.hwnd = GetFocus ();
142                         msg.lParam = 0;
143                         DispatchMessageA (&msg);
144                 }
145
146                 fst->n_pending_keys = 0;
147 #endif
148
149                 /* See comment for maybe_set_program call below */
150                 maybe_set_program (fst);
151                 fst->want_program = -1;
152                 fst->want_chunk = 0;
153                 /* If we don't have an editor window yet, we still need to
154                  * set up the program, otherwise when we load a plugin without
155                  * opening its window it will sound wrong.  However, it seems
156                  * that if you don't also load the program after opening the GUI,
157                  * the GUI does not reflect the program properly.  So we'll not
158                  * mark that we've done this (ie we won't set want_program to -1)
159                  * and so it will be done again if and when the GUI arrives.
160                  */
161                 if (fst->program_set_without_editor == 0) {
162                         maybe_set_program (fst);
163                         fst->program_set_without_editor = 1;
164                 }
165
166                 pthread_mutex_unlock (&fst->lock);
167         }
168
169         pthread_mutex_unlock (&plugin_mutex);
170 }
171
172 static void
173 fst_idle_timer_add_plugin (VSTState* fst)
174 {
175         pthread_mutex_lock (&plugin_mutex);
176
177         if (fst_first == NULL) {
178                 fst_first = fst;
179         } else {
180                 VSTState* p = fst_first;
181                 while (p->next) {
182                         p = p->next;
183                 }
184                 p->next = fst;
185         }
186
187         pthread_mutex_unlock (&plugin_mutex);
188 }
189
190 static void
191 fst_idle_timer_remove_plugin (VSTState* fst)
192 {
193         VSTState* p;
194         VSTState* prev;
195
196         pthread_mutex_lock (&plugin_mutex);
197
198         for (p = fst_first, prev = NULL; p; prev = p, p = p->next) {
199                 if (p == fst) {
200                         if (prev) {
201                                 prev->next = p->next;
202                         }
203                         break;
204                 }
205                 if (!p->next) {
206                         break;
207                 }
208         }
209
210         if (fst_first == fst) {
211                 fst_first = fst_first->next;
212         }
213
214         pthread_mutex_unlock (&plugin_mutex);
215 }
216
217 static VSTState*
218 fst_new (void)
219 {
220         VSTState* fst = (VSTState*) calloc (1, sizeof (VSTState));
221         pthread_mutex_init (&fst->lock, NULL);
222         pthread_cond_init (&fst->window_status_change, NULL); // unused ?? -> TODO check gtk2ardour
223         pthread_cond_init (&fst->plugin_dispatcher_called, NULL); // unused ??
224         fst->want_program = -1;
225         fst->want_chunk = 0;
226         fst->n_pending_keys = 0;
227         fst->has_editor = 0;
228 #ifdef PLATFORM_WINDOWS
229         fst->voffset = 50;
230         fst->hoffset = 0;
231 #else /* linux + wine */
232         fst->voffset = 24;
233         fst->hoffset = 6;
234 #endif
235         fst->program_set_without_editor = 0;
236         return fst;
237 }
238
239 static void
240 fst_delete (VSTState* fst)
241 {
242         if (fst) {
243                 free((void*)fst);
244                 fst = NULL;
245         }
246 }
247
248 static VSTHandle*
249 fst_handle_new (void)
250 {
251         VSTHandle* fst = (VSTHandle*) calloc (1, sizeof (VSTHandle));
252         return fst;
253 }
254
255 #ifndef PLATFORM_WINDOWS /* linux + wine */
256 static gboolean
257 g_idle_call (gpointer ignored) {
258         if (gui_quit) return FALSE;
259         MSG msg;
260         if (PeekMessageA (&msg, NULL, 0, 0, 1)) {
261                 TranslateMessage (&msg);
262                 DispatchMessageA (&msg);
263         }
264         idle_hands(NULL, 0, 0, 0);
265         g_main_context_iteration(NULL, FALSE);
266         return gui_quit ? FALSE : TRUE;
267 }
268 #endif
269
270
271 int
272 fst_init (void* possible_hmodule)
273 {
274         if (host_initialized) return 0;
275         HMODULE hInst;
276
277         if (possible_hmodule) {
278 #ifdef PLATFORM_WINDOWS
279                 fst_error ("Error in fst_init(): (module handle is unnecessary for Win32 build)");
280                 return -1;
281 #else /* linux + wine */
282                 hInst = (HMODULE) possible_hmodule;
283 #endif
284         } else if ((hInst = GetModuleHandleA (NULL)) == NULL) {
285                 fst_error ("can't get module handle");
286                 return -1;
287         }
288
289         if (!hInst) {
290                 fst_error ("Cannot initialise VST host");
291                 return -1;
292         }
293
294         WNDCLASSEX wclass;
295
296         wclass.cbSize = sizeof(WNDCLASSEX);
297 #ifdef PLATFORM_WINDOWS
298         wclass.style = (CS_HREDRAW | CS_VREDRAW);
299         wclass.hIcon = NULL;
300         wclass.hCursor = LoadCursor(0, IDC_ARROW);
301 #else /* linux + wine */
302         wclass.style = 0;
303         wclass.hIcon = LoadIcon(hInst, "FST");
304         wclass.hCursor = LoadCursor(0, IDI_APPLICATION);
305 #endif
306         wclass.hbrBackground = (HBRUSH)GetStockObject(BLACK_BRUSH);
307         wclass.lpfnWndProc = vstedit_wndproc;
308         wclass.cbClsExtra = 0;
309         wclass.cbWndExtra = 0;
310         wclass.hInstance = hInst;
311         wclass.lpszMenuName = "MENU_FST";
312         wclass.lpszClassName = "FST";
313         wclass.hIconSm = 0;
314
315         pthread_mutex_init (&plugin_mutex, NULL);
316         host_initialized = -1;
317
318         if (!RegisterClassExA(&wclass)){
319                 fst_error ("Error in fst_init(): (class registration failed");
320                 return -1;
321         }
322         return 0;
323 }
324
325 void
326 fst_start_threading(void)
327 {
328 #ifndef PLATFORM_WINDOWS /* linux + wine */
329         if (idle_id == 0) {
330                 gui_quit = 0;
331                 idle_id = g_idle_add (g_idle_call, NULL);
332         }
333 #endif
334 }
335
336 void
337 fst_stop_threading(void) {
338 #ifndef PLATFORM_WINDOWS /* linux + wine */
339         if (idle_id != 0) {
340                 gui_quit = 1;
341                 PostQuitMessage (0);
342                 g_main_context_iteration(NULL, FALSE);
343                 //g_source_remove(idle_id);
344                 idle_id = 0;
345         }
346 #endif
347 }
348
349 void
350 fst_exit (void)
351 {
352         if (!host_initialized) return;
353         VSTState* fst;
354         // If any plugins are still open at this point, close them!
355         while ((fst = fst_first))
356                 fst_close (fst);
357
358 #ifdef PLATFORM_WINDOWS
359         if (idle_timer_id != 0) {
360                 KillTimer(NULL, idle_timer_id);
361         }
362 #else /* linux + wine */
363         if (idle_id) {
364                 gui_quit = 1;
365                 PostQuitMessage (0);
366         }
367 #endif
368
369         host_initialized = FALSE;
370         pthread_mutex_destroy (&plugin_mutex);
371 }
372
373
374 int
375 fst_run_editor (VSTState* fst, void* window_parent)
376 {
377         if (fst->windows_window == NULL) {
378                 HMODULE hInst;
379                 HWND window;
380                 struct ERect* er = NULL;
381
382                 if (!(fst->plugin->flags & effFlagsHasEditor)) {
383                         fst_error ("Plugin \"%s\" has no editor", fst->handle->name);
384                         return -1;
385                 }
386
387                 if ((hInst = GetModuleHandleA (NULL)) == NULL) {
388                         fst_error ("fst_create_editor() can't get module handle");
389                         return 1;
390                 }
391
392                 if ((window = CreateWindowExA (0, "FST", fst->handle->name,
393                                                 window_parent ? WS_CHILD : (WS_OVERLAPPEDWINDOW & ~WS_THICKFRAME & ~WS_MAXIMIZEBOX),
394                                                 CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT,
395                                                 (HWND)window_parent, NULL,
396                                                 hInst,
397                                                 NULL) ) == NULL) {
398                         fst_error ("fst_create_editor() cannot create editor window");
399                         return 1;
400                 }
401
402                 if (!SetPropA (window, "fst_ptr", fst)) {
403                         fst_error ("fst_create_editor() cannot set fst_ptr on window");
404                 }
405
406                 fst->windows_window = window;
407
408                 if (window_parent) {
409                         // This is requiredv for some reason. Note the parent is set above when the window
410                         // is created. Without this extra call the actual plugin window will draw outside
411                         // of our plugin window.
412                         SetParent((HWND)fst->windows_window, (HWND)window_parent);
413                         fst->xid = 0;
414 #ifndef PLATFORM_WINDOWS /* linux + wine */
415                 } else {
416                         SetWindowPos (fst->windows_window, 0, 9999, 9999, 2, 2, 0);
417                         ShowWindow (fst->windows_window, SW_SHOWNA);
418                         fst->xid = (int) GetPropA (fst->windows_window, "__wine_x11_whole_window");
419 #endif
420                 }
421
422                 // This is the suggested order of calls.
423                 fst->plugin->dispatcher (fst->plugin, effEditGetRect, 0, 0, &er, 0 );
424                 fst->plugin->dispatcher (fst->plugin, effEditOpen, 0, 0, fst->windows_window, 0 );
425                 fst->plugin->dispatcher (fst->plugin, effEditGetRect, 0, 0, &er, 0 );
426
427                 if (er != NULL) {
428                         fst->width = er->right - er->left;
429                         fst->height = er->bottom - er->top;
430                 }
431
432                 fst->been_activated = TRUE;
433
434         }
435
436         if (fst->windows_window) {
437 #ifdef PLATFORM_WINDOWS
438                 if (idle_timer_id == 0) {
439                         // Init the idle timer if needed, so that the main window calls us.
440                         idle_timer_id = SetTimer(NULL, idle_timer_id, 50, (TIMERPROC) idle_hands);
441                 }
442 #endif
443
444                 fst_idle_timer_add_plugin (fst);
445         }
446
447         return fst->windows_window == NULL ? -1 : 0;
448 }
449
450 void
451 fst_destroy_editor (VSTState* fst)
452 {
453         if (fst->windows_window) {
454                 fprintf (stderr, "%s destroying edit window\n", fst->handle->name);
455
456                 fst_idle_timer_remove_plugin (fst);
457                 fst->plugin->dispatcher( fst->plugin, effEditClose, 0, 0, NULL, 0.0 );
458
459                 DestroyWindow ((HWND)(fst->windows_window));
460
461                 fst->windows_window = NULL;
462         }
463
464         fst->been_activated = FALSE;
465 }
466
467 void
468 fst_move_window_into_view (VSTState* fst)
469 {
470         if (fst->windows_window) {
471 #ifdef PLATFORM_WINDOWS
472                 SetWindowPos ((HWND)(fst->windows_window), 0, fst->hoffset, fst->voffset, fst->width + fst->hoffset, fst->height + fst->voffset, 0);
473 #else /* linux + wine */
474                 SetWindowPos ((HWND)(fst->windows_window), 0, 0, 0, fst->width + fst->hoffset, fst->height + fst->voffset, 0);
475 #endif
476                 ShowWindow ((HWND)(fst->windows_window), SW_SHOWNA);
477         }
478 }
479
480 static HMODULE
481 fst_load_vst_library(const char * path)
482 {
483         char legalized_path[PATH_MAX];
484         strcpy (legalized_path, g_locale_from_utf8(path, -1, NULL, NULL, NULL));
485         return ( LoadLibraryA (legalized_path) );
486 }
487
488 VSTHandle *
489 fst_load (const char *path)
490 {
491         VSTHandle* fhandle = NULL;
492
493         if ((strlen(path)) && (NULL != (fhandle = fst_handle_new ())))
494         {
495                 char* period;
496                 fhandle->path = strdup (path);
497                 fhandle->name = g_path_get_basename(path);
498                 if ((period = strrchr (fhandle->name, '.'))) {
499                         *period = '\0';
500                 }
501
502                 // See if we can load the plugin DLL
503                 if ((fhandle->dll = (HMODULE)fst_load_vst_library (path)) == NULL) {
504                         fst_unload (&fhandle);
505                         return NULL;
506                 }
507
508                 fhandle->main_entry = (main_entry_t) GetProcAddress ((HMODULE)fhandle->dll, "main");
509
510                 if (fhandle->main_entry == 0) {
511                         if ((fhandle->main_entry = (main_entry_t) GetProcAddress ((HMODULE)fhandle->dll, "VSTPluginMain"))) {
512                                 fprintf(stderr, "VST >= 2.4 plugin '%s'\n", path);
513                                 //PBD::warning << path << _(": is a VST >= 2.4 - this plugin may or may not function correctly with this version of Ardour.") << endmsg;
514                         }
515                 }
516
517                 if (fhandle->main_entry == 0) {
518                         fst_unload (&fhandle);
519                         return NULL;
520                 }
521         }
522         return fhandle;
523 }
524
525 int
526 fst_unload (VSTHandle** fhandle)
527 {
528         if (!(*fhandle)) {
529                 return -1;
530         }
531
532         if ((*fhandle)->plugincnt) {
533                 return -1;
534         }
535
536         if ((*fhandle)->dll) {
537                 FreeLibrary ((HMODULE)(*fhandle)->dll);
538                 (*fhandle)->dll = NULL;
539         }
540
541         if ((*fhandle)->path) {
542                 free ((*fhandle)->path);
543                 (*fhandle)->path = NULL;
544         }
545
546         if ((*fhandle)->name) {
547                 free ((*fhandle)->name);
548                 (*fhandle)->name = NULL;
549         }
550
551         free (*fhandle);
552         *fhandle = NULL;
553
554         return 0;
555 }
556
557 VSTState*
558 fst_instantiate (VSTHandle* fhandle, audioMasterCallback amc, void* userptr)
559 {
560         VSTState* fst = NULL;
561
562         if( fhandle == NULL ) {
563                 fst_error( "fst_instantiate(): (the handle was NULL)\n" );
564                 return NULL;
565         }
566
567         fst = fst_new ();
568
569         if ((fst->plugin = fhandle->main_entry (amc)) == NULL)  {
570                 fst_error ("fst_instantiate: %s could not be instantiated\n", fhandle->name);
571                 free (fst);
572                 return NULL;
573         }
574
575         fst->handle = fhandle;
576         fst->plugin->user = userptr;
577
578         if (fst->plugin->magic != kEffectMagic) {
579                 fst_error ("fst_instantiate: %s is not a vst plugin\n", fhandle->name);
580                 fst_close(fst);
581                 return NULL;
582         }
583
584         fst->plugin->dispatcher (fst->plugin, effOpen, 0, 0, 0, 0);
585         fst->vst_version = fst->plugin->dispatcher (fst->plugin, effGetVstVersion, 0, 0, 0, 0);
586
587         fst->handle->plugincnt++;
588         fst->wantIdle = 0;
589
590         return fst;
591 }
592
593 void fst_audio_master_idle(void) {
594         while(g_main_context_iteration(NULL, FALSE)) ;
595 }
596
597 void
598 fst_close (VSTState* fst)
599 {
600         if (fst != NULL) {
601                 fst_destroy_editor (fst);
602
603                 if (fst->plugin) {
604                         fst->plugin->dispatcher (fst->plugin, effMainsChanged, 0, 0, NULL, 0);
605                         fst->plugin->dispatcher (fst->plugin, effClose, 0, 0, 0, 0);
606                         fst->plugin = NULL;
607                 }
608
609                 if (fst->handle) {
610                         if (fst->handle->plugincnt && --fst->handle->plugincnt == 0) {
611
612                                 fst->handle->main_entry = NULL;
613                                 fst_unload (&fst->handle); // XXX
614                         }
615                 }
616
617                 /* It might be good for this to be in it's own cleanup function
618                         since it will free the memory for the fst leaving the caller
619                         with an invalid pointer.  Caller beware */
620                 fst_delete(fst);
621         }
622 }
623
624 #if 0 // ?? who needs this, where?
625 float htonf (float v)
626 {
627         float result;
628         char * fin = (char*)&v;
629         char * fout = (char*)&result;
630         fout[0] = fin[3];
631         fout[1] = fin[2];
632         fout[2] = fin[1];
633         fout[3] = fin[0];
634         return result;
635 }
636 #endif