rework 8e3b6c9ac (fstwin is C code, not C++)
[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                 pthread_mutex_lock (&fst->state_lock);
96                 fst->plugin->dispatcher (fst->plugin, 24 /* effSetChunk */, 1, fst->wanted_chunk_size, fst->wanted_chunk, 0);
97                 fst->want_chunk = 0;
98                 pthread_mutex_unlock (&fst->state_lock);
99         }
100 }
101
102 static VOID CALLBACK
103 idle_hands(
104                 HWND hwnd,        // handle to window for timer messages
105                 UINT message,     // WM_TIMER message
106                 UINT idTimer,     // timer identifier
107                 DWORD dwTime)     // current system time
108 {
109         VSTState* fst;
110
111         pthread_mutex_lock (&plugin_mutex);
112
113         for (fst = fst_first; fst; fst = fst->next) {
114                 if (fst->gui_shown) {
115                         // this seems insane, but some plugins will not draw their meters if you don't
116                         // call this every time.  Example Ambience by Magnus @ Smartelectron:x
117                         fst->plugin->dispatcher (fst->plugin, effEditIdle, 0, 0, NULL, 0);
118
119                         if (fst->wantIdle) {
120                                 fst->wantIdle = fst->plugin->dispatcher (fst->plugin, effIdle, 0, 0, NULL, 0);
121                         }
122                 }
123
124                 pthread_mutex_lock (&fst->lock);
125 #ifndef PLATFORM_WINDOWS /* linux + wine */
126                 /* Dispatch messages to send keypresses to the plugin */
127                 int i;
128
129                 for (i = 0; i < fst->n_pending_keys; ++i) {
130                         MSG msg;
131                         /* I'm not quite sure what is going on here; it seems
132                          * `special' keys must be delivered with WM_KEYDOWN,
133                          * but that alphanumerics etc. must use WM_CHAR or
134                          * they will be ignored.  Ours is not to reason why ...
135                          */
136                         if (fst->pending_keys[i].special != 0) {
137                                 msg.message = WM_KEYDOWN;
138                                 msg.wParam = fst->pending_keys[i].special;
139                         } else {
140                                 msg.message = WM_CHAR;
141                                 msg.wParam = fst->pending_keys[i].character;
142                         }
143                         msg.hwnd = GetFocus ();
144                         msg.lParam = 0;
145                         DispatchMessageA (&msg);
146                 }
147
148                 fst->n_pending_keys = 0;
149 #endif
150
151                 /* See comment for maybe_set_program call below */
152                 maybe_set_program (fst);
153                 fst->want_program = -1;
154                 fst->want_chunk = 0;
155                 /* If we don't have an editor window yet, we still need to
156                  * set up the program, otherwise when we load a plugin without
157                  * opening its window it will sound wrong.  However, it seems
158                  * that if you don't also load the program after opening the GUI,
159                  * the GUI does not reflect the program properly.  So we'll not
160                  * mark that we've done this (ie we won't set want_program to -1)
161                  * and so it will be done again if and when the GUI arrives.
162                  */
163                 if (fst->program_set_without_editor == 0) {
164                         maybe_set_program (fst);
165                         fst->program_set_without_editor = 1;
166                 }
167
168                 pthread_mutex_unlock (&fst->lock);
169         }
170
171         pthread_mutex_unlock (&plugin_mutex);
172 }
173
174 static void
175 fst_idle_timer_add_plugin (VSTState* fst)
176 {
177         pthread_mutex_lock (&plugin_mutex);
178
179         if (fst_first == NULL) {
180                 fst_first = fst;
181         } else {
182                 VSTState* p = fst_first;
183                 while (p->next) {
184                         p = p->next;
185                 }
186                 p->next = fst;
187         }
188
189         pthread_mutex_unlock (&plugin_mutex);
190 }
191
192 static void
193 fst_idle_timer_remove_plugin (VSTState* fst)
194 {
195         VSTState* p;
196         VSTState* prev;
197
198         pthread_mutex_lock (&plugin_mutex);
199
200         for (p = fst_first, prev = NULL; p; prev = p, p = p->next) {
201                 if (p == fst) {
202                         if (prev) {
203                                 prev->next = p->next;
204                         }
205                         break;
206                 }
207                 if (!p->next) {
208                         break;
209                 }
210         }
211
212         if (fst_first == fst) {
213                 fst_first = fst_first->next;
214         }
215
216         pthread_mutex_unlock (&plugin_mutex);
217 }
218
219 static VSTState*
220 fst_new (void)
221 {
222         VSTState* fst = (VSTState*) calloc (1, sizeof (VSTState));
223         vststate_init (fst);
224 #ifdef PLATFORM_WINDOWS
225         fst->voffset = 50;
226         fst->hoffset = 0;
227 #else /* linux + wine */
228         fst->voffset = 24;
229         fst->hoffset = 6;
230 #endif
231         return fst;
232 }
233
234 static void
235 fst_delete (VSTState* fst)
236 {
237         if (fst) {
238                 free((void*)fst);
239                 fst = NULL;
240         }
241 }
242
243 static VSTHandle*
244 fst_handle_new (void)
245 {
246         VSTHandle* fst = (VSTHandle*) calloc (1, sizeof (VSTHandle));
247         return fst;
248 }
249
250 #ifndef PLATFORM_WINDOWS /* linux + wine */
251 static gboolean
252 g_idle_call (gpointer ignored) {
253         if (gui_quit) return FALSE;
254         MSG msg;
255         if (PeekMessageA (&msg, NULL, 0, 0, 1)) {
256                 TranslateMessage (&msg);
257                 DispatchMessageA (&msg);
258         }
259         idle_hands(NULL, 0, 0, 0);
260         g_main_context_iteration(NULL, FALSE);
261         return gui_quit ? FALSE : TRUE;
262 }
263 #endif
264
265
266 int
267 fst_init (void* possible_hmodule)
268 {
269         if (host_initialized) return 0;
270         HMODULE hInst;
271
272         if (possible_hmodule) {
273 #ifdef PLATFORM_WINDOWS
274                 fst_error ("Error in fst_init(): (module handle is unnecessary for Win32 build)");
275                 return -1;
276 #else /* linux + wine */
277                 hInst = (HMODULE) possible_hmodule;
278 #endif
279         } else if ((hInst = GetModuleHandleA (NULL)) == NULL) {
280                 fst_error ("can't get module handle");
281                 return -1;
282         }
283
284         if (!hInst) {
285                 fst_error ("Cannot initialise VST host");
286                 return -1;
287         }
288
289         WNDCLASSEX wclass;
290
291         wclass.cbSize = sizeof(WNDCLASSEX);
292 #ifdef PLATFORM_WINDOWS
293         wclass.style = (CS_HREDRAW | CS_VREDRAW);
294         wclass.hIcon = NULL;
295         wclass.hCursor = LoadCursor(0, IDC_ARROW);
296 #else /* linux + wine */
297         wclass.style = 0;
298         wclass.hIcon = LoadIcon(hInst, "FST");
299         wclass.hCursor = LoadCursor(0, IDI_APPLICATION);
300 #endif
301         wclass.hbrBackground = (HBRUSH)GetStockObject(BLACK_BRUSH);
302         wclass.lpfnWndProc = vstedit_wndproc;
303         wclass.cbClsExtra = 0;
304         wclass.cbWndExtra = 0;
305         wclass.hInstance = hInst;
306         wclass.lpszMenuName = "MENU_FST";
307         wclass.lpszClassName = "FST";
308         wclass.hIconSm = 0;
309
310         pthread_mutex_init (&plugin_mutex, NULL);
311         host_initialized = -1;
312
313         if (!RegisterClassExA(&wclass)){
314                 fst_error ("Error in fst_init(): (class registration failed");
315                 return -1;
316         }
317         return 0;
318 }
319
320 void
321 fst_start_threading(void)
322 {
323 #ifndef PLATFORM_WINDOWS /* linux + wine */
324         if (idle_id == 0) {
325                 gui_quit = 0;
326                 idle_id = g_idle_add (g_idle_call, NULL);
327         }
328 #endif
329 }
330
331 void
332 fst_stop_threading(void) {
333 #ifndef PLATFORM_WINDOWS /* linux + wine */
334         if (idle_id != 0) {
335                 gui_quit = 1;
336                 PostQuitMessage (0);
337                 g_main_context_iteration(NULL, FALSE);
338                 //g_source_remove(idle_id);
339                 idle_id = 0;
340         }
341 #endif
342 }
343
344 void
345 fst_exit (void)
346 {
347         if (!host_initialized) return;
348         VSTState* fst;
349         // If any plugins are still open at this point, close them!
350         while ((fst = fst_first))
351                 fst_close (fst);
352
353 #ifdef PLATFORM_WINDOWS
354         if (idle_timer_id != 0) {
355                 KillTimer(NULL, idle_timer_id);
356         }
357 #else /* linux + wine */
358         if (idle_id) {
359                 gui_quit = 1;
360                 PostQuitMessage (0);
361         }
362 #endif
363
364         host_initialized = FALSE;
365         pthread_mutex_destroy (&plugin_mutex);
366 }
367
368
369 int
370 fst_run_editor (VSTState* fst, void* window_parent)
371 {
372         /* For safety, remove any pre-existing editor window */ 
373         fst_destroy_editor (fst);
374         
375         if (fst->windows_window == NULL) {
376                 HMODULE hInst;
377                 HWND window;
378                 struct ERect* er = NULL;
379
380                 if (!(fst->plugin->flags & effFlagsHasEditor)) {
381                         fst_error ("Plugin \"%s\" has no editor", fst->handle->name);
382                         return -1;
383                 }
384
385                 if ((hInst = GetModuleHandleA (NULL)) == NULL) {
386                         fst_error ("fst_create_editor() can't get module handle");
387                         return 1;
388                 }
389
390                 if ((window = CreateWindowExA (0, "FST", fst->handle->name,
391                                                 window_parent ? WS_CHILD : (WS_OVERLAPPEDWINDOW & ~WS_THICKFRAME & ~WS_MAXIMIZEBOX),
392                                                 CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT,
393                                                 (HWND)window_parent, NULL,
394                                                 hInst,
395                                                 NULL) ) == NULL) {
396                         fst_error ("fst_create_editor() cannot create editor window");
397                         return 1;
398                 }
399
400                 if (!SetPropA (window, "fst_ptr", fst)) {
401                         fst_error ("fst_create_editor() cannot set fst_ptr on window");
402                 }
403
404                 fst->windows_window = window;
405
406                 if (window_parent) {
407                         // This is requiredv for some reason. Note the parent is set above when the window
408                         // is created. Without this extra call the actual plugin window will draw outside
409                         // of our plugin window.
410                         SetParent((HWND)fst->windows_window, (HWND)window_parent);
411                         fst->xid = 0;
412 #ifndef PLATFORM_WINDOWS /* linux + wine */
413                 } else {
414                         SetWindowPos (fst->windows_window, 0, 9999, 9999, 2, 2, 0);
415                         ShowWindow (fst->windows_window, SW_SHOWNA);
416                         fst->xid = (int) GetPropA (fst->windows_window, "__wine_x11_whole_window");
417 #endif
418                 }
419
420                 // This is the suggested order of calls.
421                 fst->plugin->dispatcher (fst->plugin, effEditGetRect, 0, 0, &er, 0 );
422                 fst->plugin->dispatcher (fst->plugin, effEditOpen, 0, 0, fst->windows_window, 0 );
423                 fst->plugin->dispatcher (fst->plugin, effEditGetRect, 0, 0, &er, 0 );
424
425                 if (er != NULL) {
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->hoffset, fst->height + fst->voffset, 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->path = strdup (path);
495                 fhandle->name = g_path_get_basename(path);
496                 if ((period = strrchr (fhandle->name, '.'))) {
497                         *period = '\0';
498                 }
499
500                 // See if we can load the plugin DLL
501                 if ((fhandle->dll = (HMODULE)fst_load_vst_library (path)) == NULL) {
502                         fst_unload (&fhandle);
503                         return NULL;
504                 }
505
506                 fhandle->main_entry = (main_entry_t) GetProcAddress ((HMODULE)fhandle->dll, "main");
507
508                 if (fhandle->main_entry == 0) {
509                         if ((fhandle->main_entry = (main_entry_t) GetProcAddress ((HMODULE)fhandle->dll, "VSTPluginMain"))) {
510                                 fprintf(stderr, "VST >= 2.4 plugin '%s'\n", path);
511                                 //PBD::warning << path << _(": is a VST >= 2.4 - this plugin may or may not function correctly with this version of Ardour.") << endmsg;
512                         }
513                 }
514
515                 if (fhandle->main_entry == 0) {
516                         fst_unload (&fhandle);
517                         return NULL;
518                 }
519         }
520         return fhandle;
521 }
522
523 int
524 fst_unload (VSTHandle** fhandle)
525 {
526         if (!(*fhandle)) {
527                 return -1;
528         }
529
530         if ((*fhandle)->plugincnt) {
531                 return -1;
532         }
533
534         if ((*fhandle)->dll) {
535                 FreeLibrary ((HMODULE)(*fhandle)->dll);
536                 (*fhandle)->dll = NULL;
537         }
538
539         if ((*fhandle)->path) {
540                 free ((*fhandle)->path);
541                 (*fhandle)->path = NULL;
542         }
543
544         if ((*fhandle)->name) {
545                 free ((*fhandle)->name);
546                 (*fhandle)->name = NULL;
547         }
548
549         free (*fhandle);
550         *fhandle = NULL;
551
552         return 0;
553 }
554
555 VSTState*
556 fst_instantiate (VSTHandle* fhandle, audioMasterCallback amc, void* userptr)
557 {
558         VSTState* fst = NULL;
559
560         if( fhandle == NULL ) {
561                 fst_error( "fst_instantiate(): (the handle was NULL)\n" );
562                 return NULL;
563         }
564
565         fst = fst_new ();
566
567         if ((fst->plugin = fhandle->main_entry (amc)) == NULL)  {
568                 fst_error ("fst_instantiate: %s could not be instantiated\n", fhandle->name);
569                 free (fst);
570                 return NULL;
571         }
572
573         fst->handle = fhandle;
574         fst->plugin->user = userptr;
575
576         if (fst->plugin->magic != kEffectMagic) {
577                 fst_error ("fst_instantiate: %s is not a vst plugin\n", fhandle->name);
578                 fst_close(fst);
579                 return NULL;
580         }
581
582         fst->plugin->dispatcher (fst->plugin, effOpen, 0, 0, 0, 0);
583         fst->vst_version = fst->plugin->dispatcher (fst->plugin, effGetVstVersion, 0, 0, 0, 0);
584
585         fst->handle->plugincnt++;
586         fst->wantIdle = 0;
587
588         return fst;
589 }
590
591 void fst_audio_master_idle(void) {
592         while(g_main_context_iteration(NULL, FALSE)) ;
593 }
594
595 void
596 fst_close (VSTState* fst)
597 {
598         if (fst != NULL) {
599                 fst_destroy_editor (fst);
600
601                 if (fst->plugin) {
602                         fst->plugin->dispatcher (fst->plugin, effMainsChanged, 0, 0, NULL, 0);
603                         fst->plugin->dispatcher (fst->plugin, effClose, 0, 0, 0, 0);
604                         fst->plugin = NULL;
605                 }
606
607                 if (fst->handle) {
608                         if (fst->handle->plugincnt && --fst->handle->plugincnt == 0) {
609
610                                 fst->handle->main_entry = NULL;
611                                 fst_unload (&fst->handle); // XXX
612                         }
613                 }
614
615                 /* It might be good for this to be in it's own cleanup function
616                         since it will free the memory for the fst leaving the caller
617                         with an invalid pointer.  Caller beware */
618                 fst_delete(fst);
619         }
620 }
621
622 #if 0 // ?? who needs this, where?
623 float htonf (float v)
624 {
625         float result;
626         char * fin = (char*)&v;
627         char * fout = (char*)&result;
628         fout[0] = fin[3];
629         fout[1] = fin[2];
630         fout[2] = fin[1];
631         fout[3] = fin[0];
632         return result;
633 }
634 #endif