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