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