use ActionManager namespace, rather than ActionMap objects, and remove all per-contex...
[ardour.git] / gtk2_ardour / linux_vst_gui_support.cc
index e5722b2be0b84888d3bd281f5e84816ced76cacc..8dc2e928e4cbd9b0bdde6c79389e3f6d459a0ce6 100644 (file)
@@ -33,6 +33,7 @@
 #include <glibmm/timer.h>
 
 #include "ardour/linux_vst_support.h"
+#include "ardour/vst_plugin.h"
 
 #include <X11/X.h>
 #include <X11/Xlib.h>
@@ -56,20 +57,19 @@ static VSTState * vstfx_first = NULL;
 
 const char magic[] = "VSTFX Plugin State v002";
 
-int  gui_thread_id = 0;
-static int gui_quit = 0;
+static volatile int gui_quit = 0;
 
 /*This will be our connection to X*/
 
-Display* LXVST_XDisplay = NULL;
+static Display* LXVST_XDisplay = NULL;
 
 /*The thread handle for the GUI event loop*/
 
-pthread_t LXVST_gui_event_thread;
+static pthread_t LXVST_gui_event_thread;
 
 /*Util functions to get the value of a property attached to an XWindow*/
 
-bool LXVST_xerror;
+static bool LXVST_xerror;
 
 int TempErrorHandler(Display *display, XErrorEvent *e)
 {
@@ -190,31 +190,37 @@ dispatch_x_events (XEvent* event, VSTState* vstfx)
                        int width = event->xconfigure.width;
                        int height = event->xconfigure.height;
 
-                       /*If we get a config notify on the parent window XID then we need to see
-                       if the size has been changed - some plugins re-size their UI window e.g.
-                       when opening a preset manager (you might think that should be spawned as a new window...) */
-
-                       /*if the size has changed, we flag this so that in lxvst_pluginui.cc we can make the
-                       change to the GTK parent window in ardour, from its UI thread*/
+                       /* If we get a config notify on the parent window XID then we need to see
+                        * if the size has been changed - some plugins re-size their UI window e.g.
+                        * when opening a preset manager.
+                        *
+                        * if the size has changed, we flag this so that in lxvst_pluginui.cc
+                        * we can make the change to the GTK parent window in ardour, from its UI thread */
 
                        if (window == (Window) (vstfx->linux_window)) {
-                               if (width != vstfx->width || height!=vstfx->height) {
+#ifndef NDEBUG
+                               printf("dispatch_x_events: ConfigureNotify cfg:(%d %d) plugin:(%d %d)\n",
+                                               width, height,
+                                               vstfx->width, vstfx->height
+                                               );
+#endif
+                               if (width != vstfx->width || height != vstfx->height) {
                                        vstfx->width = width;
                                        vstfx->height = height;
-                                       vstfx->want_resize = 1;
+                                       ARDOUR::VSTPlugin* plug = (ARDOUR::VSTPlugin *)(vstfx->plugin->ptr1);
+                                       plug->VSTSizeWindow (); /* EMIT SIGNAL */
+                               }
 
-                                       /*QUIRK : Loomer plugins not only resize the UI but throw it into some random
-                                       position at the same time. We need to re-position the window at the origin of
-                                       the parent window*/
+                               /* QUIRK : Loomer plugins not only resize the UI but throw it into some random
+                                * position at the same time. We need to re-position the window at the origin of
+                                * the parent window*/
 
-                                       if (vstfx->linux_plugin_ui_window) {
-                                               XMoveWindow (LXVST_XDisplay, vstfx->linux_plugin_ui_window, 0, 0);
-                                       }
+                               if (vstfx->linux_plugin_ui_window) {
+                                       XMoveWindow (LXVST_XDisplay, vstfx->linux_plugin_ui_window, 0, 0);
                                }
                        }
 
                        break;
-
                }
 
                /*Reparent Notify - when the plugin UI is reparented into
@@ -303,29 +309,6 @@ dispatch_x_events (XEvent* event, VSTState* vstfx)
        vstfx->eventProc((void*)event);
 }
 
-static void
-maybe_set_program (VSTState* vstfx)
-{
-       if (vstfx->want_program != -1) {
-               if (vstfx->vst_version >= 2) {
-                       vstfx->plugin->dispatcher (vstfx->plugin, 67 /* effBeginSetProgram */, 0, 0, NULL, 0);
-               }
-
-               vstfx->plugin->dispatcher (vstfx->plugin, effSetProgram, 0, vstfx->want_program, NULL, 0);
-
-               if (vstfx->vst_version >= 2) {
-                       vstfx->plugin->dispatcher (vstfx->plugin, 68 /* effEndSetProgram */, 0, 0, NULL, 0);
-               }
-
-               vstfx->want_program = -1;
-       }
-
-       if (vstfx->want_chunk == 1) {
-               vstfx->plugin->dispatcher (vstfx->plugin, 24 /* effSetChunk */, 1, vstfx->wanted_chunk_size, vstfx->wanted_chunk, 0);
-               vstfx->want_chunk = 0;
-       }
-}
-
 /** This is the main gui event loop for the plugin, we also need to pass
 any Xevents to all the UI callbacks plugins 'may' have registered on their
 windows, that is if they don't manage their own UIs **/
