Basic region naming test.
[ardour.git] / gtk2_ardour / linux_vst_gui_support.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/linux_vst_support.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 VSTState * 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 /*Util functions to get the value of a property attached to an XWindow*/
55
56 bool LXVST_xerror;
57
58 int TempErrorHandler(Display *display, XErrorEvent *e)
59 {
60         LXVST_xerror = true;
61         
62         return 0;
63 }
64
65 #ifdef LXVST_32BIT
66
67 int getXWindowProperty(Window window, Atom atom)
68 {
69         int result = 0;
70         int userSize;
71         unsigned long bytes;
72         unsigned long userCount;
73         unsigned char *data;
74         Atom userType;
75         LXVST_xerror = false;
76         
77         /*Use our own Xerror handler while we're in here - in an
78         attempt to stop the brain dead default Xerror behaviour of
79         qutting the entire application because of e.g. an invalid
80         window ID*/
81         
82         XErrorHandler olderrorhandler = XSetErrorHandler(TempErrorHandler);
83         
84         XGetWindowProperty(     LXVST_XDisplay,                 //The display
85                                                 window,                                 //The Window
86                                                 atom,                                   //The property
87                                                 0,                                              //Offset into the data
88                                                 1,                                              //Number of 32Bit chunks of data
89                                                 false,                                  //false = don't delete the property
90                                                 AnyPropertyType,                //Required property type mask
91                                                 &userType,                              //Actual type returned
92                                                 &userSize,                              //Actual format returned
93                                                 &userCount,                             //Actual number of items stored in the returned data
94                                                 &bytes,                                 //Number of bytes remaining if a partial read
95                                                 &data);                                 //The actual data read
96                                                 
97         if(LXVST_xerror == false && userCount == 1)
98                 result = *(int*)data;
99                 
100         XSetErrorHandler(olderrorhandler);
101         
102         /*Hopefully this will return zero if the property is not set*/
103         
104         return result;
105 }
106
107 #endif
108
109 #ifdef LXVST_64BIT
110
111 /********************************************************************/
112 /* This is untested - have no 64Bit plugins which use this          */
113 /* system of passing an eventProc address                           */
114 /********************************************************************/
115
116 long getXWindowProperty(Window window, Atom atom)
117 {
118         long result = 0;
119         int userSize;
120         unsigned long bytes;
121         unsigned long userCount;
122         unsigned char *data;
123         Atom userType;
124         LXVST_xerror = false;
125         
126         /*Use our own Xerror handler while we're in here - in an
127         attempt to stop the brain dead default Xerror behaviour of
128         qutting the entire application because of e.g. an invalid
129         window ID*/
130         
131         XErrorHandler olderrorhandler = XSetErrorHandler(TempErrorHandler);
132         
133         XGetWindowProperty(     LXVST_XDisplay, 
134                                                 window, 
135                                                 atom,
136                                                 0,
137                                                 2,
138                                                 false,
139                                                 AnyPropertyType, 
140                                                 &userType,
141                                                 &userSize,
142                                                 &userCount,
143                                                 &bytes,
144                                                 &data);
145                                                 
146         if(LXVST_xerror == false && userCount == 1)
147                 result = *(long*)data;
148                 
149         XSetErrorHandler(olderrorhandler);
150         
151         /*Hopefully this will return zero if the property is not set*/
152         
153         return result;
154 }
155
156 #endif
157
158 /*The event handler - called from within the main GUI thread to
159 dispatch events to any VST UIs which have callbacks stuck to them*/
160
161 static void
162 dispatch_x_events (XEvent* event, VSTState* vstfx)
163 {
164         /*Handle some of the Events we might be interested in*/
165         
166         switch(event->type)
167         {
168                 /*Configure event - when the window is resized or first drawn*/
169                         
170                 case ConfigureNotify:
171                 {
172                         Window window = event->xconfigure.event;
173                         
174                         int width = event->xconfigure.width;
175                         int height = event->xconfigure.height;
176                         
177                         /*If we get a config notify on the parent window XID then we need to see
178                         if the size has been changed - some plugins re-size their UI window e.g.
179                         when opening a preset manager (you might think that should be spawned as a new window...) */
180                         
181                         /*if the size has changed, we flag this so that in lxvst_pluginui.cc we can make the
182                         change to the GTK parent window in ardour, from its UI thread*/ 
183                         
184                         if (window == (Window) (vstfx->linux_window)) {
185                                 if (width != vstfx->width || height!=vstfx->height) {
186                                         vstfx->width = width;
187                                         vstfx->height = height;
188                                         vstfx->want_resize = 1;
189                                         
190                                         /*QUIRK : Loomer plugins not only resize the UI but throw it into some random
191                                         position at the same time. We need to re-position the window at the origin of
192                                         the parent window*/
193                                         
194                                         if (vstfx->linux_plugin_ui_window) {
195                                                 XMoveWindow (LXVST_XDisplay, vstfx->linux_plugin_ui_window, 0, 0);
196                                         }
197                                 }
198                         }
199                         
200                         break;
201                         
202                 }
203                 
204                 /*Reparent Notify - when the plugin UI is reparented into
205                 our Host Window we will get an event here... probably... */
206                 
207                 case ReparentNotify:
208                 {
209                         Window ParentWindow = event->xreparent.parent;
210                         
211                         /*If the ParentWindow matches the window for the vstfx instance then
212                         the Child window must be the XID of the pluginUI window created by the
213                         plugin, so we need to see if it has a callback stuck to it, and if so
214                         set that up in the vstfx */
215                         
216                         /***********************************************************/
217                         /* 64Bit --- This mechanism is not 64Bit compatible at the */
218                         /* present time                                            */
219                         /***********************************************************/
220                         
221                         if (ParentWindow == (Window) (vstfx->linux_window)) {
222
223                                 Window PluginUIWindowID = event->xreparent.window;
224                                 
225                                 vstfx->linux_plugin_ui_window = PluginUIWindowID;
226 #ifdef LXVST_32BIT
227                                 int result = getXWindowProperty(PluginUIWindowID, XInternAtom(LXVST_XDisplay, "_XEventProc", false));
228         
229                                 if (result == 0) {
230                                         vstfx->eventProc = NULL;
231                                 } else {
232                                         vstfx->eventProc = (void (*) (void* event))result;
233                                 }
234 #endif
235 #ifdef LXVST_64BIT
236                                 long result = getXWindowProperty(PluginUIWindowID, XInternAtom(LXVST_XDisplay, "_XEventProc", false));
237         
238                                 if(result == 0)
239                                         vstfx->eventProc = NULL;
240                                 else
241                                         vstfx->eventProc = (void (*) (void* event))result;
242 #endif
243                         }
244                         break;
245                 }
246                 
247                 case ClientMessage:
248                 {
249                         Window window = event->xany.window;
250                         Atom message_type = event->xclient.message_type;
251                         
252                         /*The only client message we are interested in is to signal
253                         that the plugin parent window is now valid and can be passed
254                         to effEditOpen when the editor is launched*/
255                         
256                         if (window == (Window) (vstfx->linux_window)) {
257                                 char* message = XGetAtomName(LXVST_XDisplay, message_type);
258                                 
259                                 if (strcmp(message,"LaunchEditor") == 0) {
260                                         if (event->xclient.data.l[0] == 0x0FEEDBAC) {
261                                                 vstfx_launch_editor (vstfx);
262                                         }
263                                 }
264                                 
265                                 XFree(message);
266                         }
267                         break;
268                 }
269                 
270                 default:
271                         break;
272         }
273         
274         /* Some VSTs built with toolkits e.g. JUCE will manager their own UI
275         autonomously in the plugin, running the UI in its own thread, so once
276         we have created a parent window for the plugin, its UI takes care of
277         itself.*/
278         
279         /*Other types register a callback as an Xwindow property on the plugin
280         UI window after they create it.  If that is the case, we need to call it
281         here, passing the XEvent into it*/
282         
283         if (vstfx->eventProc == NULL) {
284                 return;
285         }
286                                 
287         vstfx->eventProc((void*)event);
288 }
289
290 static void
291 maybe_set_program (VSTState* vstfx)
292 {
293         if (vstfx->want_program != -1) {
294                 if (vstfx->vst_version >= 2) {
295                         vstfx->plugin->dispatcher (vstfx->plugin, 67 /* effBeginSetProgram */, 0, 0, NULL, 0);
296                 }
297
298                 vstfx->plugin->dispatcher (vstfx->plugin, effSetProgram, 0, vstfx->want_program, NULL, 0);
299                 
300                 if (vstfx->vst_version >= 2) {
301                         vstfx->plugin->dispatcher (vstfx->plugin, 68 /* effEndSetProgram */, 0, 0, NULL, 0);
302                 }
303                 
304                 vstfx->want_program = -1; 
305         }
306
307         if (vstfx->want_chunk == 1) {
308                 vstfx->plugin->dispatcher (vstfx->plugin, 24 /* effSetChunk */, 1, vstfx->wanted_chunk_size, vstfx->wanted_chunk, 0);
309                 vstfx->want_chunk = 0;
310         }
311 }
312
313 /** This is the main gui event loop for the plugin, we also need to pass
314 any Xevents to all the UI callbacks plugins 'may' have registered on their
315 windows, that is if they don't manage their own UIs **/
316
317 void* gui_event_loop (void* ptr)
318 {
319         VSTState* vstfx;
320         int LXVST_sched_event_timer = 0;
321         int LXVST_sched_timer_interval = 50; //ms
322         XEvent event;
323         
324         /*The 'Forever' loop - runs the plugin UIs etc - based on the FST gui event loop*/
325         
326         while (!gui_quit)
327         {
328                 /* handle window creation requests, destroy requests, 
329                    and run idle callbacks */
330
331                 /*Look at the XEvent queue - if there are any XEvents we need to handle them,
332                 including passing them to all the plugin (eventProcs) we are currently managing*/
333                 
334                 if(LXVST_XDisplay)
335                 {
336                         /*See if there are any events in the queue*/
337                 
338                         int num_events = XPending(LXVST_XDisplay);
339                         
340                         /*process them if there are any*/
341                 
342                         while(num_events)
343                         {
344                                 XNextEvent(LXVST_XDisplay, &event);
345                                 
346                                 /*Call dispatch events, with the event, for each plugin in the linked list*/
347                                 
348                                 for (vstfx = vstfx_first; vstfx; vstfx = vstfx->next)
349                                 {       
350                                         pthread_mutex_lock(&vstfx->lock);
351                                         
352                                         dispatch_x_events(&event, vstfx);
353                                         
354                                         pthread_mutex_unlock(&vstfx->lock);
355                                 }
356                                 
357                                 num_events--;
358                         }
359                 }
360
361                 /*We don't want to use all the CPU.. */
362
363                 usleep(1000);
364                 
365                 LXVST_sched_event_timer++;
366                 
367                 LXVST_sched_event_timer = LXVST_sched_event_timer & 0x00FFFFFF;
368
369                 /*See if its time for us to do a scheduled event pass on all the plugins*/
370
371                 if((LXVST_sched_timer_interval!=0) && (!(LXVST_sched_event_timer% LXVST_sched_timer_interval)))
372                 {
373                         pthread_mutex_lock (&plugin_mutex);
374                     
375 again:
376                         /*Parse through the linked list of plugins*/
377                         
378                         for (vstfx = vstfx_first; vstfx; vstfx = vstfx->next)
379                         {       
380                                 pthread_mutex_lock (&vstfx->lock);
381
382                                 /*Window scheduled for destruction*/
383                                 
384                                 if (vstfx->destroy) {
385                                         if (vstfx->linux_window) {
386                                                 vstfx->plugin->dispatcher (vstfx->plugin, effEditClose, 0, 0, NULL, 0.0);
387                                                         
388                                                 XDestroyWindow (LXVST_XDisplay, vstfx->linux_window);
389                                                 /* FIXME - probably safe to assume we never have an XID of 0 but not explicitly true */
390                                                 vstfx->linux_window = 0;
391                                                 vstfx->destroy = FALSE;
392                                         }
393                                         
394                                         vstfx_event_loop_remove_plugin (vstfx);
395                                         vstfx->been_activated = FALSE;
396                                         pthread_cond_signal (&vstfx->window_status_change);
397                                         pthread_mutex_unlock (&vstfx->lock);
398                                         
399                                         goto again;
400                                 } 
401                                 
402                                 /*Window does not yet exist - scheduled for creation*/
403
404                                 /* FIXME - probably safe to assume 0 is not a valid XID but not explicitly true */
405                                 if (vstfx->linux_window == 0) {
406                                         if (vstfx_create_editor (vstfx)) {
407                                                 vstfx_error ("** ERROR ** VSTFX : Cannot create editor for plugin %s", vstfx->handle->name);
408                                                 vstfx_event_loop_remove_plugin (vstfx);
409                                                 pthread_cond_signal (&vstfx->window_status_change);
410                                                 pthread_mutex_unlock (&vstfx->lock);
411                                                 goto again;
412                                         } else {
413                                                 /* condition/unlock: it was signalled & unlocked in fst_create_editor()   */
414                                         }
415                                 }
416
417                                 maybe_set_program (vstfx);
418                                 vstfx->want_program = -1;
419                                 vstfx->want_chunk = 0;
420                                 
421                                 /*scheduled call to dispatcher*/
422                                 
423                                 if (vstfx->dispatcher_wantcall) {
424                                         vstfx->dispatcher_retval = vstfx->plugin->dispatcher (
425                                                 vstfx->plugin, 
426                                                 vstfx->dispatcher_opcode,
427                                                 vstfx->dispatcher_index,
428                                                 vstfx->dispatcher_val,
429                                                 vstfx->dispatcher_ptr,
430                                                 vstfx->dispatcher_opt
431                                                 );
432                                         
433                                         vstfx->dispatcher_wantcall = 0;
434                                         pthread_cond_signal (&vstfx->plugin_dispatcher_called);
435                                 }
436                                 
437                                 /*Call the editor Idle function in the plugin*/
438                                 
439                                 vstfx->plugin->dispatcher (vstfx->plugin, effEditIdle, 0, 0, NULL, 0);
440
441                                 if(vstfx->wantIdle)
442                                         vstfx->plugin->dispatcher (vstfx->plugin, 53, 0, 0, NULL, 0);
443                                         
444                                 pthread_mutex_unlock (&vstfx->lock);
445                         }
446                         pthread_mutex_unlock (&plugin_mutex);
447                 }
448         }
449
450         /*Drop out to here if we set gui_quit to 1 */
451
452         return NULL;
453 }
454
455 /*The VSTFX Init function - this needs to be called before the VSTFX engine
456 can be accessed, it gets the UI thread running, opens a connection to X etc
457 normally started in globals.cc*/
458
459 int vstfx_init (void* ptr)
460 {
461
462         int thread_create_result;
463         
464         pthread_attr_t thread_attributes;
465         
466         /*Init the attribs to defaults*/
467         
468         pthread_attr_init(&thread_attributes);
469         
470         /*Make sure the thread is joinable - this should be the default anyway - 
471         so we can join to it on vstfx_exit*/
472         
473         pthread_attr_setdetachstate(&thread_attributes, PTHREAD_CREATE_JOINABLE);
474         
475
476         /*This is where we need to open a connection to X, and start the GUI thread*/
477         
478         /*Open our connection to X - all linuxVST plugin UIs handled by the LXVST engine
479         will talk to X down this connection - X cannot handle multi-threaded access via
480         the same Display* */
481         
482         if(LXVST_XDisplay==NULL)
483                 LXVST_XDisplay = XOpenDisplay(NULL);    //We might be able to make this open a specific screen etc
484
485         /*Drop out and report the error if we fail to connect to X */
486         
487         if(LXVST_XDisplay==NULL)
488         {
489                 vstfx_error ("** ERROR ** VSTFX: Failed opening connection to X");
490                 
491                 return -1;
492         }
493         
494         /*We have a connection to X - so start the gui event loop*/
495         
496         /*Create the thread - use default attrs for now, don't think we need anything special*/
497         
498         thread_create_result = pthread_create(&LXVST_gui_event_thread, NULL, gui_event_loop, NULL);
499         
500         if(thread_create_result!=0)
501         {
502                 /*There was a problem starting the GUI event thread*/
503                 
504                 vstfx_error ("** ERROR ** VSTFX: Failed starting GUI event thread");
505                 
506                 XCloseDisplay(LXVST_XDisplay);
507                 
508                 return -1;
509         }
510         
511         return 0;
512 }
513
514 /*The vstfx Quit function*/
515
516 void vstfx_exit()
517 {
518         gui_quit = 1;
519         
520         /*We need to pthread_join the gui_thread here so
521         we know when it has stopped*/
522         
523         pthread_join(LXVST_gui_event_thread, NULL);
524 }
525
526 /*Adds a new plugin (VSTFX) instance to the linked list*/
527
528 int vstfx_run_editor (VSTState* vstfx)
529 {
530         pthread_mutex_lock (&plugin_mutex);
531
532         /* Add the new VSTFX instance to the linked list */
533
534         if (vstfx_first == NULL) {
535                 vstfx_first = vstfx;
536         } else {
537                 VSTState* p = vstfx_first;
538                 
539                 while (p->next) {
540                         p = p->next;
541                 }
542                 p->next = vstfx;
543                 
544                 /* Mark the new end of the list */
545                 
546                 vstfx->next = NULL;
547         }
548
549         pthread_mutex_unlock (&plugin_mutex);
550
551         /* wait for the plugin editor window to be created (or not) */
552
553         pthread_mutex_lock (&vstfx->lock);
554         
555         if (!vstfx->linux_window) {
556                 pthread_cond_wait (&vstfx->window_status_change, &vstfx->lock);
557         }
558         
559         pthread_mutex_unlock (&vstfx->lock);
560
561         if (!vstfx->linux_window) {
562                 return -1;
563         }
564
565         return 0;
566 }
567
568
569 /*Creates an editor for the plugin - normally called from within the gui event loop
570 after run_editor has added the plugin (editor) to the linked list*/
571
572 int vstfx_create_editor (VSTState* vstfx)
573 {
574         Window parent_window;
575         
576         int x_size = 1;
577         int y_size = 1;
578
579         /* Note: vstfx->lock is held while this function is called */
580
581         if (!(vstfx->plugin->flags & effFlagsHasEditor))
582         {
583                 vstfx_error ("** ERROR ** VSTFX: Plugin \"%s\" has no editor", vstfx->handle->name);
584                 return -1;
585         }
586         
587         
588         /*Create an XWindow for the plugin to inhabit*/
589         
590         parent_window = XCreateSimpleWindow (
591                 LXVST_XDisplay,
592                 DefaultRootWindow(LXVST_XDisplay),
593                 0,
594                 0,
595                 x_size,
596                 y_size,
597                 0,
598                 0,
599                 0
600                 );
601                                                                                 
602         /*Select the events we are interested in receiving - we need Substructure notify so that
603         if the plugin resizes its window - e.g. Loomer Manifold then we get a message*/
604         
605         XSelectInput(LXVST_XDisplay, 
606                                 parent_window,
607                                 SubstructureNotifyMask | ButtonPressMask | ButtonReleaseMask | ButtonMotionMask | ExposureMask);
608                                                                                 
609         vstfx->linux_window = parent_window;
610                                                                                 
611         vstfx->xid = parent_window;  //vstfx->xid will be referenced to connect to GTK UI in ardour later
612         
613         /*Because the plugin may be operating on a different Display* to us, and therefore
614         the two event queues can be asynchronous, although we have created the window on
615         our display, we can't guarantee it exists in the server yet, which will
616         cause BadWindow crashes if the plugin tries to use it.
617         
618         It would be nice to use CreateNotify events here, but they don't get
619         through on all window managers, so instead we pass a client message
620         into out queue, after the XCreateWindow.  When this message pops out
621         in our event handler, it will trigger the second stage of plugin
622         Editor instantiation, and by then the Window should be valid...*/
623         
624         XClientMessageEvent event;
625         
626         /*Create an atom to identify our message (only if it doesn't already exist)*/
627         
628         Atom WindowActiveAtom = XInternAtom(LXVST_XDisplay, "LaunchEditor", false);
629         
630         event.type = ClientMessage;
631         event.send_event = true;
632         event.window = parent_window;
633         event.message_type = WindowActiveAtom;
634
635         event.format = 32;                                              //Data format
636         event.data.l[0] = 0x0FEEDBAC;                   //Something we can recognize later
637         
638         /*Push the event into the queue on our Display*/
639         
640         XSendEvent(LXVST_XDisplay, parent_window, FALSE, NoEventMask, (XEvent*)&event);
641
642         /*Unlock - and we are done for the first part of staring the Editor...*/
643         
644         pthread_mutex_unlock (&vstfx->lock);
645         
646         return 0;
647 }
648
649 int
650 vstfx_launch_editor (VSTState* vstfx)
651 {
652         /*This is the second stage of launching the editor (see vstfx_create editor)
653         we get called here in response to receiving the ClientMessage on our Window,
654         therefore it's about as safe (as can be) to assume that the Window we created
655         is now valid in the XServer and can be passed to the plugin in effEditOpen
656         without generating BadWindow errors when the plugin reparents itself into our
657         parent window*/
658         
659         if(vstfx->been_activated)
660                 return 0;
661         
662         Window parent_window;
663         struct ERect* er;
664         
665         int x_size = 1;
666         int y_size = 1;
667         
668         parent_window = vstfx->linux_window;
669         
670         /*Open the editor - Bah! we have to pass the int windowID as a void pointer - yuck
671         it gets cast back to an int as the parent window XID in the plugin - and we have to pass the
672         Display* as a long */
673         
674         /**************************************************************/
675         /* 64Bit --- parent window is an int passed as a void* so     */
676         /* that should be ok for 64Bit machines                       */
677         /*                                                            */
678         /* Display is passed in as a long - ok on arch's where sizeof */
679         /* long = 8                                                   */
680         /*                                                            */
681         /* Most linux VST plugins open a connection to X on their own */
682         /* Display anyway so it may not matter                        */
683         /*                                                            */
684         /* linuxDSP VSTs don't use the host Display* at all           */
685         /**************************************************************/
686         
687         vstfx->plugin->dispatcher (vstfx->plugin, effEditOpen, 0, (long)LXVST_XDisplay, (void*)(parent_window), 0 );
688         
689         /*QUIRK - some plugins need a slight delay after opening the editor before you can
690         ask the window size or they might return zero - specifically discoDSP */
691         
692         usleep(100000);
693         
694         /*Now we can find out how big the parent window should be (and try) to resize it*/
695         
696         vstfx->plugin->dispatcher (vstfx->plugin, effEditGetRect, 0, 0, &er, 0 );
697
698         x_size = er->right - er->left;
699         y_size = er->bottom - er->top;
700         
701         vstfx->width =  x_size;
702         vstfx->height =  y_size;
703         
704         XResizeWindow(LXVST_XDisplay, parent_window, x_size, y_size);
705         
706         XFlush (LXVST_XDisplay);
707         
708         /*Not sure if we need to map the window or if the plugin will do it for us
709         it should be ok because XReparentWindow generates a Map event*/
710         
711         /*mark the editor as activated - mainly so that vstfx_get_XID
712         will know it is valid*/
713
714         vstfx->been_activated = TRUE;
715         
716         pthread_cond_signal (&vstfx->window_status_change);
717         return 0;
718 }
719
720 /** Destroy the editor window */
721 void
722 vstfx_destroy_editor (VSTState* vstfx)
723 {
724         pthread_mutex_lock (&vstfx->lock);
725         if (vstfx->linux_window) {
726                 vstfx->destroy = TRUE;
727                 pthread_cond_wait (&vstfx->window_status_change, &vstfx->lock);
728         }
729         pthread_mutex_unlock (&vstfx->lock);
730 }
731
732 /** Remove a vstfx instance from the linked list parsed by the
733     event loop
734 */
735 void
736 vstfx_event_loop_remove_plugin (VSTState* vstfx)
737 {
738         /* This only ever gets called from within our GUI thread
739            so we don't need to lock here - if we did there would be
740            a deadlock anyway
741         */
742         
743         VSTState* p;
744         VSTState* prev;
745         
746         for (p = vstfx_first, prev = NULL; p; prev = p, p = p->next) {
747                 if (p == vstfx) {
748                         if (prev) {
749                                 prev->next = p->next;
750                                 break;
751                         }
752                 }
753         }
754
755         if (vstfx_first == vstfx) {
756                 vstfx_first = vstfx_first->next;
757         }
758 }
759