merged with 1697 revision of trunk (which is post-rc1 but pre-rc2
[ardour.git] / libs / fst / vstwin.c
1 #include <stdio.h>
2 #include <libgen.h>
3 #include <windows.h>
4 #include <winnt.h>
5 #include <wine/exception.h>
6 #include <pthread.h>
7 #include <signal.h>
8
9 //#include <x11/xlib.h>
10 //#include <x11/xresource.h>
11 //#include <x11/xutil.h>
12 //#include <x11/xatom.h>
13
14 #include "fst.h"
15
16
17 struct ERect{
18     short top;
19     short left;
20     short bottom;
21     short right;
22 };
23
24 static pthread_mutex_t plugin_mutex = PTHREAD_MUTEX_INITIALIZER;
25 static FST* fst_first = NULL;
26
27 DWORD  gui_thread_id = 0;
28
29 static char* message_name (int message)
30 {
31         switch (message) {
32         case 0x0000:
33                 return "WM_NULL";
34
35         case 0x0001:
36                 return "WM_CREATE";
37
38         case 0x0002:
39                 return "WM_DESTROY";
40
41         case 0x0003:
42                 return "WM_MOVE";
43
44         case 0x0004:
45                 return "WM_SIZEWAIT";
46
47         case 0x0005:
48                 return "WM_SIZE";
49
50         case 0x0006:
51                 return "WM_ACTIVATE";
52
53         case 0x0007:
54                 return "WM_SETFOCUS";
55
56         case 0x0008:
57                 return "WM_KILLFOCUS";
58
59         case 0x0009:
60                 return "WM_SETVISIBLE";
61
62         case 0x000a:
63                 return "WM_ENABLE";
64
65         case 0x000b:
66                 return "WM_SETREDRAW";
67
68         case 0x000c:
69                 return "WM_SETTEXT";
70
71         case 0x000d:
72                 return "WM_GETTEXT";
73
74         case 0x000e:
75                 return "WM_GETTEXTLENGTH";
76
77         case 0x000f:
78                 return "WM_PAINT";
79
80         case 0x0010:
81                 return "WM_CLOSE";
82
83         case 0x0011:
84                 return "WM_QUERYENDSESSION";
85
86         case 0x0012:
87                 return "WM_QUIT";
88
89         case 0x0013:
90                 return "WM_QUERYOPEN";
91
92         case 0x0014:
93                 return "WM_ERASEBKGND";
94
95         case 0x0015:
96                 return "WM_SYSCOLORCHANGE";
97
98         case 0x0016:
99                 return "WM_ENDSESSION";
100
101         case 0x0017:
102                 return "WM_SYSTEMERROR";
103
104         case 0x0018:
105                 return "WM_SHOWWINDOW";
106
107         case 0x0019:
108                 return "WM_CTLCOLOR";
109
110         case 0x001a:
111                 return "WM_WININICHANGE";
112
113         case 0x001b:
114                 return "WM_DEVMODECHANGE";
115
116         case 0x001c:
117                 return "WM_ACTIVATEAPP";
118
119         case 0x001d:
120                 return "WM_FONTCHANGE";
121
122         case 0x001e:
123                 return "WM_TIMECHANGE";
124
125         case 0x001f:
126                 return "WM_CANCELMODE";
127
128         case 0x0020:
129                 return "WM_SETCURSOR";
130
131         case 0x0021:
132                 return "WM_MOUSEACTIVATE";
133
134         case 0x0022:
135                 return "WM_CHILDACTIVATE";
136
137         case 0x0023:
138                 return "WM_QUEUESYNC";
139
140         case 0x0024:
141                 return "WM_GETMINMAXINFO";
142
143         default:
144                 break;
145         }
146         return "--- OTHER ---";
147 }
148         
149 static LRESULT WINAPI 
150 my_window_proc (HWND w, UINT msg, WPARAM wp, LPARAM lp)
151 {
152         FST* fst;
153
154 //      if (msg != WM_TIMER) {
155 //              fst_error ("window callback handler, msg = 0x%x (%s) win=%p\n", msg, message_name (msg), w);
156 //      }
157
158         switch (msg) {
159         case WM_KEYUP:
160         case WM_KEYDOWN:
161                 break;
162
163         case WM_CLOSE:
164                 PostQuitMessage (0);
165
166         case WM_DESTROY:
167         case WM_NCDESTROY:
168                 /* we should never get these */
169                 //return 0;
170                 break;
171
172         case WM_PAINT:
173                 if ((fst = GetPropA (w, "fst_ptr")) != NULL) {
174                         if (fst->window && !fst->been_activated) {
175                                 fst->been_activated = TRUE;
176                                 pthread_cond_signal (&fst->window_status_change);
177                                 pthread_mutex_unlock (&fst->lock);
178                         }
179                 }
180                 break;
181
182         default:
183                 break;
184         }
185
186         return DefWindowProcA (w, msg, wp, lp );
187 }
188
189 static FST* 
190 fst_new ()
191 {
192         FST* fst = (FST*) calloc (1, sizeof (FST));
193
194         pthread_mutex_init (&fst->lock, NULL);
195         pthread_cond_init (&fst->window_status_change, NULL);
196
197         return fst;
198 }
199
200 static FSTHandle* 
201 fst_handle_new ()
202 {
203         FSTHandle* fst = (FSTHandle*) calloc (1, sizeof (FSTHandle));
204         return fst;
205 }
206
207 int
208 fst_create_editor (FST* fst)
209 {
210         HMODULE hInst;
211         HWND window;
212
213         /* "guard point" to trap errors that occur during plugin loading */
214
215         /* Note: fst->lock is held while this function is called */
216
217         if (!(fst->plugin->flags & effFlagsHasEditor)) {
218                 fst_error ("Plugin \"%s\" has no editor", fst->handle->name);
219                 return -1;
220         }
221
222         if ((hInst = GetModuleHandleA (NULL)) == NULL) {
223                 fst_error ("can't get module handle");
224                 return 1;
225         }
226         
227 //      if ((window = CreateWindowExA (WS_EX_TOOLWINDOW | WS_EX_TRAYWINDOW, "FST", fst->handle->name,
228         if ((window = CreateWindowExA (0, "FST", fst->handle->name,
229                                        (WS_OVERLAPPEDWINDOW & ~WS_THICKFRAME & ~WS_MAXIMIZEBOX),
230                                        0, 0, 1, 1,
231                                        NULL, NULL,
232                                        hInst,
233                                        NULL)) == NULL) {
234                 fst_error ("cannot create editor window");
235                 return 1;
236         }
237
238         if (!SetPropA (window, "fst_ptr", fst)) {
239                 fst_error ("cannot set fst_ptr on window");
240         }
241
242         fst->window = window;
243         fst->xid = (int) GetPropA (window, "__wine_x11_whole_window");
244
245         {
246                 struct ERect* er;
247
248                 ShowWindow (fst->window, SW_SHOW);
249         
250                 fst->plugin->dispatcher (fst->plugin, effEditOpen, 0, 0, fst->window, 0 );
251                 fst->plugin->dispatcher (fst->plugin, effEditGetRect, 0, 0, &er, 0 );
252                 
253                 fst->width =  er->right-er->left;
254                 fst->height =  er->bottom-er->top;
255                 
256                 SetWindowPos (fst->window, 0, 0, 0, er->right-er->left+8, er->bottom-er->top+26, SWP_SHOWWINDOW|SWP_NOMOVE|SWP_NOZORDER);
257         }
258
259         return 0;
260 }
261
262 void
263 fst_destroy_editor (FST* fst)
264 {
265         pthread_mutex_lock (&fst->lock);
266         if (fst->window) {
267                 fst->destroy = TRUE;
268                 if (!PostThreadMessageA (gui_thread_id, WM_USER, 0, 0)) {
269                         fst_error ("could not post message to gui thread");
270                 }
271                 pthread_cond_wait (&fst->window_status_change, &fst->lock);
272
273         }
274         pthread_mutex_unlock (&fst->lock);
275 }
276
277 void
278 fst_event_loop_remove_plugin (FST* fst)
279 {
280         FST* p;
281         FST* prev;
282
283         for (p = fst_first, prev = NULL; p->next; prev = p, p = p->next) {
284                 if (p == fst) {
285                         if (prev) {
286                                 prev->next = p->next;
287                         }
288                 }
289         }
290
291         if (fst_first == fst) {
292                 fst_first = fst_first->next;
293         }
294
295 }
296
297 void debreak( void ) { printf( "debreak\n" ); }
298
299 DWORD WINAPI gui_event_loop (LPVOID param)
300 {
301         MSG msg;
302         FST* fst;
303         HMODULE hInst;
304         HWND window;
305
306         gui_thread_id = GetCurrentThreadId ();
307
308         /* create a dummy window for timer events */
309
310         if ((hInst = GetModuleHandleA (NULL)) == NULL) {
311                 fst_error ("can't get module handle");
312                 return 1;
313         }
314         
315         if ((window = CreateWindowExA (0, "FST", "dummy",
316                                        WS_OVERLAPPEDWINDOW & ~WS_THICKFRAME & ~WS_MAXIMIZEBOX,
317                                        CW_USEDEFAULT, CW_USEDEFAULT,
318                                        CW_USEDEFAULT, CW_USEDEFAULT,
319                                        NULL, NULL,
320                                        hInst,
321                                        NULL )) == NULL) {
322                 fst_error ("cannot create dummy timer window");
323         }
324
325         if (!SetTimer (window, 1000, 100, NULL)) {
326                 fst_error ("cannot set timer on dummy window");
327         }
328
329         while (1) {
330
331                 GetMessageA (&msg, NULL, 0,0);
332
333                 if (msg.message == WM_SYSTEMERROR) {
334                         /* sent when this thread is supposed to exist */
335                         break;
336                 }
337                 
338                 if (msg.message == WM_KEYDOWN) debreak();
339
340                 TranslateMessage( &msg );
341                 DispatchMessageA (&msg);
342
343                 /* handle window creation requests, destroy requests, 
344                    and run idle callbacks 
345                 */
346                 
347                 if( msg.message == WM_TIMER ) {
348                     pthread_mutex_lock (&plugin_mutex);
349 again:
350                     for (fst = fst_first; fst; fst = fst->next) {
351
352                         if (fst->destroy) {
353                             if (fst->window) {
354                                 fst->plugin->dispatcher( fst->plugin, effEditClose, 0, 0, NULL, 0.0 );
355                                 CloseWindow (fst->window);
356                                 fst->window = NULL;
357                                 fst->destroy = FALSE;
358                             }
359                             fst_event_loop_remove_plugin (fst);
360                             fst->been_activated = FALSE;
361                             pthread_mutex_lock (&fst->lock);
362                             pthread_cond_signal (&fst->window_status_change);
363                             pthread_mutex_unlock (&fst->lock);
364                             goto again;
365                         } 
366
367                         if (fst->window == NULL) {
368                             pthread_mutex_lock (&fst->lock);
369                             if (fst_create_editor (fst)) {
370                                 fst_error ("cannot create editor for plugin %s", fst->handle->name);
371                                 fst_event_loop_remove_plugin (fst);
372                                 pthread_cond_signal (&fst->window_status_change);
373                                 pthread_mutex_unlock (&fst->lock);
374                                 goto again;
375                             }
376                             /* condition/unlock handled when we receive WM_ACTIVATE */
377                         }
378
379                         fst->plugin->dispatcher (fst->plugin, effEditIdle, 0, 0, NULL, 0);
380                     }
381                     pthread_mutex_unlock (&plugin_mutex);
382                 }
383         }
384         fst_error ("FST GUI event loop has quit!");
385         return 0;
386 }
387
388 int
389 fst_init ()
390 {
391         WNDCLASSA wc;
392         HMODULE hInst;
393
394         if ((hInst = GetModuleHandleA (NULL)) == NULL) {
395                 fst_error ("can't get module handle");
396                 return -1;
397         }
398         wc.style = 0;
399         wc.lpfnWndProc = my_window_proc;
400         wc.cbClsExtra = 0;
401         wc.cbWndExtra = 0;
402         wc.hInstance = hInst;
403         wc.hIcon = LoadIconA( hInst, "FST");
404         wc.hCursor = LoadCursorA( NULL, IDI_APPLICATION );
405         wc.hbrBackground = GetStockObject( BLACK_BRUSH );
406         wc.lpszMenuName = "MENU_FST";
407         wc.lpszClassName = "FST";
408
409         if (!RegisterClassA(&wc)){
410                 return 1;
411         }
412
413         if (CreateThread (NULL, 0, gui_event_loop, NULL, 0, NULL) == NULL) {
414                 fst_error ("could not create new thread proxy");
415                 return -1;
416         }
417
418         return 0;
419 }
420
421 void
422 fst_finish ()
423 {
424         PostThreadMessageA (gui_thread_id, WM_SYSTEMERROR, 0, 0);
425 }
426
427 int
428 fst_run_editor (FST* fst)
429 {
430         /* Add the FST to the list of all that should be handled by the GUI thread */
431
432         pthread_mutex_lock (&plugin_mutex);
433
434         if (fst_first == NULL) {
435                 fst_first = fst;
436         } else {
437                 FST* p = fst_first;
438                 while (p->next) {
439                         p = p->next;
440                 }
441                 p->next = fst;
442         }
443
444         if (!PostThreadMessageA (gui_thread_id, WM_USER, 0, 0)) {
445                 fst_error ("could not post message to gui thread");
446                 return -1;
447         }
448
449         pthread_mutex_unlock (&plugin_mutex);
450
451         /* wait for the plugin editor window to be created (or not) */
452
453         pthread_mutex_lock (&fst->lock);
454         if (!fst->window) {
455                 pthread_cond_wait (&fst->window_status_change, &fst->lock);
456         } 
457         pthread_mutex_unlock (&fst->lock);
458
459         if (!fst->window) {
460                 fst_error ("no window created for VST plugin editor");
461                 return -1;
462         }
463
464         return 0;
465 }
466
467 FSTHandle*
468 fst_load (const char *path)
469 {
470         char* buf;
471         FSTHandle* fhandle;
472         char* period;
473
474         fhandle = fst_handle_new ();
475         
476         // XXX: Would be nice to find the correct call for this.
477         //      if the user does not configure Z: to be / we are doomed :(
478
479         if (strstr (path, ".dll") == NULL) {
480
481                 buf = (char *) malloc (strlen (path) + 7);
482
483                 if( path[0] == '/' ) {
484                     sprintf (buf, "Z:%s.dll", path);
485                 } else {
486                     sprintf (buf, "%s.dll", path);
487                 }
488
489                 fhandle->nameptr = strdup (path);
490
491         } else {
492
493                 buf = (char *) malloc (strlen (path) + 3);
494
495                 if( path[0] == '/' ) {
496                     sprintf (buf, "Z:%s", path);
497                 } else {
498                     sprintf (buf, "%s", path);
499                 }
500
501                 fhandle->nameptr = strdup (path);
502         }
503         
504         fhandle->name = basename (fhandle->nameptr);
505
506         /* strip off .dll */
507
508         if ((period = strrchr (fhandle->name, '.')) != NULL) {
509                 *period = '\0';
510         }
511
512         if ((fhandle->dll = LoadLibraryA (buf)) == NULL) {
513                 fst_unload (fhandle);
514                 return NULL;
515         }
516
517         typedef AEffect* (*entryFunctionType)(audioMasterCallback);
518
519         if ((fhandle->main_entry = (entryFunctionType) GetProcAddress (fhandle->dll, "main")) == NULL) {
520                 fst_unload (fhandle);
521                 return NULL;
522         }
523
524         return fhandle;
525 }
526
527 int
528 fst_unload (FSTHandle* fhandle)
529 {
530         if (fhandle->plugincnt) {
531                 return -1;
532         }
533
534         if (fhandle->dll) {
535                 FreeLibrary (fhandle->dll);
536                 fhandle->dll = NULL;
537         }
538
539         if (fhandle->nameptr) {
540                 free (fhandle->nameptr);
541                 fhandle->name = NULL;
542         }
543         
544         free (fhandle);
545         return 0;
546 }
547
548 FST*
549 fst_instantiate (FSTHandle* fhandle, audioMasterCallback amc, void* userptr)
550 {
551         FST* fst = fst_new ();
552
553         if( fhandle == NULL ) {
554             fst_error( "the handle was NULL\n" );
555             return NULL;
556         }
557
558         if ((fst->plugin = fhandle->main_entry (amc)) == NULL)  {
559                 fst_error ("%s could not be instantiated\n", fhandle->name);
560                 free (fst);
561                 return NULL;
562         }
563         
564         fst->handle = fhandle;
565         fst->plugin->user = userptr;
566                 
567         if (fst->plugin->magic != kEffectMagic) {
568                 fst_error ("%s is not a VST plugin\n", fhandle->name);
569                 free (fst);
570                 return NULL;
571         }
572         
573         fst->plugin->dispatcher (fst->plugin, effOpen, 0, 0, 0, 0);
574         //fst->plugin->dispatcher (fst->plugin, effMainsChanged, 0, 0, NULL, 0);
575
576         fst->handle->plugincnt++;
577
578         return fst;
579 }
580
581 void
582 fst_close (FST* fst)
583 {
584         fst_destroy_editor (fst);
585
586         fst->plugin->dispatcher (fst->plugin, effMainsChanged, 0, 0, NULL, 0);
587         fst->plugin->dispatcher (fst->plugin, effClose, 0, 0, 0, 0);
588
589         if (fst->handle->plugincnt) {
590                 --fst->handle->plugincnt;
591         }
592 }
593
594 int
595 fst_get_XID (FST* fst)
596 {
597         return fst->xid;
598 }