more details for VST support
[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;
25 static FST* fst_first = NULL;
26
27 DWORD  gui_thread_id = 0;
28
29
30
31
32
33
34
35 /* Define to a macro to generate an assembly function directive */
36 #define __ASM_FUNC(name) ".type " __ASM_NAME(name) ",@function"
37
38 /* Define to a macro to generate an assembly name from a C symbol */
39 #define __ASM_NAME(name) name
40
41 # define __ASM_GLOBAL_FUNC(name,code) \
42       __asm__( ".align 4\n\t" \
43                ".globl " __ASM_NAME(#name) "\n\t" \
44                __ASM_FUNC(#name) "\n" \
45                __ASM_NAME(#name) ":\n\t" \
46                code );
47
48 __ASM_GLOBAL_FUNC( fst_get_teb, ".byte 0x64\n\tmovl 0x18,%eax\n\tret" );
49
50
51
52 #define DELAYED_WINDOW
53
54 static LRESULT WINAPI 
55 my_window_proc (HWND w, UINT msg, WPARAM wp, LPARAM lp)
56 {
57         FST* fst;
58         LRESULT result;
59
60 //      if (msg != WM_TIMER) {
61 //              fst_error ("window callback handler, msg = 0x%x win=%p\n", msg, w);
62 //      }
63
64         switch (msg) {
65         case WM_KEYUP:
66         case WM_KEYDOWN:
67             printf( "got a key\n" );
68                 break;
69
70         case WM_CLOSE:
71                 printf("wtf.\n" );
72                 PostQuitMessage (0);
73         case WM_DESTROY:
74         case WM_NCDESTROY:
75                 /* we should never get these */
76                 //return 0;
77                 break;
78         
79
80         case WM_PAINT:
81 //      case WM_ACTIVATE:
82 #ifdef DELAYED_WINDOW
83                 //if (wp & WA_ACTIVE) {
84                         if ((fst = GetPropA (w, "fst_ptr")) != NULL) {
85                                 if (fst->window && !fst->been_activated) {
86                                         fst->been_activated = TRUE;
87 //      {
88 //          XSetWindowAttributes attr;
89 //
90 //          attr.override_redirect = TRUE;
91 ////    XChangeWindowAttributes( thread_display(), fst->xid, CWOverrideRedirect, &attr );
92 //      }
93                                         pthread_cond_signal (&fst->window_status_change);
94                                         pthread_mutex_unlock (&fst->lock);
95                                 }
96                         }
97 //              }
98 #endif
99                 break;
100
101         default:
102                 break;
103         }
104
105         return DefWindowProcA (w, msg, wp, lp );
106 }
107
108 static FST* 
109 fst_new ()
110 {
111         FST* fst = (FST*) calloc (1, sizeof (FST));
112
113         pthread_mutex_init (&fst->lock, NULL);
114         pthread_cond_init (&fst->window_status_change, NULL);
115
116         return fst;
117 }
118
119 static FSTHandle* 
120 fst_handle_new ()
121 {
122         FSTHandle* fst = (FSTHandle*) calloc (1, sizeof (FSTHandle));
123         return fst;
124 }
125
126 #ifdef HAVE_TLS
127 static __thread int ejmpbuf_valid = FALSE;
128 static __thread jmp_buf ejmpbuf;
129 #else
130 static pthread_key_t ejmpbuf_valid_key;
131 static pthread_key_t ejmpbuf_key;
132 #endif
133
134 void debreak( void ) { printf( "debreak\n" ); }
135
136
137 int
138 fst_init (void (*sighandler)(int,siginfo_t*,void*))
139 {
140         //SharedWineInit (sighandler);
141         wine_shared_premain();
142
143         return 0;
144 }
145
146 DWORD WINAPI gui_event_loop (LPVOID param)
147 {
148         MSG msg;
149         FST* fst;
150         char c;
151         HMODULE hInst;
152         HWND window;
153
154         gui_thread_id = GetCurrentThreadId ();
155
156         /* create a dummy window for timer events */
157
158         if ((hInst = GetModuleHandleA (NULL)) == NULL) {
159                 fst_error ("can't get module handle");
160                 return 1;
161         }
162         
163         if ((window = CreateWindowExA (0, "FST", "dummy",
164                                        WS_OVERLAPPEDWINDOW & ~WS_THICKFRAME & ~WS_MAXIMIZEBOX,
165                                        CW_USEDEFAULT, CW_USEDEFAULT,
166                                        CW_USEDEFAULT, CW_USEDEFAULT,
167                                        NULL, NULL,
168                                        hInst,
169                                        NULL )) == NULL) {
170                 fst_error ("cannot create dummy timer window");
171         }
172
173         if (!SetTimer (window, 1000, 100, NULL)) {
174                 fst_error ("cannot set timer on dummy window");
175         }
176
177         while (GetMessageA (&msg, NULL, 0,0)) {
178             if( msg.message == WM_KEYDOWN ) debreak();
179                 TranslateMessage( &msg );
180                 DispatchMessageA (&msg);
181
182                 /* handle window creation requests, destroy requests, 
183                    and run idle callbacks 
184                 */
185                 
186
187                 if( msg.message == WM_TIMER  ) {
188                 pthread_mutex_lock (&plugin_mutex);
189 again:
190                     for (fst = fst_first; fst; fst = fst->next) {
191
192                         if (fst->destroy) {
193                             if (fst->window) {
194                                 fst->plugin->dispatcher( fst->plugin, effEditClose, 0, 0, NULL, 0.0 );
195                                 CloseWindow (fst->window);
196                                 fst->window = NULL;
197                                 fst->destroy = FALSE;
198                             }
199                             fst_event_loop_remove_plugin (fst);
200                             fst->been_activated = FALSE;
201                             pthread_mutex_lock (&fst->lock);
202                             pthread_cond_signal (&fst->window_status_change);
203                             pthread_mutex_unlock (&fst->lock);
204                             goto again;
205                         } 
206
207                         if (fst->window == NULL) {
208                             pthread_mutex_lock (&fst->lock);
209                             if (fst_create_editor (fst)) {
210                                 fst_error ("cannot create editor for plugin %s", fst->handle->name);
211                                 fst_event_loop_remove_plugin (fst);
212                                 pthread_cond_signal (&fst->window_status_change);
213                                 pthread_mutex_unlock (&fst->lock);
214                                 goto again;
215                             }
216                             /* condition/unlock handled when we receive WM_ACTIVATE */
217                         }
218
219                         fst->plugin->dispatcher (fst->plugin, effEditIdle, 0, 0, NULL, 0);
220                     }
221                     pthread_mutex_unlock (&plugin_mutex);
222                 }
223         }
224         printf( "quit........\n" );
225         exit(0);
226         gtk_main_quit();
227 }
228
229 void
230 fst_set_focus (FST* fst)
231 {
232         if (fst->window) {
233                 SetFocus (fst->window);
234         }
235 }
236
237 int
238 wine_shared_premain ()
239 {
240         WNDCLASSA wc;
241         HMODULE hInst;
242
243         if ((hInst = GetModuleHandleA (NULL)) == NULL) {
244                 fst_error ("can't get module handle");
245                 return -1;
246         }
247         wc.style = 0;
248         wc.lpfnWndProc = my_window_proc;
249         wc.cbClsExtra = 0;
250         wc.cbWndExtra = 0;
251         wc.hInstance = hInst;
252         wc.hIcon = LoadIconA( hInst, "FST");
253         wc.hCursor = LoadCursorA( NULL, IDI_APPLICATION );
254         wc.hbrBackground = GetStockObject( BLACK_BRUSH );
255         wc.lpszMenuName = "MENU_FST";
256         wc.lpszClassName = "FST";
257
258         if (!RegisterClassA(&wc)){
259                 return 1;
260         }
261
262         if (CreateThread (NULL, 0, gui_event_loop, NULL, 0, NULL) == NULL) {
263                 fst_error ("could not create new thread proxy");
264                 return -1;
265         }
266
267         return 0;
268 }
269
270 int
271 fst_run_editor (FST* fst)
272 {
273         pthread_mutex_lock (&plugin_mutex);
274
275         if (fst_first == NULL) {
276                 fst_first = fst;
277         } else {
278                 FST* p = fst_first;
279                 while (p->next) {
280                         p = p->next;
281                 }
282                 p->next = fst;
283         }
284
285         printf( "gui_thread_id = %d\n", gui_thread_id );
286         if (!PostThreadMessageA (gui_thread_id, WM_USER, 0, 0)) {
287                 fst_error ("could not post message to gui thread");
288                 return -1;
289         }
290
291         pthread_mutex_unlock (&plugin_mutex);
292
293         /* wait for the plugin editor window to be created (or not) */
294
295         pthread_mutex_lock (&fst->lock);
296         if (!fst->window) {
297                 pthread_cond_wait (&fst->window_status_change, &fst->lock);
298         } 
299         pthread_mutex_unlock (&fst->lock);
300
301         if (!fst->window) {
302                 fst_error ("no window created for VST plugin editor");
303                 return -1;
304         }
305
306         return 0;
307 }
308
309 int
310 fst_create_editor (FST* fst)
311 {
312         HMODULE hInst;
313         char class[20];
314         HWND window;
315
316         /* "guard point" to trap errors that occur during plugin loading */
317
318         /* Note: fst->lock is held while this function is called */
319
320         if (!(fst->plugin->flags & effFlagsHasEditor)) {
321                 fst_error ("Plugin \"%s\" has no editor", fst->handle->name);
322                 return -1;
323         }
324
325         if ((hInst = GetModuleHandleA (NULL)) == NULL) {
326                 fst_error ("can't get module handle");
327                 return 1;
328         }
329         
330 //      if ((window = CreateWindowExA (WS_EX_TOOLWINDOW | WS_EX_TRAYWINDOW, "FST", fst->handle->name,
331         if ((window = CreateWindowExA (0, "FST", fst->handle->name,
332                                        (WS_OVERLAPPEDWINDOW & ~WS_THICKFRAME & ~WS_MAXIMIZEBOX),
333                                        0, 0, 1, 1,
334                                        NULL, NULL,
335                                        hInst,
336                                        NULL)) == NULL) {
337                 fst_error ("cannot create editor window");
338                 return 1;
339         }
340
341         if (!SetPropA (window, "fst_ptr", fst)) {
342                 fst_error ("cannot set fst_ptr on window");
343         }
344
345         fst->window = window;
346         fst->xid = (int) GetPropA (window, "__wine_x11_whole_window");
347
348 //      XChangeWindowAttributes( XOpenDisplay(NULL), fst->xid, CWOverrideRedirect, ~0 );
349
350
351 #ifdef DELAYED_WINDOW
352         {
353                 struct ERect* er;
354
355                 ShowWindow (fst->window, SW_SHOW);
356         
357                 fst->plugin->dispatcher (fst->plugin, effEditOpen, 0, 0, fst->window, 0 );
358                 fst->plugin->dispatcher (fst->plugin, effEditGetRect, 0, 0, &er, 0 );
359                 
360                 fst->width =  er->right-er->left;
361                 fst->height =  er->bottom-er->top;
362                 
363                 SetWindowPos (fst->window, 0, 0, 0, er->right-er->left+8, er->bottom-er->top+26, SWP_SHOWWINDOW|SWP_NOMOVE|SWP_NOZORDER);
364         }
365 #else
366
367         pthread_cond_signal (&fst->window_status_change);
368         pthread_mutex_unlock (&fst->lock);
369
370 #endif 
371         return 0;
372 }
373
374 void
375 fst_show_editor (FST* fst)
376 {
377 #ifndef DELAYED_WINDOW
378         struct ERect* er;
379
380         fst->plugin->dispatcher (fst->plugin, effEditOpen, 0, 0, fst->window, 0 );
381         fst->plugin->dispatcher (fst->plugin, effEditGetRect, 0, 0, &er, 0 );
382         fst->width =  er->right-er->left;
383         fst->height =  er->bottom-er->top;
384
385         SetWindowPos (window, 0, 0, 0, er->right-er->left+8, er->bottom-er->top+26, SWP_NOMOVE | SWP_NOZORDER);
386         *ejmpbuf_valid = FALSE;
387 #endif
388 }
389
390 void
391 fst_destroy_editor (FST* fst)
392 {
393         FST* p;
394         FST* prev;
395
396         pthread_mutex_lock (&fst->lock);
397         if (fst->window) {
398                 fst->destroy = TRUE;
399                 if (!PostThreadMessageA (gui_thread_id, WM_USER, 0, 0)) {
400                         fst_error ("could not post message to gui thread");
401                 }
402                 pthread_cond_wait (&fst->window_status_change, &fst->lock);
403
404         }
405         pthread_mutex_unlock (&fst->lock);
406 }
407
408 void
409 fst_event_loop_remove_plugin (FST* fst)
410 {
411         FST* p;
412         FST* prev;
413
414         for (p = fst_first, prev = NULL; p->next; prev = p, p = p->next) {
415                 if (p == fst) {
416                         if (prev) {
417                                 prev->next = p->next;
418                         }
419                 }
420         }
421
422         if (fst_first == fst) {
423                 fst_first = fst_first->next;
424         }
425
426 }
427
428 FSTHandle*
429 fst_load (const char *path)
430 {
431         char* buf, *buf2;
432         FSTHandle* fhandle;
433         char* period;
434
435         fhandle = fst_handle_new ();
436         
437         // XXX: Would be nice to find the correct call for this.
438         //      if the user does not configure Z: to be / we are doomed :(
439
440         if (strstr (path, ".dll") == NULL) {
441
442                 buf = (char *) malloc (strlen (path) + 7);
443
444                 if( path[0] == '/' ) {
445                     sprintf (buf, "Z:%s.dll", path);
446                 } else {
447                     sprintf (buf, "%s.dll", path);
448                 }
449
450                 fhandle->nameptr = strdup (path);
451
452         } else {
453
454                 buf = (char *) malloc (strlen (path) + 3);
455
456                 if( path[0] == '/' ) {
457                     sprintf (buf, "Z:%s", path);
458                 } else {
459                     sprintf (buf, "%s", path);
460                 }
461
462                 fhandle->nameptr = strdup (path);
463         }
464         
465         fhandle->name = basename (fhandle->nameptr);
466
467         /* strip off .dll */
468
469         if ((period = strrchr (fhandle->name, '.')) != NULL) {
470                 *period = '\0';
471         }
472
473         if ((fhandle->dll = LoadLibraryA (buf)) == NULL) {
474                 fst_unload (fhandle);
475                 return NULL;
476         }
477
478         if ((fhandle->main_entry = GetProcAddress (fhandle->dll, "main")) == NULL) {
479                 fst_unload (fhandle);
480                 return NULL;
481         }
482
483         return fhandle;
484 }
485
486 int
487 fst_unload (FSTHandle* fhandle)
488 {
489         if (fhandle->plugincnt) {
490                 return -1;
491         }
492
493         if (fhandle->dll) {
494                 FreeLibrary (fhandle->dll);
495                 fhandle->dll = NULL;
496         }
497
498         if (fhandle->nameptr) {
499                 free (fhandle->nameptr);
500                 fhandle->name = NULL;
501         }
502         
503         free (fhandle);
504         return 0;
505 }
506
507 FST*
508 fst_instantiate (FSTHandle* fhandle, audioMasterCallback amc, void* userptr)
509 {
510         FST* fst = fst_new ();
511
512         if( fhandle == NULL ) {
513             fst_error( "the handle was NULL\n" );
514             return NULL;
515         }
516
517         if ((fst->plugin = fhandle->main_entry (amc)) == NULL)  {
518                 fst_error ("%s could not be instantiated\n", fhandle->name);
519                 free (fst);
520                 return NULL;
521         }
522         
523         fst->handle = fhandle;
524         fst->plugin->user = userptr;
525                 
526         if (fst->plugin->magic != kEffectMagic) {
527                 fst_error ("%s is not a VST plugin\n", fhandle->name);
528                 free (fst);
529                 return NULL;
530         }
531         
532         fst->plugin->dispatcher (fst->plugin, effOpen, 0, 0, 0, 0);
533         //fst->plugin->dispatcher (fst->plugin, effMainsChanged, 0, 0, NULL, 0);
534
535         fst->handle->plugincnt++;
536
537         return fst;
538 }
539
540 void
541 fst_close (FST* fst)
542 {
543         fst_destroy_editor (fst);
544
545         fst->plugin->dispatcher (fst->plugin, effMainsChanged, 0, 0, NULL, 0);
546         fst->plugin->dispatcher (fst->plugin, effClose, 0, 0, 0, 0);
547
548         if (fst->handle->plugincnt) {
549                 --fst->handle->plugincnt;
550         }
551 }
552
553 int
554 fst_get_XID (FST* fst)
555 {
556         return fst->xid;
557 }
558
559 int
560 fst_adopt_thread ()
561 {
562         return 0;
563 }