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