When building with MSVC, guard a declaration of 'strdup()' which conflicts slightly...
[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;
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                 fst->width =  er->right-er->left;
428                 fst->height =  er->bottom-er->top;
429
430
431                 fst->been_activated = TRUE;
432
433         }
434
435         if (fst->windows_window) {
436 #ifdef PLATFORM_WINDOWS
437                 if (idle_timer_id == 0) {
438                         // Init the idle timer if needed, so that the main window calls us.
439                         idle_timer_id = SetTimer(NULL, idle_timer_id, 50, (TIMERPROC) idle_hands);
440                 }
441 #endif
442
443                 fst_idle_timer_add_plugin (fst);
444         }
445
446         return fst->windows_window == NULL ? -1 : 0;
447 }
448
449 void
450 fst_destroy_editor (VSTState* fst)
451 {
452         if (fst->windows_window) {
453                 fprintf (stderr, "%s destroying edit window\n", fst->handle->name);
454
455                 fst_idle_timer_remove_plugin (fst);
456                 fst->plugin->dispatcher( fst->plugin, effEditClose, 0, 0, NULL, 0.0 );
457
458                 DestroyWindow ((HWND)(fst->windows_window));
459
460                 fst->windows_window = NULL;
461         }
462
463         fst->been_activated = FALSE;
464 }
465
466 void
467 fst_move_window_into_view (VSTState* fst)
468 {
469         if (fst->windows_window) {
470 #ifdef PLATFORM_WINDOWS
471                 SetWindowPos ((HWND)(fst->windows_window), 0, fst->hoffset, fst->voffset, fst->width + fst->hoffset, fst->height + fst->voffset, 0);
472 #else /* linux + wine */
473                 SetWindowPos ((HWND)(fst->windows_window), 0, 0, 0, fst->width + fst->hoffset, fst->height + fst->voffset, 0);
474 #endif
475                 ShowWindow ((HWND)(fst->windows_window), SW_SHOWNA);
476         }
477 }
478
479 static HMODULE
480 fst_load_vst_library(const char * path)
481 {
482         char legalized_path[PATH_MAX];
483         strcpy (legalized_path, g_locale_from_utf8(path, -1, NULL, NULL, NULL));
484         return ( LoadLibraryA (legalized_path) );
485 }
486
487 VSTHandle *
488 fst_load (const char *path)
489 {
490         VSTHandle* fhandle = NULL;
491
492         if ((strlen(path)) && (NULL != (fhandle = fst_handle_new ())))
493         {
494                 char* period;
495                 fhandle->path = strdup (path);
496                 fhandle->name = g_path_get_basename(path);
497                 if ((period = strrchr (fhandle->name, '.'))) {
498                         *period = '\0';
499                 }
500
501                 // See if we can load the plugin DLL
502                 if ((fhandle->dll = (HMODULE)fst_load_vst_library (path)) == NULL) {
503                         fst_unload (&fhandle);
504                         return NULL;
505                 }
506
507                 fhandle->main_entry = (main_entry_t) GetProcAddress ((HMODULE)fhandle->dll, "main");
508
509                 if (fhandle->main_entry == 0) {
510                         if ((fhandle->main_entry = (main_entry_t) GetProcAddress ((HMODULE)fhandle->dll, "VSTPluginMain"))) {
511                                 fprintf(stderr, "VST >= 2.4 plugin '%s'\n", path);
512                                 //PBD::warning << path << _(": is a VST >= 2.4 - this plugin may or may not function correctly with this version of Ardour.") << endmsg;
513                         }
514                 }
515
516                 if (fhandle->main_entry == 0) {
517                         fst_unload (&fhandle);
518                         return NULL;
519                 }
520         }
521         return fhandle;
522 }
523
524 int
525 fst_unload (VSTHandle** fhandle)
526 {
527         if (!(*fhandle)) {
528                 return -1;
529         }
530
531         if ((*fhandle)->plugincnt) {
532                 return -1;
533         }
534
535         if ((*fhandle)->dll) {
536                 FreeLibrary ((HMODULE)(*fhandle)->dll);
537                 (*fhandle)->dll = NULL;
538         }
539
540         if ((*fhandle)->path) {
541                 free ((*fhandle)->path);
542                 (*fhandle)->path = NULL;
543         }
544
545         if ((*fhandle)->name) {
546                 free ((*fhandle)->name);
547                 (*fhandle)->name = NULL;
548         }
549
550         free (*fhandle);
551         *fhandle = NULL;
552
553         return 0;
554 }
555
556 VSTState*
557 fst_instantiate (VSTHandle* fhandle, audioMasterCallback amc, void* userptr)
558 {
559         VSTState* fst = NULL;
560
561         if( fhandle == NULL ) {
562                 fst_error( "fst_instantiate(): (the handle was NULL)\n" );
563                 return NULL;
564         }
565
566         fst = fst_new ();
567
568         if ((fst->plugin = fhandle->main_entry (amc)) == NULL)  {
569                 fst_error ("fst_instantiate: %s could not be instantiated\n", fhandle->name);
570                 free (fst);
571                 return NULL;
572         }
573
574         fst->handle = fhandle;
575         fst->plugin->user = userptr;
576
577         if (fst->plugin->magic != kEffectMagic) {
578                 fst_error ("fst_instantiate: %s is not a vst plugin\n", fhandle->name);
579                 fst_close(fst);
580                 return NULL;
581         }
582
583         fst->plugin->dispatcher (fst->plugin, effOpen, 0, 0, 0, 0);
584         fst->vst_version = fst->plugin->dispatcher (fst->plugin, effGetVstVersion, 0, 0, 0, 0);
585
586         fst->handle->plugincnt++;
587         fst->wantIdle = 0;
588
589         return fst;
590 }
591
592 void fst_audio_master_idle(void) {
593         while(g_main_context_iteration(NULL, FALSE)) ;
594 }
595
596 void
597 fst_close (VSTState* fst)
598 {
599         if (fst != NULL) {
600                 fst_destroy_editor (fst);
601
602                 if (fst->plugin) {
603                         fst->plugin->dispatcher (fst->plugin, effMainsChanged, 0, 0, NULL, 0);
604                         fst->plugin->dispatcher (fst->plugin, effClose, 0, 0, 0, 0);
605                         fst->plugin = NULL;
606                 }
607
608                 if (fst->handle) {
609                         if (fst->handle->plugincnt && --fst->handle->plugincnt == 0) {
610
611                                 fst->handle->main_entry = NULL;
612                                 fst_unload (&fst->handle); // XXX
613                         }
614                 }
615
616                 /* It might be good for this to be in it's own cleanup function
617                         since it will free the memory for the fst leaving the caller
618                         with an invalid pointer.  Caller beware */
619                 fst_delete(fst);
620         }
621 }
622
623 #if 0 // ?? who needs this, where?
624 float htonf (float v)
625 {
626         float result;
627         char * fin = (char*)&v;
628         char * fout = (char*)&result;
629         fout[0] = fin[3];
630         fout[1] = fin[2];
631         fout[2] = fin[1];
632         fout[3] = fin[0];
633         return result;
634 }
635 #endif