@@ -438,7 +421,7 @@ again:
                                        }
                                }
 
-                               maybe_set_program (vstfx);
+                               vststate_maybe_set_program (vstfx);
                                vstfx->want_program = -1;
                                vstfx->want_chunk = 0;
 
@@ -472,12 +455,38 @@ again:
                        clock1 = g_get_monotonic_time();
                }
 
-               if (may_sleep && elapsed_time_ms + 1 < LXVST_sched_timer_interval) {
+               if (!gui_quit && may_sleep && elapsed_time_ms + 1 < LXVST_sched_timer_interval) {
                        Glib::usleep(1000 * (LXVST_sched_timer_interval - elapsed_time_ms - 1));
                }
        }
 
-       /*Drop out to here if we set gui_quit to 1 */
+       if (LXVST_XDisplay) {
+               XCloseDisplay(LXVST_XDisplay);
+               LXVST_XDisplay = 0;
+       }
+
+       /* some plugin UIs (looking at you, u-he^abique), do set thread-keys
+        * and free, but not unset them.
+        *
+        * This leads to a double-free in __nptl_deallocate_tsd
+        * nptl/pthread_create.c:175  __pthread_keys[idx].destr (data);
+        * when the event-loop thread is joined.
+        *
+        * This workaround is dedicated to all the plugin-UI-devs
+        * who think their UI owns the complete process memory-space.
+        *
+        * NB. ardour itself does not use thread-keys for the
+        * VST event-loop thread, and anyway, this thread is joined
+        * only when ardour exit()s. If this would result in a leak,
+        * nobody will care.
+        */
+       if (!getenv ("ARDOUR_RUNNING_UNDER_VALGRIND")) {
+               for (pthread_key_t i = 0; i < PTHREAD_KEYS_MAX; ++i) {
+                       if (pthread_getspecific (i)) {
+                               pthread_setspecific (i, NULL);
+                       }
+               }
+       }
 
        return NULL;
 }
@@ -488,6 +497,8 @@ normally started in globals.cc*/
 
 int vstfx_init (void* ptr)
 {
+       assert (gui_quit == 0);
+       pthread_mutex_init (&plugin_mutex, NULL);
 
        int thread_create_result;
 
@@ -525,7 +536,7 @@ int vstfx_init (void* ptr)
 
        /*Create the thread - use default attrs for now, don't think we need anything special*/
 
-       thread_create_result = pthread_create(&LXVST_gui_event_thread, NULL, gui_event_loop, NULL);
+       thread_create_result = pthread_create(&LXVST_gui_event_thread, &thread_attributes, gui_event_loop, NULL);
 
        if(thread_create_result!=0)
        {
@@ -534,6 +545,8 @@ int vstfx_init (void* ptr)
                vstfx_error ("** ERROR ** VSTFX: Failed starting GUI event thread");
 
                XCloseDisplay(LXVST_XDisplay);
+               LXVST_XDisplay = 0;
+               gui_quit = 1;
 
                return -1;
        }
@@ -545,12 +558,16 @@ int vstfx_init (void* ptr)
 
 void vstfx_exit()
 {
+       if (gui_quit) {
+               return;
+       }
        gui_quit = 1;
 
        /*We need to pthread_join the gui_thread here so
        we know when it has stopped*/
 
        pthread_join(LXVST_gui_event_thread, NULL);
+       pthread_mutex_destroy (&plugin_mutex);
 }
 
 /*Adds a new plugin (VSTFX) instance to the linked list*/
@@ -686,7 +703,7 @@ vstfx_launch_editor (VSTState* vstfx)
                return 0;
 
        Window parent_window;
-       struct ERect* er;
+       struct ERect* er = NULL;
 
        int x_size = 1;
        int y_size = 1;
@@ -721,11 +738,15 @@ vstfx_launch_editor (VSTState* vstfx)
 
        vstfx->plugin->dispatcher (vstfx->plugin, effEditGetRect, 0, 0, &er, 0 );
 
-       x_size = er->right - er->left;
-       y_size = er->bottom - er->top;
+       if (er) {
+               // Don't crash is plugin does not implement effEditGetRect
+               // it'll result in 1x1 px window but that's not our problem :)
+               x_size = er->right - er->left;
+               y_size = er->bottom - er->top;
+       }
 
-       vstfx->width =  x_size;
-       vstfx->height =  y_size;
+       vstfx->width = x_size;
+       vstfx->height = y_size;
 
        XResizeWindow(LXVST_XDisplay, parent_window, x_size, y_size);