correctly forward configure events to plugin windows, and cleanup FST code
[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 (GetMessageA (&msg, NULL, 0,0)) {
330                 
331             if( msg.message == WM_KEYDOWN ) debreak();
332                 TranslateMessage( &msg );
333                 DispatchMessageA (&msg);
334
335                 /* handle window creation requests, destroy requests, 
336                    and run idle callbacks 
337                 */
338                 
339
340                 if( msg.message == WM_TIMER ) {
341                     pthread_mutex_lock (&plugin_mutex);
342 again:
343                     for (fst = fst_first; fst; fst = fst->next) {
344
345                         if (fst->destroy) {
346                             if (fst->window) {
347                                 fst->plugin->dispatcher( fst->plugin, effEditClose, 0, 0, NULL, 0.0 );
348                                 CloseWindow (fst->window);
349                                 fst->window = NULL;
350                                 fst->destroy = FALSE;
351                             }
352                             fst_event_loop_remove_plugin (fst);
353                             fst->been_activated = FALSE;
354                             pthread_mutex_lock (&fst->lock);
355                             pthread_cond_signal (&fst->window_status_change);
356                             pthread_mutex_unlock (&fst->lock);
357                             goto again;
358                         } 
359
360                         if (fst->window == NULL) {
361                             pthread_mutex_lock (&fst->lock);
362                             if (fst_create_editor (fst)) {
363                                 fst_error ("cannot create editor for plugin %s", fst->handle->name);
364                                 fst_event_loop_remove_plugin (fst);
365                                 pthread_cond_signal (&fst->window_status_change);
366                                 pthread_mutex_unlock (&fst->lock);
367                                 goto again;
368                             }
369                             /* condition/unlock handled when we receive WM_ACTIVATE */
370                         }
371
372                         fst->plugin->dispatcher (fst->plugin, effEditIdle, 0, 0, NULL, 0);
373                     }
374                     pthread_mutex_unlock (&plugin_mutex);
375                 }
376         }
377         fst_error ("FST GUI event loop has quit!");
378         return 0;
379 }
380
381 int
382 fst_init ()
383 {
384         WNDCLASSA wc;
385         HMODULE hInst;
386
387         if ((hInst = GetModuleHandleA (NULL)) == NULL) {
388                 fst_error ("can't get module handle");
389                 return -1;
390         }
391         wc.style = 0;
392         wc.lpfnWndProc = my_window_proc;
393         wc.cbClsExtra = 0;
394         wc.cbWndExtra = 0;
395         wc.hInstance = hInst;
396         wc.hIcon = LoadIconA( hInst, "FST");
397         wc.hCursor = LoadCursorA( NULL, IDI_APPLICATION );
398         wc.hbrBackground = GetStockObject( BLACK_BRUSH );
399         wc.lpszMenuName = "MENU_FST";
400         wc.lpszClassName = "FST";
401
402         if (!RegisterClassA(&wc)){
403                 return 1;
404         }
405
406         if (CreateThread (NULL, 0, gui_event_loop, NULL, 0, NULL) == NULL) {
407                 fst_error ("could not create new thread proxy");
408                 return -1;
409         }
410
411         return 0;
412 }
413
414 int
415 fst_run_editor (FST* fst)
416 {
417         /* Add the FST to the list of all that should be handled by the GUI thread */
418
419         pthread_mutex_lock (&plugin_mutex);
420
421         if (fst_first == NULL) {
422                 fst_first = fst;
423         } else {
424                 FST* p = fst_first;
425                 while (p->next) {
426                         p = p->next;
427                 }
428                 p->next = fst;
429         }
430
431         if (!PostThreadMessageA (gui_thread_id, WM_USER, 0, 0)) {
432                 fst_error ("could not post message to gui thread");
433                 return -1;
434         }
435
436         pthread_mutex_unlock (&plugin_mutex);
437
438         /* wait for the plugin editor window to be created (or not) */
439
440         pthread_mutex_lock (&fst->lock);
441         if (!fst->window) {
442                 pthread_cond_wait (&fst->window_status_change, &fst->lock);
443         } 
444         pthread_mutex_unlock (&fst->lock);
445
446         if (!fst->window) {
447                 fst_error ("no window created for VST plugin editor");
448                 return -1;
449         }
450
451         return 0;
452 }
453
454 FSTHandle*
455 fst_load (const char *path)
456 {
457         char* buf;
458         FSTHandle* fhandle;
459         char* period;
460
461         fhandle = fst_handle_new ();
462         
463         // XXX: Would be nice to find the correct call for this.
464         //      if the user does not configure Z: to be / we are doomed :(
465
466         if (strstr (path, ".dll") == NULL) {
467
468                 buf = (char *) malloc (strlen (path) + 7);
469
470                 if( path[0] == '/' ) {
471                     sprintf (buf, "Z:%s.dll", path);
472                 } else {
473                     sprintf (buf, "%s.dll", path);
474                 }
475
476                 fhandle->nameptr = strdup (path);
477
478         } else {
479
480                 buf = (char *) malloc (strlen (path) + 3);
481
482                 if( path[0] == '/' ) {
483                     sprintf (buf, "Z:%s", path);
484                 } else {
485                     sprintf (buf, "%s", path);
486                 }
487
488                 fhandle->nameptr = strdup (path);
489         }
490         
491         fhandle->name = basename (fhandle->nameptr);
492
493         /* strip off .dll */
494
495         if ((period = strrchr (fhandle->name, '.')) != NULL) {
496                 *period = '\0';
497         }
498
499         if ((fhandle->dll = LoadLibraryA (buf)) == NULL) {
500                 fst_unload (fhandle);
501                 return NULL;
502         }
503
504         if ((fhandle->main_entry = GetProcAddress (fhandle->dll, "main")) == NULL) {
505                 fst_unload (fhandle);
506                 return NULL;
507         }
508
509         return fhandle;
510 }
511
512 int
513 fst_unload (FSTHandle* fhandle)
514 {
515         if (fhandle->plugincnt) {
516                 return -1;
517         }
518
519         if (fhandle->dll) {
520                 FreeLibrary (fhandle->dll);
521                 fhandle->dll = NULL;
522         }
523
524         if (fhandle->nameptr) {
525                 free (fhandle->nameptr);
526                 fhandle->name = NULL;
527         }
528         
529         free (fhandle);
530         return 0;
531 }
532
533 FST*
534 fst_instantiate (FSTHandle* fhandle, audioMasterCallback amc, void* userptr)
535 {
536         FST* fst = fst_new ();
537
538         if( fhandle == NULL ) {
539             fst_error( "the handle was NULL\n" );
540             return NULL;
541         }
542
543         if ((fst->plugin = fhandle->main_entry (amc)) == NULL)  {
544                 fst_error ("%s could not be instantiated\n", fhandle->name);
545                 free (fst);
546                 return NULL;
547         }
548         
549         fst->handle = fhandle;
550         fst->plugin->user = userptr;
551                 
552         if (fst->plugin->magic != kEffectMagic) {
553                 fst_error ("%s is not a VST plugin\n", fhandle->name);
554                 free (fst);
555                 return NULL;
556         }
557         
558         fst->plugin->dispatcher (fst->plugin, effOpen, 0, 0, 0, 0);
559         //fst->plugin->dispatcher (fst->plugin, effMainsChanged, 0, 0, NULL, 0);
560
561         fst->handle->plugincnt++;
562
563         return fst;
564 }
565
566 void
567 fst_close (FST* fst)
568 {
569         fst_destroy_editor (fst);
570
571         fst->plugin->dispatcher (fst->plugin, effMainsChanged, 0, 0, NULL, 0);
572         fst->plugin->dispatcher (fst->plugin, effClose, 0, 0, 0, 0);
573
574         if (fst->handle->plugincnt) {
575                 --fst->handle->plugincnt;
576         }
577 }
578
579 int
580 fst_get_XID (FST* fst)
581 {
582         return fst->xid;
583 }