really fix windows compilation.
[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
224         //vststate_init (fst);
225         pthread_mutex_init (&fst->lock, 0);
226         pthread_mutex_init (&fst->state_lock, 0);
227         pthread_cond_init (&fst->window_status_change, 0);
228         pthread_cond_init (&fst->plugin_dispatcher_called, 0);
229         pthread_cond_init (&fst->window_created, 0);
230         fst->want_program = -1;
231         //
232
233 #ifdef PLATFORM_WINDOWS
234         fst->voffset = 50;
235         fst->hoffset = 0;
236 #else /* linux + wine */
237         fst->voffset = 24;
238         fst->hoffset = 6;
239 #endif
240         return fst;
241 }
242
243 static void
244 fst_delete (VSTState* fst)
245 {
246         if (fst) {
247                 free((void*)fst);
248                 fst = NULL;
249         }
250 }
251
252 static VSTHandle*
253 fst_handle_new (void)
254 {
255         VSTHandle* fst = (VSTHandle*) calloc (1, sizeof (VSTHandle));
256         return fst;
257 }
258
259 #ifndef PLATFORM_WINDOWS /* linux + wine */
260 static gboolean
261 g_idle_call (gpointer ignored) {
262         if (gui_quit) return FALSE;
263         MSG msg;
264         if (PeekMessageA (&msg, NULL, 0, 0, 1)) {
265                 TranslateMessage (&msg);
266                 DispatchMessageA (&msg);
267         }
268         idle_hands(NULL, 0, 0, 0);
269         g_main_context_iteration(NULL, FALSE);
270         return gui_quit ? FALSE : TRUE;
271 }
272 #endif
273
274
275 int
276 fst_init (void* possible_hmodule)
277 {
278         if (host_initialized) return 0;
279         HMODULE hInst;
280
281         if (possible_hmodule) {
282 #ifdef PLATFORM_WINDOWS
283                 fst_error ("Error in fst_init(): (module handle is unnecessary for Win32 build)");
284                 return -1;
285 #else /* linux + wine */
286                 hInst = (HMODULE) possible_hmodule;
287 #endif
288         } else if ((hInst = GetModuleHandleA (NULL)) == NULL) {
289                 fst_error ("can't get module handle");
290                 return -1;
291         }
292
293         if (!hInst) {
294                 fst_error ("Cannot initialise VST host");
295                 return -1;
296         }
297
298         WNDCLASSEX wclass;
299
300         wclass.cbSize = sizeof(WNDCLASSEX);
301 #ifdef PLATFORM_WINDOWS
302         wclass.style = (CS_HREDRAW | CS_VREDRAW);
303         wclass.hIcon = NULL;
304         wclass.hCursor = LoadCursor(0, IDC_ARROW);
305 #else /* linux + wine */
306         wclass.style = 0;
307         wclass.hIcon = LoadIcon(hInst, "FST");
308         wclass.hCursor = LoadCursor(0, IDI_APPLICATION);
309 #endif
310         wclass.hbrBackground = (HBRUSH)GetStockObject(BLACK_BRUSH);
311         wclass.lpfnWndProc = vstedit_wndproc;
312         wclass.cbClsExtra = 0;
313         wclass.cbWndExtra = 0;
314         wclass.hInstance = hInst;
315         wclass.lpszMenuName = "MENU_FST";
316         wclass.lpszClassName = "FST";
317         wclass.hIconSm = 0;
318
319         pthread_mutex_init (&plugin_mutex, NULL);
320         host_initialized = -1;
321
322         if (!RegisterClassExA(&wclass)){
323                 fst_error ("Error in fst_init(): (class registration failed");
324                 return -1;
325         }
326         return 0;
327 }
328
329 void
330 fst_start_threading(void)
331 {
332 #ifndef PLATFORM_WINDOWS /* linux + wine */
333         if (idle_id == 0) {
334                 gui_quit = 0;
335                 idle_id = g_idle_add (g_idle_call, NULL);
336         }
337 #endif
338 }
339
340 void
341 fst_stop_threading(void) {
342 #ifndef PLATFORM_WINDOWS /* linux + wine */
343         if (idle_id != 0) {
344                 gui_quit = 1;
345                 PostQuitMessage (0);
346                 g_main_context_iteration(NULL, FALSE);
347                 //g_source_remove(idle_id);
348                 idle_id = 0;
349         }
350 #endif
351 }
352
353 void
354 fst_exit (void)
355 {
356         if (!host_initialized) return;
357         VSTState* fst;
358         // If any plugins are still open at this point, close them!
359         while ((fst = fst_first))
360                 fst_close (fst);
361
362 #ifdef PLATFORM_WINDOWS
363         if (idle_timer_id != 0) {
364                 KillTimer(NULL, idle_timer_id);
365         }
366 #else /* linux + wine */
367         if (idle_id) {
368                 gui_quit = 1;
369                 PostQuitMessage (0);
370         }
371 #endif
372
373         host_initialized = FALSE;
374         pthread_mutex_destroy (&plugin_mutex);
375 }
376
377
378 int
379 fst_run_editor (VSTState* fst, void* window_parent)
380 {
381         /* For safety, remove any pre-existing editor window */ 
382         fst_destroy_editor (fst);
383         
384         if (fst->windows_window == NULL) {
385                 HMODULE hInst;
386                 HWND window;
387                 struct ERect* er = NULL;
388
389                 if (!(fst->plugin->flags & effFlagsHasEditor)) {
390                         fst_error ("Plugin \"%s\" has no editor", fst->handle->name);
391                         return -1;
392                 }
393
394                 if ((hInst = GetModuleHandleA (NULL)) == NULL) {
395                         fst_error ("fst_create_editor() can't get module handle");
396                         return 1;
397                 }
398
399                 if ((window = CreateWindowExA (0, "FST", fst->handle->name,
400                                                 window_parent ? WS_CHILD : (WS_OVERLAPPEDWINDOW & ~WS_THICKFRAME & ~WS_MAXIMIZEBOX),
401                                                 CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT,
402                                                 (HWND)window_parent, NULL,
403                                                 hInst,
404                                                 NULL) ) == NULL) {
405                         fst_error ("fst_create_editor() cannot create editor window");
406                         return 1;
407                 }
408
409                 if (!SetPropA (window, "fst_ptr", fst)) {
410                         fst_error ("fst_create_editor() cannot set fst_ptr on window");
411                 }
412
413                 fst->windows_window = window;
414
415                 if (window_parent) {
416                         // This is requiredv for some reason. Note the parent is set above when the window
417                         // is created. Without this extra call the actual plugin window will draw outside
418                         // of our plugin window.
419                         SetParent((HWND)fst->windows_window, (HWND)window_parent);
420                         fst->xid = 0;
421 #ifndef PLATFORM_WINDOWS /* linux + wine */
422                 } else {
423                         SetWindowPos (fst->windows_window, 0, 9999, 9999, 2, 2, 0);
424                         ShowWindow (fst->windows_window, SW_SHOWNA);
425                         fst->xid = (int) GetPropA (fst->windows_window, "__wine_x11_whole_window");
426 #endif
427                 }
428
429                 // This is the suggested order of calls.
430                 fst->plugin->dispatcher (fst->plugin, effEditGetRect, 0, 0, &er, 0 );
431                 fst->plugin->dispatcher (fst->plugin, effEditOpen, 0, 0, fst->windows_window, 0 );
432                 fst->plugin->dispatcher (fst->plugin, effEditGetRect, 0, 0, &er, 0 );
433
434                 if (er != NULL) {
435                         fst->width = er->right - er->left;
436                         fst->height = er->bottom - er->top;
437                 }
438
439                 fst->been_activated = TRUE;
440
441         }
442
443         if (fst->windows_window) {
444 #ifdef PLATFORM_WINDOWS
445                 if (idle_timer_id == 0) {
446                         // Init the idle timer if needed, so that the main window calls us.
447                         idle_timer_id = SetTimer(NULL, idle_timer_id, 50, (TIMERPROC) idle_hands);
448                 }
449 #endif
450
451                 fst_idle_timer_add_plugin (fst);
452         }
453
454         return fst->windows_window == NULL ? -1 : 0;
455 }
456
457 void
458 fst_destroy_editor (VSTState* fst)
459 {
460         if (fst->windows_window) {
461                 fprintf (stderr, "%s destroying edit window\n", fst->handle->name);
462
463                 fst_idle_timer_remove_plugin (fst);
464                 fst->plugin->dispatcher( fst->plugin, effEditClose, 0, 0, NULL, 0.0 );
465
466                 DestroyWindow ((HWND)(fst->windows_window));
467
468                 fst->windows_window = NULL;
469         }
470
471         fst->been_activated = FALSE;
472 }
473
474 void
475 fst_move_window_into_view (VSTState* fst)
476 {
477         if (fst->windows_window) {
478 #ifdef PLATFORM_WINDOWS
479                 SetWindowPos ((HWND)(fst->windows_window), 0, fst->hoffset, fst->voffset, fst->width + fst->hoffset, fst->height + fst->voffset, 0);
480 #else /* linux + wine */
481                 SetWindowPos ((HWND)(fst->windows_window), 0, 0, 0, fst->width + fst->hoffset, fst->height + fst->voffset, 0);
482 #endif
483                 ShowWindow ((HWND)(fst->windows_window), SW_SHOWNA);
484         }
485 }
486
487 static HMODULE
488 fst_load_vst_library(const char * path)
489 {
490         char legalized_path[PATH_MAX];
491         strcpy (legalized_path, g_locale_from_utf8(path, -1, NULL, NULL, NULL));
492         return ( LoadLibraryA (legalized_path) );
493 }
494
495 VSTHandle *
496 fst_load (const char *path)
497 {
498         VSTHandle* fhandle = NULL;
499
500         if ((strlen(path)) && (NULL != (fhandle = fst_handle_new ())))
501         {
502                 char* period;
503                 fhandle->path = strdup (path);
504                 fhandle->name = g_path_get_basename(path);
505                 if ((period = strrchr (fhandle->name, '.'))) {
506                         *period = '\0';
507                 }
508
509                 // See if we can load the plugin DLL
510                 if ((fhandle->dll = (HMODULE)fst_load_vst_library (path)) == NULL) {
511                         fst_unload (&fhandle);
512                         return NULL;
513                 }
514
515                 fhandle->main_entry = (main_entry_t) GetProcAddress ((HMODULE)fhandle->dll, "main");
516
517                 if (fhandle->main_entry == 0) {
518                         if ((fhandle->main_entry = (main_entry_t) GetProcAddress ((HMODULE)fhandle->dll, "VSTPluginMain"))) {
519                                 fprintf(stderr, "VST >= 2.4 plugin '%s'\n", path);
520                                 //PBD::warning << path << _(": is a VST >= 2.4 - this plugin may or may not function correctly with this version of Ardour.") << endmsg;
521                         }
522                 }
523
524                 if (fhandle->main_entry == 0) {
525                         fst_unload (&fhandle);
526                         return NULL;
527                 }
528         }
529         return fhandle;
530 }
531
532 int
533 fst_unload (VSTHandle** fhandle)
534 {
535         if (!(*fhandle)) {
536                 return -1;
537         }
538
539         if ((*fhandle)->plugincnt) {
540                 return -1;
541         }
542
543         if ((*fhandle)->dll) {
544                 FreeLibrary ((HMODULE)(*fhandle)->dll);
545                 (*fhandle)->dll = NULL;
546         }
547
548         if ((*fhandle)->path) {
549                 free ((*fhandle)->path);
550                 (*fhandle)->path = NULL;
551         }
552
553         if ((*fhandle)->name) {
554                 free ((*fhandle)->name);
555                 (*fhandle)->name = NULL;
556         }
557
558         free (*fhandle);
559         *fhandle = NULL;
560
561         return 0;
562 }
563
564 VSTState*
565 fst_instantiate (VSTHandle* fhandle, audioMasterCallback amc, void* userptr)
566 {
567         VSTState* fst = NULL;
568
569         if( fhandle == NULL ) {
570                 fst_error( "fst_instantiate(): (the handle was NULL)\n" );
571                 return NULL;
572         }
573
574         fst = fst_new ();
575
576         if ((fst->plugin = fhandle->main_entry (amc)) == NULL)  {
577                 fst_error ("fst_instantiate: %s could not be instantiated\n", fhandle->name);
578                 free (fst);
579                 return NULL;
580         }
581
582         fst->handle = fhandle;
583         fst->plugin->user = userptr;
584
585         if (fst->plugin->magic != kEffectMagic) {
586                 fst_error ("fst_instantiate: %s is not a vst plugin\n", fhandle->name);
587                 fst_close(fst);
588                 return NULL;
589         }
590
591         fst->plugin->dispatcher (fst->plugin, effOpen, 0, 0, 0, 0);
592         fst->vst_version = fst->plugin->dispatcher (fst->plugin, effGetVstVersion, 0, 0, 0, 0);
593
594         fst->handle->plugincnt++;
595         fst->wantIdle = 0;
596
597         return fst;
598 }
599
600 void fst_audio_master_idle(void) {
601         while(g_main_context_iteration(NULL, FALSE)) ;
602 }
603
604 void
605 fst_close (VSTState* fst)
606 {
607         if (fst != NULL) {
608                 fst_destroy_editor (fst);
609
610                 if (fst->plugin) {
611                         fst->plugin->dispatcher (fst->plugin, effMainsChanged, 0, 0, NULL, 0);
612                         fst->plugin->dispatcher (fst->plugin, effClose, 0, 0, 0, 0);
613                         fst->plugin = NULL;
614                 }
615
616                 if (fst->handle) {
617                         if (fst->handle->plugincnt && --fst->handle->plugincnt == 0) {
618
619                                 fst->handle->main_entry = NULL;
620                                 fst_unload (&fst->handle); // XXX
621                         }
622                 }
623
624                 /* It might be good for this to be in it's own cleanup function
625                         since it will free the memory for the fst leaving the caller
626                         with an invalid pointer.  Caller beware */
627                 fst_delete(fst);
628         }
629 }
630
631 #if 0 // ?? who needs this, where?
632 float htonf (float v)
633 {
634         float result;
635         char * fin = (char*)&v;
636         char * fout = (char*)&result;
637         fout[0] = fin[3];
638         fout[1] = fin[2];
639         fout[2] = fin[1];
640         fout[3] = fin[0];
641         return result;
642 }
643 #endif