a1a7d73909c7b40571ebbe79fc72101c539cdad3
[ardour.git] / libs / ardour / vstfxwin.cc
1 /******************************************************************/
2 /** VSTFX - An engine based on FST for handling linuxVST plugins **/
3 /******************************************************************/
4
5 /*This is derived from the original FST (C code) with some tweaks*/
6
7
8 /** EDITOR tab stops at 4 **/
9
10 #include <stdlib.h>
11 #include <stdio.h>
12 #include <jack/jack.h>
13 #include <jack/thread.h>
14 #include <libgen.h>
15
16 #include <pthread.h>
17 #include <signal.h>
18 #include <glib.h>
19
20 #include <ardour/vstfx.h>
21
22 #include <X11/X.h>
23 #include <X11/Xlib.h>
24 #include <dlfcn.h>
25 #include <string.h>
26 #include <time.h>
27 #include <unistd.h>
28 #include <pthread.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
39 static VSTFX* vstfx_first = NULL;
40
41 const char magic[] = "VSTFX Plugin State v002";
42
43 int  gui_thread_id = 0;
44 static int gui_quit = 0;
45
46 /*This will be our connection to X*/
47
48 Display* LXVST_XDisplay = NULL;
49
50 /*The thread handle for the GUI event loop*/
51
52 pthread_t LXVST_gui_event_thread;
53
54 #define DELAYED_WINDOW 1
55
56 /*Util functions to get the value of a property attached to an XWindow*/
57
58 bool LXVST_xerror;
59
60 int TempErrorHandler(Display *display, XErrorEvent *e)
61 {
62         LXVST_xerror = true;
63         
64         return 0;
65 }
66
67 #ifdef LXVST_32BIT
68
69 int getXWindowProperty(Window window, Atom atom)
70 {
71         int result = 0;
72         int userSize;
73         unsigned long bytes;
74         unsigned long userCount;
75         unsigned char *data;
76         Atom userType;
77         LXVST_xerror = false;
78         
79         /*Use our own Xerror handler while we're in here - in an
80         attempt to stop the brain dead default Xerror behaviour of
81         qutting the entire application because of e.g. an invalid
82         window ID*/
83         
84         XErrorHandler olderrorhandler = XSetErrorHandler(TempErrorHandler);
85         
86         XGetWindowProperty(     LXVST_XDisplay,                 //The display
87                                                 window,                                 //The Window
88                                                 atom,                                   //The property
89                                                 0,                                              //Offset into the data
90                                                 1,                                              //Number of 32Bit chunks of data
91                                                 false,                                  //false = don't delete the property
92                                                 AnyPropertyType,                //Required property type mask
93                                                 &userType,                              //Actual type returned
94                                                 &userSize,                              //Actual format returned
95                                                 &userCount,                             //Actual number of items stored in the returned data
96                                                 &bytes,                                 //Number of bytes remaining if a partial read
97                                                 &data);                                 //The actual data read
98                                                 
99         if(LXVST_xerror == false && userCount == 1)
100                 result = *(int*)data;
101                 
102         XSetErrorHandler(olderrorhandler);
103         
104         /*Hopefully this will return zero if the property is not set*/
105         
106         return result;
107 }
108
109 #endif
110
111 #ifdef LXVST_64BIT
112
113 /********************************************************************/
114 /* This is untested - have no 64Bit plugins which use this          */
115 /* system of passing an eventProc address                           */
116 /********************************************************************/
117
118 long getXWindowProperty(Window window, Atom atom)
119 {
120         long result = 0;
121         int userSize;
122         unsigned long bytes;
123         unsigned long userCount;
124         unsigned char *data;
125         Atom userType;
126         LXVST_xerror = false;
127         
128         /*Use our own Xerror handler while we're in here - in an
129         attempt to stop the brain dead default Xerror behaviour of
130         qutting the entire application because of e.g. an invalid
131         window ID*/
132         
133         XErrorHandler olderrorhandler = XSetErrorHandler(TempErrorHandler);
134         
135         XGetWindowProperty(     LXVST_XDisplay, 
136                                                 window, 
137                                                 atom,
138                                                 0,
139                                                 2,
140                                                 false,
141                                                 AnyPropertyType, 
142                                                 &userType,
143                                                 &userSize,
144                                                 &userCount,
145                                                 &bytes,
146                                                 &data);
147                                                 
148         if(LXVST_xerror == false && userCount == 1)
149                 result = *(long*)data;
150                 
151         XSetErrorHandler(olderrorhandler);
152         
153         /*Hopefully this will return zero if the property is not set*/
154         
155         return result;
156 }
157
158 #endif
159
160 /*The event handler - called from within the main GUI thread to
161 dispatch events to any VST UIs which have callbacks stuck to them*/
162
163 static void dispatch_x_events(XEvent* event, VSTFX* vstfx)
164 {
165         /*Handle some of the Events we might be interested in*/
166         
167         switch(event->type)
168         {
169                 /*Configure event - when the window is resized or first drawn*/
170                         
171                 case ConfigureNotify:
172                 {
173                         Window window = event->xconfigure.event;
174                         
175                         int width = event->xconfigure.width;
176                         int height = event->xconfigure.height;
177                         
178                         /*If we get a config notify on the parent window XID then we need to see
179                         if the size has been changed - some plugins re-size their UI window e.g.
180                         when opening a preset manager (you might think that should be spawned as a new window...) */
181                         
182                         /*if the size has changed, we flag this so that in lxvst_pluginui.cc we can make the
183                         change to the GTK parent window in ardour, from its UI thread*/ 
184                         
185                         if(window == (Window)(vstfx->window))
186                         {
187                                 if((width!=vstfx->width) || (height!=vstfx->height))
188                                 {
189                                         vstfx->width = width;
190                                         vstfx->height = height;
191                                         vstfx->want_resize = 1;
192                                         
193                                         /*QUIRK : Loomer plugins not only resize the UI but throw it into some random
194                                         position at the same time. We need to re-position the window at the origin of
195                                         the parent window*/
196                                         
197                                         if(vstfx->plugin_ui_window)
198                                                 XMoveWindow(LXVST_XDisplay, vstfx->plugin_ui_window, 0, 0);
199                                 }
200                         }
201                         
202                         break;
203                         
204                 }
205                 
206                 /*Reparent Notify - when the plugin UI is reparented into
207                 our Host Window we will get an event here... probably... */
208                 
209                 case ReparentNotify:
210                 {
211                         Window ParentWindow = event->xreparent.parent;
212                         
213                         /*If the ParentWindow matches the window for the vstfx instance then
214                         the Child window must be the XID of the pluginUI window created by the
215                         plugin, so we need to see if it has a callback stuck to it, and if so
216                         set that up in the vstfx */
217                         
218                         /***********************************************************/
219                         /* 64Bit --- This mechanism is not 64Bit compatible at the */
220                         /* present time                                            */
221                         /***********************************************************/
222                         
223                         if(ParentWindow == (Window)(vstfx->window))
224                         {
225                                 Window PluginUIWindowID = event->xreparent.window;
226                                 
227                                 vstfx->plugin_ui_window = PluginUIWindowID;
228 #ifdef LXVST_32BIT
229                                 int result = getXWindowProperty(PluginUIWindowID, XInternAtom(LXVST_XDisplay, "_XEventProc", false));
230         
231                                 if(result == 0)
232                                         vstfx->eventProc = NULL;
233                                 else
234                                         vstfx->eventProc = (void (*) (void* event))result;
235 #endif
236 #ifdef LXVST_64BIT
237                                 long result = getXWindowProperty(PluginUIWindowID, XInternAtom(LXVST_XDisplay, "_XEventProc", false));
238         
239                                 if(result == 0)
240                                         vstfx->eventProc = NULL;
241                                 else
242                                         vstfx->eventProc = (void (*) (void* event))result;
243 #endif
244                         }
245                         break;
246                 }
247                 
248                 case ClientMessage:
249                 {
250                         Window window = event->xany.window;
251                         Atom message_type = event->xclient.message_type;
252                         
253                         /*The only client message we are interested in is to signal
254                         that the plugin parent window is now valid and can be passed
255                         to effEditOpen when the editor is launched*/
256                         
257                         if(window == (Window)(vstfx->window))
258                         {
259                                 char* message = XGetAtomName(LXVST_XDisplay, message_type);
260                                 
261                                 if(strcmp(message,"LaunchEditor") == 0)
262                                 {
263                                 
264                                         if(event->xclient.data.l[0] == 0x0FEEDBAC)
265                                                 vstfx_launch_editor(vstfx);
266                                 }
267                                 
268                                 XFree(message);
269                         }
270                         break;
271                 }
272                 
273                 default:
274                         break;
275         }
276         
277         /* Some VSTs built with toolkits e.g. JUCE will manager their own UI
278         autonomously in the plugin, running the UI in its own thread, so once
279         we have created a parent window for the plugin, its UI takes care of
280         itself.*/
281         
282         /*Other types register a callback as an Xwindow property on the plugin
283         UI window after they create it.  If that is the case, we need to call it
284         here, passing the XEvent into it*/
285         
286         if(vstfx->eventProc == NULL)
287                 return;
288                                 
289         vstfx->eventProc((void*)event);
290 }
291
292
293 /*Create and return a pointer to a new vstfx instance*/
294
295 static VSTFX* vstfx_new ()
296 {
297         VSTFX* vstfx = (VSTFX*) calloc (1, sizeof (VSTFX));
298         
299         /*Mutexes*/
300         
301         pthread_mutex_init (&vstfx->lock, NULL);
302         pthread_cond_init (&vstfx->window_status_change, NULL);
303         pthread_cond_init (&vstfx->plugin_dispatcher_called, NULL);
304         pthread_cond_init (&vstfx->window_created, NULL);
305
306         /*Safe values*/
307         
308         vstfx->want_program = -1;
309         vstfx->want_chunk = 0;
310         vstfx->current_program = -1;
311         vstfx->n_pending_keys = 0;
312         vstfx->has_editor = 0;
313         vstfx->program_set_without_editor = 0;
314         vstfx->window = 0;
315         vstfx->plugin_ui_window = 0;
316         vstfx->eventProc = NULL;
317         vstfx->extra_data = NULL;
318         vstfx->want_resize = 0;
319         
320         return vstfx;
321 }
322
323 /*Create and return a pointer to a new VSTFX handle*/
324
325 static VSTFXHandle* vstfx_handle_new()
326 {
327         VSTFXHandle* vstfx = (VSTFXHandle*)calloc(1, sizeof (VSTFXHandle));
328         return vstfx;
329 }
330
331 /** This is the main gui event loop for the plugin, we also need to pass
332 any Xevents to all the UI callbacks plugins 'may' have registered on their
333 windows, that is if they don't manage their own UIs **/
334
335 void* gui_event_loop (void* ptr)
336 {
337
338         VSTFX* vstfx;
339         int LXVST_sched_event_timer = 0;
340         int LXVST_sched_timer_interval = 50; //ms
341         XEvent event;
342         
343         /*The 'Forever' loop - runs the plugin UIs etc - based on the FST gui event loop*/
344         
345         while (!gui_quit)
346         {
347                 /* handle window creation requests, destroy requests, 
348                    and run idle callbacks */
349
350                 /*Look at the XEvent queue - if there are any XEvents we need to handle them,
351                 including passing them to all the plugin (eventProcs) we are currently managing*/
352                 
353                 if(LXVST_XDisplay)
354                 {
355                         /*See if there are any events in the queue*/
356                 
357                         int num_events = XPending(LXVST_XDisplay);
358                         
359                         /*process them if there are any*/
360                 
361                         while(num_events)
362                         {
363                                 XNextEvent(LXVST_XDisplay, &event);
364                                 
365                                 /*Call dispatch events, with the event, for each plugin in the linked list*/
366                                 
367                                 for (vstfx = vstfx_first; vstfx; vstfx = vstfx->next)
368                                 {       
369                                         pthread_mutex_lock(&vstfx->lock);
370                                         
371                                         dispatch_x_events(&event, vstfx);
372                                         
373                                         pthread_mutex_unlock(&vstfx->lock);
374                                 }
375                                 
376                                 num_events--;
377                         }
378                 }
379
380                 /*We don't want to use all the CPU.. */
381
382                 usleep(1000);
383                 
384                 LXVST_sched_event_timer++;
385                 
386                 LXVST_sched_event_timer = LXVST_sched_event_timer & 0x00FFFFFF;
387
388                 /*See if its time for us to do a scheduled event pass on all the plugins*/
389
390                 if((LXVST_sched_timer_interval!=0) && (!(LXVST_sched_event_timer% LXVST_sched_timer_interval)))
391                 {
392                         pthread_mutex_lock (&plugin_mutex);
393                     
394 again:
395                         /*Parse through the linked list of plugins*/
396                         
397                         for (vstfx = vstfx_first; vstfx; vstfx = vstfx->next)
398                         {       
399                                 pthread_mutex_lock (&vstfx->lock);
400
401                                 /*Window scheduled for destruction*/
402                                 
403                                 if (vstfx->destroy)
404                                 {
405                                         if (vstfx->window)
406                                         {
407                                                 vstfx->plugin->dispatcher( vstfx->plugin, effEditClose, 0, 0, NULL, 0.0 );
408                                                         
409                                                 XDestroyWindow (LXVST_XDisplay, vstfx->window);
410                                                 vstfx->window = 0;                              //FIXME - probably safe to assume we never have an XID of 0 but not explicitly true
411                                                 vstfx->destroy = FALSE;
412                                         }
413                                         
414                                         vstfx_event_loop_remove_plugin (vstfx);
415                                         vstfx->been_activated = FALSE;
416                                         pthread_cond_signal (&vstfx->window_status_change);
417                                         pthread_mutex_unlock (&vstfx->lock);
418                                         
419                                         goto again;
420                                 } 
421                                 
422                                 /*Window does not yet exist - scheduled for creation*/
423                                 
424                                 if (vstfx->window == 0)         //FIXME - probably safe to assume 0 is not a valid XID but not explicitly true
425                                 {
426                                         if (vstfx_create_editor (vstfx))
427                                         {
428                                                 vstfx_error ("** ERROR ** VSTFX : Cannot create editor for plugin %s", vstfx->handle->name);
429                                                 vstfx_event_loop_remove_plugin (vstfx);
430                                                 pthread_cond_signal (&vstfx->window_status_change);
431                                                 pthread_mutex_unlock (&vstfx->lock);
432                                                 goto again;
433                                         }
434                                         else
435                                         {
436                                                 /* condition/unlock: it was signalled & unlocked in fst_create_editor()   */
437                                         }
438                                 }
439
440                                 /*Scheduled for setting a new program*/
441
442                                 if (vstfx->want_program != -1 )
443                                 {
444                                         if (vstfx->vst_version >= 2)
445                                         {
446                                                 vstfx->plugin->dispatcher (vstfx->plugin, 67 /* effBeginSetProgram */, 0, 0, NULL, 0);
447                                         }
448
449                                         vstfx->plugin->dispatcher (vstfx->plugin, effSetProgram, 0, vstfx->want_program, NULL, 0);
450
451                                         if (vstfx->vst_version >= 2)
452                                         {
453                                                 vstfx->plugin->dispatcher (vstfx->plugin, 68 /* effEndSetProgram */, 0, 0, NULL, 0);
454                                         }
455                                         
456                                         /* did it work? */
457                                         
458                                         vstfx->current_program = vstfx->plugin->dispatcher (vstfx->plugin, 3, /* effGetProgram */ 0, 0, NULL, 0);
459                                         vstfx->want_program = -1; 
460                                 }
461                                 
462                                 /*scheduled call to dispatcher*/
463                                 
464                                 if(vstfx->dispatcher_wantcall)
465                                 {
466                                         vstfx->dispatcher_retval = vstfx->plugin->dispatcher( vstfx->plugin, 
467                                                                                                                                                         vstfx->dispatcher_opcode,
468                                                                                                                                                         vstfx->dispatcher_index,
469                                                                                                                                                         vstfx->dispatcher_val,
470                                                                                                                                                         vstfx->dispatcher_ptr,
471                                                                                                                                                         vstfx->dispatcher_opt );
472                                                                                                                                                         vstfx->dispatcher_wantcall = 0;
473                                         pthread_cond_signal (&vstfx->plugin_dispatcher_called);
474                                 }
475                                 
476                                 /*Call the editor Idle function in the plugin*/
477                                 
478                                 vstfx->plugin->dispatcher (vstfx->plugin, effEditIdle, 0, 0, NULL, 0);
479
480                                 if(vstfx->wantIdle)
481                                         vstfx->plugin->dispatcher (vstfx->plugin, 53, 0, 0, NULL, 0);
482                                         
483                                 pthread_mutex_unlock (&vstfx->lock);
484                         }
485                         pthread_mutex_unlock (&plugin_mutex);
486                 }
487         }
488
489         /*Drop out to here if we set gui_quit to 1 */
490
491         return NULL;
492 }
493
494 /*The VSTFX Init function - this needs to be called before the VSTFX engine
495 can be accessed, it gets the UI thread running, opens a connection to X etc
496 normally started in globals.cc*/
497
498 int vstfx_init (void* ptr)
499 {
500
501         int thread_create_result;
502         
503         pthread_attr_t thread_attributes;
504         
505         /*Init the attribs to defaults*/
506         
507         pthread_attr_init(&thread_attributes);
508         
509         /*Make sure the thread is joinable - this should be the default anyway - 
510         so we can join to it on vstfx_exit*/
511         
512         pthread_attr_setdetachstate(&thread_attributes, PTHREAD_CREATE_JOINABLE);
513         
514
515         /*This is where we need to open a connection to X, and start the GUI thread*/
516         
517         /*Open our connection to X - all linuxVST plugin UIs handled by the LXVST engine
518         will talk to X down this connection - X cannot handle multi-threaded access via
519         the same Display* */
520         
521         if(LXVST_XDisplay==NULL)
522                 LXVST_XDisplay = XOpenDisplay(NULL);    //We might be able to make this open a specific screen etc
523
524         /*Drop out and report the error if we fail to connect to X */
525         
526         if(LXVST_XDisplay==NULL)
527         {
528                 vstfx_error ("** ERROR ** VSTFX: Failed opening connection to X");
529                 
530                 return -1;
531         }
532         
533         /*We have a connection to X - so start the gui event loop*/
534         
535         /*Create the thread - use default attrs for now, don't think we need anything special*/
536         
537         thread_create_result = pthread_create(&LXVST_gui_event_thread, NULL, gui_event_loop, NULL);
538         
539         if(thread_create_result!=0)
540         {
541                 /*There was a problem starting the GUI event thread*/
542                 
543                 vstfx_error ("** ERROR ** VSTFX: Failed starting GUI event thread");
544                 
545                 XCloseDisplay(LXVST_XDisplay);
546                 
547                 return -1;
548         }
549         
550         return 0;
551 }
552
553 /*The vstfx Quit function*/
554
555 void vstfx_exit()
556 {
557         gui_quit = 1;
558         
559         /*We need to pthread_join the gui_thread here so
560         we know when it has stopped*/
561         
562         pthread_join(LXVST_gui_event_thread, NULL);
563 }
564
565 /*Adds a new plugin (VSTFX) instance to the linked list*/
566
567 int vstfx_run_editor (VSTFX* vstfx)
568 {
569         pthread_mutex_lock (&plugin_mutex);
570
571         /*Add the new VSTFX instance to the linked list*/
572
573         if (vstfx_first == NULL)
574         {
575                 vstfx_first = vstfx;
576         }
577         else
578         {
579                 VSTFX* p = vstfx_first;
580                 
581                 while (p->next)
582                 {
583                         p = p->next;
584                 }
585                 p->next = vstfx;
586                 
587                 /*Mark the new end of the list*/
588                 
589                 vstfx->next = NULL;
590         }
591
592         pthread_mutex_unlock (&plugin_mutex);
593
594         /* wait for the plugin editor window to be created (or not) */
595
596         pthread_mutex_lock (&vstfx->lock);
597         
598         if (!vstfx->window)
599         {
600                 pthread_cond_wait (&vstfx->window_status_change, &vstfx->lock);
601         }
602         
603         pthread_mutex_unlock (&vstfx->lock);
604
605         if (!vstfx->window)
606         {
607                 return -1;
608         }
609
610         return 0;
611 }
612
613 /*Set up a call to the plugins 'dispatcher' function*/
614
615 int vstfx_call_dispatcher (VSTFX *vstfx, int opcode, int index, int val, void *ptr, float opt) 
616 {
617         pthread_mutex_lock (&vstfx->lock);
618         
619         /*Set up the opcode and parameters*/
620         
621         vstfx->dispatcher_opcode = opcode;
622         vstfx->dispatcher_index = index;
623         vstfx->dispatcher_val = val;
624         vstfx->dispatcher_ptr = ptr;
625         vstfx->dispatcher_opt = opt;
626         
627         /*Signal that we want the call to happen*/
628         
629         vstfx->dispatcher_wantcall = 1;
630
631         /*Wait for the call to happen*/
632
633         pthread_cond_wait (&vstfx->plugin_dispatcher_called, &vstfx->lock);
634         pthread_mutex_unlock (&vstfx->lock);
635
636         /*Return the result*/
637         
638         return vstfx->dispatcher_retval;
639 }
640
641 /*Creates an editor for the plugin - normally called from within the gui event loop
642 after run_editor has added the plugin (editor) to the linked list*/
643
644 int vstfx_create_editor (VSTFX* vstfx)
645 {
646         Window parent_window;
647         
648         int x_size = 1;
649         int y_size = 1;
650
651         /* Note: vstfx->lock is held while this function is called */
652
653         if (!(vstfx->plugin->flags & effFlagsHasEditor))
654         {
655                 vstfx_error ("** ERROR ** VSTFX: Plugin \"%s\" has no editor", vstfx->handle->name);
656                 return -1;
657         }
658         
659         
660         /*Create an XWindow for the plugin to inhabit*/
661         
662         parent_window = XCreateSimpleWindow(LXVST_XDisplay,
663                                                                                 DefaultRootWindow(LXVST_XDisplay),
664                                                                                 0,
665                                                                                 0,
666                                                                                 x_size,
667                                                                                 y_size,
668                                                                                 0,
669                                                                                 0,
670                                                                                 0);
671                                                                                 
672         /*Select the events we are interested in receiving - we need Substructure notify so that
673         if the plugin resizes its window - e.g. Loomer Manifold then we get a message*/
674         
675         XSelectInput(LXVST_XDisplay, 
676                                 parent_window,
677                                 SubstructureNotifyMask | ButtonPressMask | ButtonReleaseMask | ButtonMotionMask | ExposureMask);
678                                                                                 
679         vstfx->window = parent_window;
680                                                                                 
681         vstfx->xid = parent_window;  //vstfx->xid will be referenced to connect to GTK UI in ardour later
682         
683         /*Because the plugin may be operating on a different Display* to us, and therefore
684         the two event queues can be asynchronous, although we have created the window on
685         our display, we can't guarantee it exists in the server yet, which will
686         cause BadWindow crashes if the plugin tries to use it.
687         
688         It would be nice to use CreateNotify events here, but they don't get
689         through on all window managers, so instead we pass a client message
690         into out queue, after the XCreateWindow.  When this message pops out
691         in our event handler, it will trigger the second stage of plugin
692         Editor instantiation, and by then the Window should be valid...*/
693         
694         XClientMessageEvent event;
695         
696         /*Create an atom to identify our message (only if it doesn't already exist)*/
697         
698         Atom WindowActiveAtom = XInternAtom(LXVST_XDisplay, "LaunchEditor", false);
699         
700         event.type = ClientMessage;
701         event.send_event = true;
702         event.window = parent_window;
703         event.message_type = WindowActiveAtom;
704
705         event.format = 32;                                              //Data format
706         event.data.l[0] = 0x0FEEDBAC;                   //Something we can recognize later
707         
708         /*Push the event into the queue on our Display*/
709         
710         XSendEvent(LXVST_XDisplay, parent_window, FALSE, NoEventMask, (XEvent*)&event);
711
712         /*Unlock - and we are done for the first part of staring the Editor...*/
713         
714         pthread_mutex_unlock (&vstfx->lock);
715         
716         return 0;
717 }
718
719 int vstfx_launch_editor(VSTFX* vstfx)
720 {
721         /*This is the second stage of launching the editor (see vstfx_create editor)
722         we get called here in response to receiving the ClientMessage on our Window,
723         therefore it's about as safe (as can be) to assume that the Window we created
724         is now valid in the XServer and can be passed to the plugin in effEditOpen
725         without generating BadWindow errors when the plugin reparents itself into our
726         parent window*/
727         
728         if(vstfx->been_activated)
729                 return 0;
730         
731         Window parent_window;
732         struct ERect* er;
733         
734         int x_size = 1;
735         int y_size = 1;
736         
737         parent_window = vstfx->window;
738         
739         /*Open the editor - Bah! we have to pass the int windowID as a void pointer - yuck
740         it gets cast back to an int as the parent window XID in the plugin - and we have to pass the
741         Display* as a long */
742         
743         /**************************************************************/
744         /* 64Bit --- parent window is an int passed as a void* so     */
745         /* that should be ok for 64Bit machines                       */
746         /*                                                            */
747         /* Display is passed in as a long - ok on arch's where sizeof */
748         /* long = 8                                                   */
749         /*                                                            */
750         /* Most linux VST plugins open a connection to X on their own */
751         /* Display anyway so it may not matter                        */
752         /*                                                            */
753         /* linuxDSP VSTs don't use the host Display* at all           */
754         /**************************************************************/
755         
756         vstfx->plugin->dispatcher (vstfx->plugin, effEditOpen, 0, (long)LXVST_XDisplay, (void*)(parent_window), 0 );
757         
758         /*QUIRK - some plugins need a slight delay after opening the editor before you can
759         ask the window size or they might return zero - specifically discoDSP */
760         
761         usleep(100000);
762         
763         /*Now we can find out how big the parent window should be (and try) to resize it*/
764         
765         vstfx->plugin->dispatcher (vstfx->plugin, effEditGetRect, 0, 0, &er, 0 );
766
767         x_size = er->right - er->left;
768         y_size = er->bottom - er->top;
769         
770         vstfx->width =  x_size;
771         vstfx->height =  y_size;
772         
773         XResizeWindow(LXVST_XDisplay, parent_window, x_size, y_size);
774         
775         XFlush (LXVST_XDisplay);
776         
777         /*Not sure if we need to map the window or if the plugin will do it for us
778         it should be ok because XReparentWindow generates a Map event*/
779         
780         /*mark the editor as activated - mainly so that vstfx_get_XID
781         will know it is valid*/
782
783         vstfx->been_activated = TRUE;
784         
785         pthread_cond_signal (&vstfx->window_status_change);
786         return 0;
787 }
788
789 /*May not be needed in the XLib version*/
790
791 void vstfx_move_window_into_view (VSTFX* vstfx)
792 {
793
794         /*This is probably the equivalent of Mapping an XWindow
795         but we most likely don't need it because the window
796         will be Mapped by XReparentWindow*/
797
798 }
799
800 /*Destroy the editor window*/
801
802 void vstfx_destroy_editor (VSTFX* vstfx)
803 {
804         pthread_mutex_lock (&vstfx->lock);
805         if (vstfx->window)
806         {
807                 vstfx->destroy = TRUE;
808                 pthread_cond_wait (&vstfx->window_status_change, &vstfx->lock);
809         }
810         pthread_mutex_unlock (&vstfx->lock);
811 }
812
813 /*Remove a vstfx instance from the linked list parsed by the
814 event loop*/
815
816 void vstfx_event_loop_remove_plugin (VSTFX* vstfx)
817 {
818         /*This only ever gets called from within our GUI thread
819         so we don't need to lock here - if we did there would be
820         a deadlock anyway*/
821         
822         VSTFX* p;
823         VSTFX* prev;
824         
825         for(p = vstfx_first, prev = NULL; p; prev = p, p = p->next)
826         {
827                 if(p == vstfx)
828                 {
829                         if(prev)
830                         {
831                                 prev->next = p->next;
832                                 break;
833                         }
834                 }
835         }
836
837         if (vstfx_first == vstfx)
838                 vstfx_first = vstfx_first->next;
839 }
840
841 /*This loads the plugin shared library*/
842
843 void* vstfx_load_vst_library(const char* path)
844 {
845         void* dll;
846         char* full_path;
847         char* envdup;
848         char* lxvst_path;
849         size_t len1;
850         size_t len2;
851
852         /*Try and load the shared library pointed to by the path - 
853         NOTE: You have to give RTLD_LAZY or RTLD_NOW to dlopen or
854         you get some occasional failures to load - dlerror reports
855         invalid arguments*/
856
857         if ((dll = dlopen (path, RTLD_LOCAL | RTLD_LAZY)) != NULL)
858                 return dll;
859                 
860         /*We didn't find the library so try and get the path specified in the
861         env variable LXVST_PATH*/
862
863         envdup = getenv ("LXVST_PATH");
864         
865         /*Path not specified - not much more we can do*/
866         
867         if (envdup == NULL)
868                 return NULL;
869         
870         /*Copy the path into envdup*/
871                 
872         envdup = strdup (envdup);
873         
874         if (envdup == NULL)
875                 return NULL;
876                 
877         len2 = strlen(path);
878
879         /*Try all the possibilities in the path - deliminated by : */
880
881         lxvst_path = strtok (envdup, ":");
882         
883         while (lxvst_path != NULL)
884         {
885                 vstfx_error ("\"%s\"", lxvst_path);
886                 len1 = strlen(lxvst_path);
887                 
888                 full_path = (char*)malloc(len1 + 1 + len2 + 1);
889                 memcpy(full_path, lxvst_path, len1);
890                 full_path[len1] = '/';
891                 memcpy(full_path + len1 + 1, path, len2);
892                 full_path[len1 + 1 + len2] = '\0';
893
894                 /*Try and load the library*/
895
896                 if ((dll = dlopen(full_path, RTLD_LOCAL | RTLD_LAZY)) != NULL)
897                 {
898                         /*Succeeded */
899                         break;
900                 }
901         
902                 /*Try again*/
903
904                 lxvst_path = strtok (NULL, ":");
905         }
906
907         /*Free the path*/
908
909         free(envdup);
910
911         return dll;
912 }
913
914 /*This loads up a plugin, given the path to its .so file and
915  finds its main entry point etc*/
916
917 VSTFXHandle* vstfx_load (const char *path)
918 {
919         char* buf = NULL;
920         VSTFXHandle* fhandle;
921         int i;
922         
923         /*Create a new handle we can use to reference the plugin*/
924
925         fhandle = vstfx_handle_new();
926         
927         /*See if we have .so appended to the path - if not we need to make sure it is added*/
928         
929         if (strstr (path, ".so") == NULL)
930         {
931
932                 /*Append the .so to the path - Make sure the path has enough space*/
933                 
934                 buf = (char *)malloc(strlen(path) + 4); //The .so and a terminating zero
935
936                 sprintf (buf, "%s.so", path);
937                 
938                 fhandle->nameptr = strdup (path);
939
940         }
941         else
942         {
943                 /*We already have .so appened to the filename*/
944                 
945                 buf = strdup(path);
946                 
947                 fhandle->nameptr = strdup (path);
948         }
949         
950         /*Use basename to shorten the path and then strip off the .so - the old VST problem,
951         we don't know anything about its name until we load and instantiate the plugin
952         which we don't want to do at this point*/
953         
954         for(i=0; i < (int)strlen(fhandle->nameptr); i++)
955         {
956                 if(fhandle->nameptr[i] == '.')
957                         fhandle->nameptr[i] = 0;
958         }
959                         
960         
961         fhandle->name = basename (fhandle->nameptr);
962
963         /*call load_vstfx_library to actually load the .so into memory*/
964
965         if ((fhandle->dll = vstfx_load_vst_library (buf)) == NULL)
966         {
967                 vstfx_unload (fhandle);
968                 
969                 free(buf);
970                 
971                 return NULL;
972         }
973
974         /*Find the main entry point into the plugin*/
975
976         if ((fhandle->main_entry = (main_entry_t) dlsym(fhandle->dll, "main")) == NULL)
977         {
978                 /*If it can't be found, unload the plugin and return a NULL handle*/
979                 
980                 vstfx_unload (fhandle);
981                 
982                 free(buf);
983                 
984                 return NULL;
985         }
986
987         free(buf);
988
989         /*return the handle of the plugin*/
990
991         return fhandle;
992 }
993
994 /*This unloads a plugin*/
995
996 int vstfx_unload (VSTFXHandle* fhandle)
997 {
998         if (fhandle->plugincnt)
999         {
1000                 /*Still have plugin instances - can't unload the library
1001                 - actually dlclose keeps an instance count anyway*/
1002                 
1003                 return -1;
1004         }
1005
1006         /*Valid plugin loaded?*/
1007
1008         if (fhandle->dll)
1009         {
1010                 dlclose(fhandle->dll);
1011                 fhandle->dll = NULL;
1012         }
1013
1014         if (fhandle->nameptr)
1015         {
1016                 free (fhandle->nameptr);
1017                 fhandle->name = NULL;
1018         }
1019         
1020         /*Don't need the plugin handle any more*/
1021         
1022         free (fhandle);
1023         return 0;
1024 }
1025
1026 /*This instantiates a plugin*/
1027
1028 VSTFX* vstfx_instantiate (VSTFXHandle* fhandle, audioMasterCallback amc, void* userptr)
1029 {
1030         VSTFX* vstfx = vstfx_new ();
1031
1032         if(fhandle == NULL)
1033         {
1034             vstfx_error( "** ERROR ** VSTFX : The handle was NULL\n" );
1035             return NULL;
1036         }
1037
1038         if ((vstfx->plugin = fhandle->main_entry (amc)) == NULL) 
1039         {
1040                 vstfx_error ("** ERROR ** VSTFX : %s could not be instantiated :(\n", fhandle->name);
1041                 free (vstfx);
1042                 return NULL;
1043         }
1044         
1045         vstfx->handle = fhandle;
1046         vstfx->plugin->user = userptr;
1047                 
1048         if (vstfx->plugin->magic != kEffectMagic)
1049         {
1050                 vstfx_error ("** ERROR ** VSTFX : %s is not a VST plugin\n", fhandle->name);
1051                 free (vstfx);
1052                 return NULL;
1053         }
1054         
1055         vstfx->plugin->dispatcher (vstfx->plugin, effOpen, 0, 0, 0, 0);
1056         
1057         /*May or May not need to 'switch the plugin on' here - unlikely
1058         since FST doesn't and most plugins start up 'On' by default - I think this is the least of our worries*/
1059         
1060         //vstfx->plugin->dispatcher (vstfx->plugin, effMainsChanged, 0, 1, NULL, 0);
1061         
1062         vstfx->vst_version = vstfx->plugin->dispatcher (vstfx->plugin, effGetVstVersion, 0, 0, 0, 0);
1063         
1064         vstfx->handle->plugincnt++;
1065         vstfx->wantIdle = 0;
1066         
1067         return vstfx;
1068 }
1069
1070 /*Close a vstfx instance*/
1071
1072 void vstfx_close (VSTFX* vstfx)
1073 {
1074         vstfx_destroy_editor(vstfx);
1075         
1076         if(vstfx->plugin)
1077         {
1078                 vstfx->plugin->dispatcher (vstfx->plugin, effMainsChanged, 0, 0, NULL, 0);
1079                 
1080                 /*Calling dispatcher with effClose will cause the plugin's destructor to
1081                 be called, which will also remove the editor if it exists*/
1082                 
1083                 vstfx->plugin->dispatcher (vstfx->plugin, effClose, 0, 0, 0, 0);
1084         }
1085
1086         if (vstfx->handle->plugincnt)
1087                         vstfx->handle->plugincnt--;
1088                 
1089         /*vstfx_unload will unload the dll if the instance count allows - 
1090         we need to do this because some plugins keep their own instance count
1091         and (JUCE) manages the plugin UI in its own thread.  When the plugins
1092         internal instance count reaches zero, JUCE stops the UI thread and won't
1093         restart it until the next time the library is loaded.  If we don't unload
1094         the lib JUCE will never restart*/
1095         
1096         
1097         if (vstfx->handle->plugincnt)
1098         {
1099                 return;
1100         }
1101         
1102         /*Valid plugin loaded - so we can unload it and NULL the pointer
1103         to it.  We can't free the handle here because we don't know what else
1104         might need it.  It should be / is freed when the plugin is deleted*/
1105
1106         if (vstfx->handle->dll)
1107         {
1108                 dlclose(vstfx->handle->dll); //dlclose keeps its own reference count
1109                 vstfx->handle->dll = NULL;
1110         }
1111 }
1112
1113
1114 /*Get the XID of the plugin editor window*/
1115
1116 int vstfx_get_XID (VSTFX* vstfx)
1117 {
1118         int id;
1119         
1120         /*Wait for the lock to become free - otherwise
1121         the window might be in the process of being
1122         created and we get bad Window errors when trying
1123         to embed it in the GTK UI*/
1124         
1125         pthread_mutex_lock(&vstfx->lock);
1126         
1127         /*The Window may be scheduled for creation
1128         but not actually created by the gui_event_loop yet - 
1129         
1130         spin here until it has been activated.  Possible
1131         deadlock if the window never gets activated but
1132         should not be called here if the window doesn't
1133         exist or will never exist*/
1134         
1135         while(!(vstfx->been_activated))
1136                 usleep(1000);
1137         
1138         id = vstfx->xid;
1139         
1140         pthread_mutex_unlock(&vstfx->lock);
1141         
1142         /*Finally it might be safe to return the ID - 
1143         problems will arise if we return either a zero ID
1144         and GTK tries to socket it or if we return an ID
1145         which hasn't yet become real to the server*/
1146         
1147         return id;
1148 }
1149
1150 float htonf (float v)
1151 {
1152       float result;
1153       char * fin = (char*)&v;
1154       char * fout = (char*)&result;
1155       fout[0] = fin[3];
1156       fout[1] = fin[2];
1157       fout[2] = fin[1];
1158       fout[3] = fin[0];
1159       return result;
1160 }
1161
1162
1163 /*load_state and save_state do not appear to be needed (yet) in ardour
1164 - untested at the moment, these are just replicas of the fst code*/
1165
1166
1167 #if 0
1168 int vstfx_load_state (VSTFX* vstfx, char * filename)
1169 {
1170         FILE* f = fopen (filename, "rb");
1171         if(f)
1172         {
1173                 char testMagic[sizeof (magic)];
1174                 fread (&testMagic, sizeof (magic), 1, f);
1175                 if (strcmp (testMagic, magic))
1176                 {
1177                         printf ("File corrupt\n");
1178                         return FALSE;
1179                 }
1180
1181                 char productString[64];
1182                 char vendorString[64];
1183                 char effectName[64];
1184                 char testString[64];
1185                 unsigned length;
1186                 int success;
1187
1188                 fread (&length, sizeof (unsigned), 1, f);
1189                 length = htonl (length);
1190                 fread (productString, length, 1, f);
1191                 productString[length] = 0;
1192                 printf ("Product string: %s\n", productString);
1193
1194                 success = vstfx_call_dispatcher(vstfx, effGetProductString, 0, 0, testString, 0);
1195                 
1196                 if (success == 1)
1197                 {
1198                         if (strcmp (testString, productString) != 0)
1199                         {
1200                                 printf ("Product string mismatch! Plugin has: %s\n", testString);
1201                                 fclose (f);
1202                                 return FALSE;
1203                         }
1204                 }
1205                 else if (length != 0)
1206                 {
1207                         printf ("Product string mismatch! Plugin has none.\n", testString);
1208                         fclose (f);
1209                         return FALSE;
1210                 }
1211
1212                 fread (&length, sizeof (unsigned), 1, f);
1213                 length = htonl (length);
1214                 fread (effectName, length, 1, f);
1215                 effectName[length] = 0;
1216                 printf ("Effect name: %s\n", effectName);
1217
1218                 success = vstfx_call_dispatcher(vstfx, effGetEffectName, 0, 0, testString, 0);
1219                 
1220                 if(success == 1)
1221                 {
1222                         if(strcmp(testString, effectName)!= 0)
1223                         {
1224                                 printf ("Effect name mismatch! Plugin has: %s\n", testString);
1225                                 fclose (f);
1226                                 return FALSE;
1227                         }
1228                 }
1229                 else if(length != 0)
1230                 {
1231                         printf ("Effect name mismatch! Plugin has none.\n", testString);
1232                         fclose (f);
1233                         return FALSE;
1234                 }
1235
1236                 fread (&length, sizeof (unsigned), 1, f);
1237                 length = htonl (length);
1238                 fread (vendorString, length, 1, f);
1239                 vendorString[length] = 0;
1240                 
1241                 printf ("Vendor string: %s\n", vendorString);
1242
1243                 success = vstfx_call_dispatcher(vstfx, effGetVendorString, 0, 0, testString, 0);
1244                 if(success == 1)
1245                 {
1246                         if (strcmp(testString, vendorString)!= 0)
1247                         {
1248                                 printf ("Vendor string mismatch! Plugin has: %s\n", testString);
1249                                 fclose (f);
1250                                 return FALSE;
1251                         }
1252                 }
1253                 else if(length != 0)
1254                 {
1255                         printf ("Vendor string mismatch! Plugin has none.\n", testString);
1256                         fclose (f);
1257                         return FALSE;
1258                 }
1259
1260                 int numParam;
1261                 unsigned i;
1262                 fread (&numParam, sizeof (int), 1, f);
1263                 numParam = htonl (numParam);
1264                 
1265                 for (i = 0; i < numParam; ++i)
1266                 {
1267                         float val;
1268                         fread (&val, sizeof (float), 1, f);
1269                         val = htonf (val);
1270
1271                         pthread_mutex_lock(&vstfx->lock );
1272                         vstfx->plugin->setParameter(vstfx->plugin, i, val);
1273                         pthread_mutex_unlock(&vstfx->lock );
1274                 }
1275
1276                 int bytelen;
1277                 
1278                 fread (&bytelen, sizeof (int), 1, f);
1279                 bytelen = htonl (bytelen);
1280                 
1281                 if (bytelen)
1282                 {
1283                         char * buf = malloc (bytelen);
1284                         fread (buf, bytelen, 1, f);
1285
1286                         vstfx_call_dispatcher(vstfx, 24, 0, bytelen, buf, 0);
1287                         free (buf);
1288                 }
1289         }
1290         else
1291         {
1292                 printf ("Could not open state file\n");
1293                 return FALSE;
1294         }
1295         return TRUE;
1296
1297 }
1298 #endif
1299
1300 int vstfx_save_state (VSTFX* vstfx, char * filename)
1301 {
1302         FILE* f = fopen (filename, "wb");
1303         if (f)
1304         {
1305                 int bytelen;
1306                 int numParams = vstfx->plugin->numParams;
1307                 int i;
1308                 char productString[64];
1309                 char effectName[64];
1310                 char vendorString[64];
1311                 int success;
1312
1313                 /* write header */
1314                 
1315                 fprintf(f, "<plugin_state>\n");
1316
1317                 success = vstfx_call_dispatcher(vstfx, effGetProductString, 0, 0, productString, 0);
1318                 
1319                 if(success == 1)
1320                 {
1321                         fprintf (f, "  <check field=\"productString\" value=\"%s\"/>\n", productString);
1322                 }
1323                 else
1324                 {
1325                         printf ("No product string\n");
1326                 }
1327
1328                 success = vstfx_call_dispatcher(vstfx, effGetEffectName, 0, 0, effectName, 0);
1329                 
1330                 if(success == 1)
1331                 {
1332                         fprintf (f, "  <check field=\"effectName\" value=\"%s\"/>\n", effectName);
1333                         printf ("Effect name: %s\n", effectName);
1334                 }
1335                 else
1336                 {
1337                         printf ("No effect name\n");
1338                 }
1339
1340                 success = vstfx_call_dispatcher(vstfx, effGetVendorString, 0, 0, vendorString, 0);
1341                 
1342                 if( success == 1 )
1343                 {
1344                         fprintf (f, "  <check field=\"vendorString\" value=\"%s\"/>\n", vendorString);
1345                         printf ("Vendor string: %s\n", vendorString);
1346                 }
1347                 else
1348                 {
1349                         printf ("No vendor string\n");
1350                 }
1351
1352
1353                 if(vstfx->plugin->flags & 32 )
1354                 {
1355                         numParams = 0;
1356                 }
1357
1358                 for(i=0; i < numParams; i++)
1359                 {
1360                         float val;
1361                         
1362                         pthread_mutex_lock( &vstfx->lock );
1363                         val = vstfx->plugin->getParameter(vstfx->plugin, i );
1364                         pthread_mutex_unlock( &vstfx->lock );
1365                         fprintf( f, "  <param index=\"%d\" value=\"%f\"/>\n", i, val );
1366                 }
1367
1368                 if(vstfx->plugin->flags & 32 )
1369                 {
1370                         printf( "getting chunk...\n" );
1371                         void * chunk;
1372                         bytelen = vstfx_call_dispatcher(vstfx, 23, 0, 0, &chunk, 0 );
1373                         printf( "got tha chunk..\n" );
1374                         if( bytelen )
1375                         {
1376                                 if( bytelen < 0 )
1377                                 {
1378                                         printf( "Chunke len < 0 !!! Not saving chunk.\n" );
1379                                 }
1380                                 else
1381                                 {
1382                                         //char *encoded = g_base64_encode( chunk, bytelen );
1383                                         //fprintf( f, "  <chunk size=\"%d\">\n    %s\n  </chunk>\n", bytelen, encoded );
1384                                         //g_free( encoded );
1385                                 }
1386                         }
1387                 } 
1388
1389                 fprintf( f, "</plugin_state>\n" );
1390                 fclose( f );
1391         }
1392         else
1393         {
1394                 printf ("Could not open state file\n");
1395                 return FALSE;
1396         }
1397         return TRUE;
1398 }
1399