No-op; formatting.
[ardour.git] / gtk2_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                                         vstfx->dispatcher_retval = vstfx->plugin->dispatcher (
466                                                 vstfx->plugin, 
467                                                 vstfx->dispatcher_opcode,
468                                                 vstfx->dispatcher_index,
469                                                 vstfx->dispatcher_val,
470                                                 vstfx->dispatcher_ptr,
471                                                 vstfx->dispatcher_opt
472                                                 );
473                                         
474                                         vstfx->dispatcher_wantcall = 0;
475                                         pthread_cond_signal (&vstfx->plugin_dispatcher_called);
476                                 }
477                                 
478                                 /*Call the editor Idle function in the plugin*/
479                                 
480                                 vstfx->plugin->dispatcher (vstfx->plugin, effEditIdle, 0, 0, NULL, 0);
481
482                                 if(vstfx->wantIdle)
483                                         vstfx->plugin->dispatcher (vstfx->plugin, 53, 0, 0, NULL, 0);
484                                         
485                                 pthread_mutex_unlock (&vstfx->lock);
486                         }
487                         pthread_mutex_unlock (&plugin_mutex);
488                 }
489         }
490
491         /*Drop out to here if we set gui_quit to 1 */
492
493         return NULL;
494 }
495
496 /*The VSTFX Init function - this needs to be called before the VSTFX engine
497 can be accessed, it gets the UI thread running, opens a connection to X etc
498 normally started in globals.cc*/
499
500 int vstfx_init (void* ptr)
501 {
502
503         int thread_create_result;
504         
505         pthread_attr_t thread_attributes;
506         
507         /*Init the attribs to defaults*/
508         
509         pthread_attr_init(&thread_attributes);
510         
511         /*Make sure the thread is joinable - this should be the default anyway - 
512         so we can join to it on vstfx_exit*/
513         
514         pthread_attr_setdetachstate(&thread_attributes, PTHREAD_CREATE_JOINABLE);
515         
516
517         /*This is where we need to open a connection to X, and start the GUI thread*/
518         
519         /*Open our connection to X - all linuxVST plugin UIs handled by the LXVST engine
520         will talk to X down this connection - X cannot handle multi-threaded access via
521         the same Display* */
522         
523         if(LXVST_XDisplay==NULL)
524                 LXVST_XDisplay = XOpenDisplay(NULL);    //We might be able to make this open a specific screen etc
525
526         /*Drop out and report the error if we fail to connect to X */
527         
528         if(LXVST_XDisplay==NULL)
529         {
530                 vstfx_error ("** ERROR ** VSTFX: Failed opening connection to X");
531                 
532                 return -1;
533         }
534         
535         /*We have a connection to X - so start the gui event loop*/
536         
537         /*Create the thread - use default attrs for now, don't think we need anything special*/
538         
539         thread_create_result = pthread_create(&LXVST_gui_event_thread, NULL, gui_event_loop, NULL);
540         
541         if(thread_create_result!=0)
542         {
543                 /*There was a problem starting the GUI event thread*/
544                 
545                 vstfx_error ("** ERROR ** VSTFX: Failed starting GUI event thread");
546                 
547                 XCloseDisplay(LXVST_XDisplay);
548                 
549                 return -1;
550         }
551         
552         return 0;
553 }
554
555 /*The vstfx Quit function*/
556
557 void vstfx_exit()
558 {
559         gui_quit = 1;
560         
561         /*We need to pthread_join the gui_thread here so
562         we know when it has stopped*/
563         
564         pthread_join(LXVST_gui_event_thread, NULL);
565 }
566
567 /*Adds a new plugin (VSTFX) instance to the linked list*/
568
569 int vstfx_run_editor (VSTFX* vstfx)
570 {
571         pthread_mutex_lock (&plugin_mutex);
572
573         /*Add the new VSTFX instance to the linked list*/
574
575         if (vstfx_first == NULL)
576         {
577                 vstfx_first = vstfx;
578         }
579         else
580         {
581                 VSTFX* p = vstfx_first;
582                 
583                 while (p->next)
584                 {
585                         p = p->next;
586                 }
587                 p->next = vstfx;
588                 
589                 /*Mark the new end of the list*/
590                 
591                 vstfx->next = NULL;
592         }
593
594         pthread_mutex_unlock (&plugin_mutex);
595
596         /* wait for the plugin editor window to be created (or not) */
597
598         pthread_mutex_lock (&vstfx->lock);
599         
600         if (!vstfx->window)
601         {
602                 pthread_cond_wait (&vstfx->window_status_change, &vstfx->lock);
603         }
604         
605         pthread_mutex_unlock (&vstfx->lock);
606
607         if (!vstfx->window)
608         {
609                 return -1;
610         }
611
612         return 0;
613 }
614
615 /*Set up a call to the plugins 'dispatcher' function*/
616
617 int vstfx_call_dispatcher (VSTFX *vstfx, int opcode, int index, int val, void *ptr, float opt) 
618 {
619         pthread_mutex_lock (&vstfx->lock);
620         
621         /*Set up the opcode and parameters*/
622         
623         vstfx->dispatcher_opcode = opcode;
624         vstfx->dispatcher_index = index;
625         vstfx->dispatcher_val = val;
626         vstfx->dispatcher_ptr = ptr;
627         vstfx->dispatcher_opt = opt;
628         
629         /*Signal that we want the call to happen*/
630         
631         vstfx->dispatcher_wantcall = 1;
632
633         /*Wait for the call to happen*/
634
635         pthread_cond_wait (&vstfx->plugin_dispatcher_called, &vstfx->lock);
636         pthread_mutex_unlock (&vstfx->lock);
637
638         /*Return the result*/
639         
640         return vstfx->dispatcher_retval;
641 }
642
643 /*Creates an editor for the plugin - normally called from within the gui event loop
644 after run_editor has added the plugin (editor) to the linked list*/
645
646 int vstfx_create_editor (VSTFX* vstfx)
647 {
648         Window parent_window;
649         
650         int x_size = 1;
651         int y_size = 1;
652
653         /* Note: vstfx->lock is held while this function is called */
654
655         if (!(vstfx->plugin->flags & effFlagsHasEditor))
656         {
657                 vstfx_error ("** ERROR ** VSTFX: Plugin \"%s\" has no editor", vstfx->handle->name);
658                 return -1;
659         }
660         
661         
662         /*Create an XWindow for the plugin to inhabit*/
663         
664         parent_window = XCreateSimpleWindow (
665                 LXVST_XDisplay,
666                 DefaultRootWindow(LXVST_XDisplay),
667                 0,
668                 0,
669                 x_size,
670                 y_size,
671                 0,
672                 0,
673                 0
674                 );
675                                                                                 
676         /*Select the events we are interested in receiving - we need Substructure notify so that
677         if the plugin resizes its window - e.g. Loomer Manifold then we get a message*/
678         
679         XSelectInput(LXVST_XDisplay, 
680                                 parent_window,
681                                 SubstructureNotifyMask | ButtonPressMask | ButtonReleaseMask | ButtonMotionMask | ExposureMask);
682                                                                                 
683         vstfx->window = parent_window;
684                                                                                 
685         vstfx->xid = parent_window;  //vstfx->xid will be referenced to connect to GTK UI in ardour later
686         
687         /*Because the plugin may be operating on a different Display* to us, and therefore
688         the two event queues can be asynchronous, although we have created the window on
689         our display, we can't guarantee it exists in the server yet, which will
690         cause BadWindow crashes if the plugin tries to use it.
691         
692         It would be nice to use CreateNotify events here, but they don't get
693         through on all window managers, so instead we pass a client message
694         into out queue, after the XCreateWindow.  When this message pops out
695         in our event handler, it will trigger the second stage of plugin
696         Editor instantiation, and by then the Window should be valid...*/
697         
698         XClientMessageEvent event;
699         
700         /*Create an atom to identify our message (only if it doesn't already exist)*/
701         
702         Atom WindowActiveAtom = XInternAtom(LXVST_XDisplay, "LaunchEditor", false);
703         
704         event.type = ClientMessage;
705         event.send_event = true;
706         event.window = parent_window;
707         event.message_type = WindowActiveAtom;
708
709         event.format = 32;                                              //Data format
710         event.data.l[0] = 0x0FEEDBAC;                   //Something we can recognize later
711         
712         /*Push the event into the queue on our Display*/
713         
714         XSendEvent(LXVST_XDisplay, parent_window, FALSE, NoEventMask, (XEvent*)&event);
715
716         /*Unlock - and we are done for the first part of staring the Editor...*/
717         
718         pthread_mutex_unlock (&vstfx->lock);
719         
720         return 0;
721 }
722
723 int vstfx_launch_editor(VSTFX* vstfx)
724 {
725         /*This is the second stage of launching the editor (see vstfx_create editor)
726         we get called here in response to receiving the ClientMessage on our Window,
727         therefore it's about as safe (as can be) to assume that the Window we created
728         is now valid in the XServer and can be passed to the plugin in effEditOpen
729         without generating BadWindow errors when the plugin reparents itself into our
730         parent window*/
731         
732         if(vstfx->been_activated)
733                 return 0;
734         
735         Window parent_window;
736         struct ERect* er;
737         
738         int x_size = 1;
739         int y_size = 1;
740         
741         parent_window = vstfx->window;
742         
743         /*Open the editor - Bah! we have to pass the int windowID as a void pointer - yuck
744         it gets cast back to an int as the parent window XID in the plugin - and we have to pass the
745         Display* as a long */
746         
747         /**************************************************************/
748         /* 64Bit --- parent window is an int passed as a void* so     */
749         /* that should be ok for 64Bit machines                       */
750         /*                                                            */
751         /* Display is passed in as a long - ok on arch's where sizeof */
752         /* long = 8                                                   */
753         /*                                                            */
754         /* Most linux VST plugins open a connection to X on their own */
755         /* Display anyway so it may not matter                        */
756         /*                                                            */
757         /* linuxDSP VSTs don't use the host Display* at all           */
758         /**************************************************************/
759         
760         vstfx->plugin->dispatcher (vstfx->plugin, effEditOpen, 0, (long)LXVST_XDisplay, (void*)(parent_window), 0 );
761         
762         /*QUIRK - some plugins need a slight delay after opening the editor before you can
763         ask the window size or they might return zero - specifically discoDSP */
764         
765         usleep(100000);
766         
767         /*Now we can find out how big the parent window should be (and try) to resize it*/
768         
769         vstfx->plugin->dispatcher (vstfx->plugin, effEditGetRect, 0, 0, &er, 0 );
770
771         x_size = er->right - er->left;
772         y_size = er->bottom - er->top;
773         
774         vstfx->width =  x_size;
775         vstfx->height =  y_size;
776         
777         XResizeWindow(LXVST_XDisplay, parent_window, x_size, y_size);
778         
779         XFlush (LXVST_XDisplay);
780         
781         /*Not sure if we need to map the window or if the plugin will do it for us
782         it should be ok because XReparentWindow generates a Map event*/
783         
784         /*mark the editor as activated - mainly so that vstfx_get_XID
785         will know it is valid*/
786
787         vstfx->been_activated = TRUE;
788         
789         pthread_cond_signal (&vstfx->window_status_change);
790         return 0;
791 }
792
793 /*May not be needed in the XLib version*/
794
795 void vstfx_move_window_into_view (VSTFX* vstfx)
796 {
797
798         /*This is probably the equivalent of Mapping an XWindow
799         but we most likely don't need it because the window
800         will be Mapped by XReparentWindow*/
801
802 }
803
804 /*Destroy the editor window*/
805
806 void vstfx_destroy_editor (VSTFX* vstfx)
807 {
808         pthread_mutex_lock (&vstfx->lock);
809         if (vstfx->window)
810         {
811                 vstfx->destroy = TRUE;
812                 pthread_cond_wait (&vstfx->window_status_change, &vstfx->lock);
813         }
814         pthread_mutex_unlock (&vstfx->lock);
815 }
816
817 /*Remove a vstfx instance from the linked list parsed by the
818 event loop*/
819
820 void vstfx_event_loop_remove_plugin (VSTFX* vstfx)
821 {
822         /*This only ever gets called from within our GUI thread
823         so we don't need to lock here - if we did there would be
824         a deadlock anyway*/
825         
826         VSTFX* p;
827         VSTFX* prev;
828         
829         for(p = vstfx_first, prev = NULL; p; prev = p, p = p->next)
830         {
831                 if(p == vstfx)
832                 {
833                         if(prev)
834                         {
835                                 prev->next = p->next;
836                                 break;
837                         }
838                 }
839         }
840
841         if (vstfx_first == vstfx)
842                 vstfx_first = vstfx_first->next;
843 }
844
845 /*This loads the plugin shared library*/
846
847 void* vstfx_load_vst_library(const char* path)
848 {
849         void* dll;
850         char* full_path;
851         char* envdup;
852         char* lxvst_path;
853         size_t len1;
854         size_t len2;
855
856         /*Try and load the shared library pointed to by the path - 
857         NOTE: You have to give RTLD_LAZY or RTLD_NOW to dlopen or
858         you get some occasional failures to load - dlerror reports
859         invalid arguments*/
860
861         if ((dll = dlopen (path, RTLD_LOCAL | RTLD_LAZY)) != NULL)
862                 return dll;
863                 
864         /*We didn't find the library so try and get the path specified in the
865         env variable LXVST_PATH*/
866
867         envdup = getenv ("LXVST_PATH");
868         
869         /*Path not specified - not much more we can do*/
870         
871         if (envdup == NULL)
872                 return NULL;
873         
874         /*Copy the path into envdup*/
875                 
876         envdup = strdup (envdup);
877         
878         if (envdup == NULL)
879                 return NULL;
880                 
881         len2 = strlen(path);
882
883         /*Try all the possibilities in the path - deliminated by : */
884
885         lxvst_path = strtok (envdup, ":");
886         
887         while (lxvst_path != NULL)
888         {
889                 vstfx_error ("\"%s\"", lxvst_path);
890                 len1 = strlen(lxvst_path);
891                 
892                 full_path = (char*)malloc(len1 + 1 + len2 + 1);
893                 memcpy(full_path, lxvst_path, len1);
894                 full_path[len1] = '/';
895                 memcpy(full_path + len1 + 1, path, len2);
896                 full_path[len1 + 1 + len2] = '\0';
897
898                 /*Try and load the library*/
899
900                 if ((dll = dlopen(full_path, RTLD_LOCAL | RTLD_LAZY)) != NULL)
901                 {
902                         /*Succeeded */
903                         break;
904                 }
905         
906                 /*Try again*/
907
908                 lxvst_path = strtok (NULL, ":");
909         }
910
911         /*Free the path*/
912
913         free(envdup);
914
915         return dll;
916 }
917
918 /*This loads up a plugin, given the path to its .so file and
919  finds its main entry point etc*/
920
921 VSTFXHandle* vstfx_load (const char *path)
922 {
923         char* buf = NULL;
924         VSTFXHandle* fhandle;
925         int i;
926         
927         /*Create a new handle we can use to reference the plugin*/
928
929         fhandle = vstfx_handle_new();
930         
931         /*See if we have .so appended to the path - if not we need to make sure it is added*/
932         
933         if (strstr (path, ".so") == NULL)
934         {
935
936                 /*Append the .so to the path - Make sure the path has enough space*/
937                 
938                 buf = (char *)malloc(strlen(path) + 4); //The .so and a terminating zero
939
940                 sprintf (buf, "%s.so", path);
941                 
942                 fhandle->nameptr = strdup (path);
943
944         }
945         else
946         {
947                 /*We already have .so appened to the filename*/
948                 
949                 buf = strdup(path);
950                 
951                 fhandle->nameptr = strdup (path);
952         }
953         
954         /*Use basename to shorten the path and then strip off the .so - the old VST problem,
955         we don't know anything about its name until we load and instantiate the plugin
956         which we don't want to do at this point*/
957         
958         for(i=0; i < (int)strlen(fhandle->nameptr); i++)
959         {
960                 if(fhandle->nameptr[i] == '.')
961                         fhandle->nameptr[i] = 0;
962         }
963                         
964         
965         fhandle->name = basename (fhandle->nameptr);
966
967         /*call load_vstfx_library to actually load the .so into memory*/
968
969         if ((fhandle->dll = vstfx_load_vst_library (buf)) == NULL)
970         {
971                 vstfx_unload (fhandle);
972                 
973                 free(buf);
974                 
975                 return NULL;
976         }
977
978         /*Find the main entry point into the plugin*/
979
980         if ((fhandle->main_entry = (main_entry_t) dlsym(fhandle->dll, "main")) == NULL)
981         {
982                 /*If it can't be found, unload the plugin and return a NULL handle*/
983                 
984                 vstfx_unload (fhandle);
985                 
986                 free(buf);
987                 
988                 return NULL;
989         }
990
991         free(buf);
992
993         /*return the handle of the plugin*/
994
995         return fhandle;
996 }
997
998 /*This unloads a plugin*/
999
1000 int vstfx_unload (VSTFXHandle* fhandle)
1001 {
1002         if (fhandle->plugincnt)
1003         {
1004                 /*Still have plugin instances - can't unload the library
1005                 - actually dlclose keeps an instance count anyway*/
1006                 
1007                 return -1;
1008         }
1009
1010         /*Valid plugin loaded?*/
1011
1012         if (fhandle->dll)
1013         {
1014                 dlclose(fhandle->dll);
1015                 fhandle->dll = NULL;
1016         }
1017
1018         if (fhandle->nameptr)
1019         {
1020                 free (fhandle->nameptr);
1021                 fhandle->name = NULL;
1022         }
1023         
1024         /*Don't need the plugin handle any more*/
1025         
1026         free (fhandle);
1027         return 0;
1028 }
1029
1030 /*This instantiates a plugin*/
1031
1032 VSTFX* vstfx_instantiate (VSTFXHandle* fhandle, audioMasterCallback amc, void* userptr)
1033 {
1034         VSTFX* vstfx = vstfx_new ();
1035
1036         if(fhandle == NULL)
1037         {
1038             vstfx_error( "** ERROR ** VSTFX : The handle was NULL\n" );
1039             return NULL;
1040         }
1041
1042         if ((vstfx->plugin = fhandle->main_entry (amc)) == NULL) 
1043         {
1044                 vstfx_error ("** ERROR ** VSTFX : %s could not be instantiated :(\n", fhandle->name);
1045                 free (vstfx);
1046                 return NULL;
1047         }
1048         
1049         vstfx->handle = fhandle;
1050         vstfx->plugin->user = userptr;
1051                 
1052         if (vstfx->plugin->magic != kEffectMagic)
1053         {
1054                 vstfx_error ("** ERROR ** VSTFX : %s is not a VST plugin\n", fhandle->name);
1055                 free (vstfx);
1056                 return NULL;
1057         }
1058         
1059         vstfx->plugin->dispatcher (vstfx->plugin, effOpen, 0, 0, 0, 0);
1060         
1061         /*May or May not need to 'switch the plugin on' here - unlikely
1062         since FST doesn't and most plugins start up 'On' by default - I think this is the least of our worries*/
1063         
1064         //vstfx->plugin->dispatcher (vstfx->plugin, effMainsChanged, 0, 1, NULL, 0);
1065         
1066         vstfx->vst_version = vstfx->plugin->dispatcher (vstfx->plugin, effGetVstVersion, 0, 0, 0, 0);
1067         
1068         vstfx->handle->plugincnt++;
1069         vstfx->wantIdle = 0;
1070         
1071         return vstfx;
1072 }
1073
1074 /*Close a vstfx instance*/
1075
1076 void vstfx_close (VSTFX* vstfx)
1077 {
1078         vstfx_destroy_editor(vstfx);
1079         
1080         if(vstfx->plugin)
1081         {
1082                 vstfx->plugin->dispatcher (vstfx->plugin, effMainsChanged, 0, 0, NULL, 0);
1083                 
1084                 /*Calling dispatcher with effClose will cause the plugin's destructor to
1085                 be called, which will also remove the editor if it exists*/
1086                 
1087                 vstfx->plugin->dispatcher (vstfx->plugin, effClose, 0, 0, 0, 0);
1088         }
1089
1090         if (vstfx->handle->plugincnt)
1091                         vstfx->handle->plugincnt--;
1092                 
1093         /*vstfx_unload will unload the dll if the instance count allows - 
1094         we need to do this because some plugins keep their own instance count
1095         and (JUCE) manages the plugin UI in its own thread.  When the plugins
1096         internal instance count reaches zero, JUCE stops the UI thread and won't
1097         restart it until the next time the library is loaded.  If we don't unload
1098         the lib JUCE will never restart*/
1099         
1100         
1101         if (vstfx->handle->plugincnt)
1102         {
1103                 return;
1104         }
1105         
1106         /*Valid plugin loaded - so we can unload it and NULL the pointer
1107         to it.  We can't free the handle here because we don't know what else
1108         might need it.  It should be / is freed when the plugin is deleted*/
1109
1110         if (vstfx->handle->dll)
1111         {
1112                 dlclose(vstfx->handle->dll); //dlclose keeps its own reference count
1113                 vstfx->handle->dll = NULL;
1114         }
1115 }
1116
1117
1118 /*Get the XID of the plugin editor window*/
1119
1120 int vstfx_get_XID (VSTFX* vstfx)
1121 {
1122         int id;
1123         
1124         /*Wait for the lock to become free - otherwise
1125         the window might be in the process of being
1126         created and we get bad Window errors when trying
1127         to embed it in the GTK UI*/
1128         
1129         pthread_mutex_lock(&vstfx->lock);
1130         
1131         /*The Window may be scheduled for creation
1132         but not actually created by the gui_event_loop yet - 
1133         
1134         spin here until it has been activated.  Possible
1135         deadlock if the window never gets activated but
1136         should not be called here if the window doesn't
1137         exist or will never exist*/
1138         
1139         while(!(vstfx->been_activated))
1140                 usleep(1000);
1141         
1142         id = vstfx->xid;
1143         
1144         pthread_mutex_unlock(&vstfx->lock);
1145         
1146         /*Finally it might be safe to return the ID - 
1147         problems will arise if we return either a zero ID
1148         and GTK tries to socket it or if we return an ID
1149         which hasn't yet become real to the server*/
1150         
1151         return id;
1152 }
1153
1154 float htonf (float v)
1155 {
1156       float result;
1157       char * fin = (char*)&v;
1158       char * fout = (char*)&result;
1159       fout[0] = fin[3];
1160       fout[1] = fin[2];
1161       fout[2] = fin[1];
1162       fout[3] = fin[0];
1163       return result;
1164 }
1165
1166
1167 /*load_state and save_state do not appear to be needed (yet) in ardour
1168 - untested at the moment, these are just replicas of the fst code*/
1169
1170
1171 #if 0
1172 int vstfx_load_state (VSTFX* vstfx, char * filename)
1173 {
1174         FILE* f = fopen (filename, "rb");
1175         if(f)
1176         {
1177                 char testMagic[sizeof (magic)];
1178                 fread (&testMagic, sizeof (magic), 1, f);
1179                 if (strcmp (testMagic, magic))
1180                 {
1181                         printf ("File corrupt\n");
1182                         return FALSE;
1183                 }
1184
1185                 char productString[64];
1186                 char vendorString[64];
1187                 char effectName[64];
1188                 char testString[64];
1189                 unsigned length;
1190                 int success;
1191
1192                 fread (&length, sizeof (unsigned), 1, f);
1193                 length = htonl (length);
1194                 fread (productString, length, 1, f);
1195                 productString[length] = 0;
1196                 printf ("Product string: %s\n", productString);
1197
1198                 success = vstfx_call_dispatcher(vstfx, effGetProductString, 0, 0, testString, 0);
1199                 
1200                 if (success == 1)
1201                 {
1202                         if (strcmp (testString, productString) != 0)
1203                         {
1204                                 printf ("Product string mismatch! Plugin has: %s\n", testString);
1205                                 fclose (f);
1206                                 return FALSE;
1207                         }
1208                 }
1209                 else if (length != 0)
1210                 {
1211                         printf ("Product string mismatch! Plugin has none.\n", testString);
1212                         fclose (f);
1213                         return FALSE;
1214                 }
1215
1216                 fread (&length, sizeof (unsigned), 1, f);
1217                 length = htonl (length);
1218                 fread (effectName, length, 1, f);
1219                 effectName[length] = 0;
1220                 printf ("Effect name: %s\n", effectName);
1221
1222                 success = vstfx_call_dispatcher(vstfx, effGetEffectName, 0, 0, testString, 0);
1223                 
1224                 if(success == 1)
1225                 {
1226                         if(strcmp(testString, effectName)!= 0)
1227                         {
1228                                 printf ("Effect name mismatch! Plugin has: %s\n", testString);
1229                                 fclose (f);
1230                                 return FALSE;
1231                         }
1232                 }
1233                 else if(length != 0)
1234                 {
1235                         printf ("Effect name mismatch! Plugin has none.\n", testString);
1236                         fclose (f);
1237                         return FALSE;
1238                 }
1239
1240                 fread (&length, sizeof (unsigned), 1, f);
1241                 length = htonl (length);
1242                 fread (vendorString, length, 1, f);
1243                 vendorString[length] = 0;
1244                 
1245                 printf ("Vendor string: %s\n", vendorString);
1246
1247                 success = vstfx_call_dispatcher(vstfx, effGetVendorString, 0, 0, testString, 0);
1248                 if(success == 1)
1249                 {
1250                         if (strcmp(testString, vendorString)!= 0)
1251                         {
1252                                 printf ("Vendor string mismatch! Plugin has: %s\n", testString);
1253                                 fclose (f);
1254                                 return FALSE;
1255                         }
1256                 }
1257                 else if(length != 0)
1258                 {
1259                         printf ("Vendor string mismatch! Plugin has none.\n", testString);
1260                         fclose (f);
1261                         return FALSE;
1262                 }
1263
1264                 int numParam;
1265                 unsigned i;
1266                 fread (&numParam, sizeof (int), 1, f);
1267                 numParam = htonl (numParam);
1268                 
1269                 for (i = 0; i < numParam; ++i)
1270                 {
1271                         float val;
1272                         fread (&val, sizeof (float), 1, f);
1273                         val = htonf (val);
1274
1275                         pthread_mutex_lock(&vstfx->lock );
1276                         vstfx->plugin->setParameter(vstfx->plugin, i, val);
1277                         pthread_mutex_unlock(&vstfx->lock );
1278                 }
1279
1280                 int bytelen;
1281                 
1282                 fread (&bytelen, sizeof (int), 1, f);
1283                 bytelen = htonl (bytelen);
1284                 
1285                 if (bytelen)
1286                 {
1287                         char * buf = malloc (bytelen);
1288                         fread (buf, bytelen, 1, f);
1289
1290                         vstfx_call_dispatcher(vstfx, 24, 0, bytelen, buf, 0);
1291                         free (buf);
1292                 }
1293         }
1294         else
1295         {
1296                 printf ("Could not open state file\n");
1297                 return FALSE;
1298         }
1299         return TRUE;
1300
1301 }
1302 #endif
1303
1304 int vstfx_save_state (VSTFX* vstfx, char * filename)
1305 {
1306         FILE* f = fopen (filename, "wb");
1307         if (f)
1308         {
1309                 int bytelen;
1310                 int numParams = vstfx->plugin->numParams;
1311                 int i;
1312                 char productString[64];
1313                 char effectName[64];
1314                 char vendorString[64];
1315                 int success;
1316
1317                 /* write header */
1318                 
1319                 fprintf(f, "<plugin_state>\n");
1320
1321                 success = vstfx_call_dispatcher(vstfx, effGetProductString, 0, 0, productString, 0);
1322                 
1323                 if(success == 1)
1324                 {
1325                         fprintf (f, "  <check field=\"productString\" value=\"%s\"/>\n", productString);
1326                 }
1327                 else
1328                 {
1329                         printf ("No product string\n");
1330                 }
1331
1332                 success = vstfx_call_dispatcher(vstfx, effGetEffectName, 0, 0, effectName, 0);
1333                 
1334                 if(success == 1)
1335                 {
1336                         fprintf (f, "  <check field=\"effectName\" value=\"%s\"/>\n", effectName);
1337                         printf ("Effect name: %s\n", effectName);
1338                 }
1339                 else
1340                 {
1341                         printf ("No effect name\n");
1342                 }
1343
1344                 success = vstfx_call_dispatcher(vstfx, effGetVendorString, 0, 0, vendorString, 0);
1345                 
1346                 if( success == 1 )
1347                 {
1348                         fprintf (f, "  <check field=\"vendorString\" value=\"%s\"/>\n", vendorString);
1349                         printf ("Vendor string: %s\n", vendorString);
1350                 }
1351                 else
1352                 {
1353                         printf ("No vendor string\n");
1354                 }
1355
1356
1357                 if(vstfx->plugin->flags & 32 )
1358                 {
1359                         numParams = 0;
1360                 }
1361
1362                 for(i=0; i < numParams; i++)
1363                 {
1364                         float val;
1365                         
1366                         pthread_mutex_lock( &vstfx->lock );
1367                         val = vstfx->plugin->getParameter(vstfx->plugin, i );
1368                         pthread_mutex_unlock( &vstfx->lock );
1369                         fprintf( f, "  <param index=\"%d\" value=\"%f\"/>\n", i, val );
1370                 }
1371
1372                 if(vstfx->plugin->flags & 32 )
1373                 {
1374                         printf( "getting chunk...\n" );
1375                         void * chunk;
1376                         bytelen = vstfx_call_dispatcher(vstfx, 23, 0, 0, &chunk, 0 );
1377                         printf( "got tha chunk..\n" );
1378                         if( bytelen )
1379                         {
1380                                 if( bytelen < 0 )
1381                                 {
1382                                         printf( "Chunke len < 0 !!! Not saving chunk.\n" );
1383                                 }
1384                                 else
1385                                 {
1386                                         //char *encoded = g_base64_encode( chunk, bytelen );
1387                                         //fprintf( f, "  <chunk size=\"%d\">\n    %s\n  </chunk>\n", bytelen, encoded );
1388                                         //g_free( encoded );
1389                                 }
1390                         }
1391                 } 
1392
1393                 fprintf( f, "</plugin_state>\n" );
1394                 fclose( f );
1395         }
1396         else
1397         {
1398                 printf ("Could not open state file\n");
1399                 return FALSE;
1400         }
1401         return TRUE;
1402 }
1403