| @@ -16,6 +16,7 @@ | |||||
| */ | */ | ||||
| #include "CarlaPluginUi.hpp" | #include "CarlaPluginUi.hpp" | ||||
| #include "CarlaHost.h" | |||||
| #ifdef HAVE_X11 | #ifdef HAVE_X11 | ||||
| # include <X11/Xatom.h> | # include <X11/Xatom.h> | ||||
| @@ -54,6 +55,9 @@ public: | |||||
| Atom wmDelete = XInternAtom(fDisplay, "WM_DELETE_WINDOW", True); | Atom wmDelete = XInternAtom(fDisplay, "WM_DELETE_WINDOW", True); | ||||
| XSetWMProtocols(fDisplay, fWindow, &wmDelete, 1); | XSetWMProtocols(fDisplay, fWindow, &wmDelete, 1); | ||||
| if (const uintptr_t transientId = carla_standalone_get_transient_win_id()) | |||||
| setTransientWinId(transientId); | |||||
| } | } | ||||
| ~X11PluginUi() override | ~X11PluginUi() override | ||||
| @@ -170,6 +174,85 @@ private: | |||||
| // ----------------------------------------------------- | // ----------------------------------------------------- | ||||
| bool CarlaPluginUi::tryTransientWinIdMatch(const char* const uiTitle, const uintptr_t winId) | |||||
| { | |||||
| CARLA_SAFE_ASSERT_RETURN(uiTitle != nullptr && uiTitle[0] != '\0', true); | |||||
| CARLA_SAFE_ASSERT_RETURN(winId != 0, true); | |||||
| #if defined(CARLA_OS_MAC) | |||||
| #elif defined(CARLA_OS_WIN) | |||||
| #elif defined(HAVE_X11) | |||||
| struct ScopedDisplay { | |||||
| Display* display; | |||||
| ScopedDisplay() : display(XOpenDisplay(0)) {} | |||||
| ~ScopedDisplay() { if (display!=nullptr) XCloseDisplay(display); } | |||||
| }; | |||||
| struct ScopedFreeData { | |||||
| uchar* data; | |||||
| ScopedFreeData(uchar* d) : data(d) {} | |||||
| ~ScopedFreeData() { XFree(data); } | |||||
| }; | |||||
| const ScopedDisplay sd; | |||||
| CARLA_SAFE_ASSERT_RETURN(sd.display != nullptr, true); | |||||
| Atom _ncl = XInternAtom(sd.display, "_NET_CLIENT_LIST" , True); | |||||
| Atom _nwn = XInternAtom(sd.display, "_NET_WM_NAME", False); | |||||
| Atom utf8 = XInternAtom(sd.display, "UTF8_STRING", False); | |||||
| Atom actualType; | |||||
| int actualFormat; | |||||
| unsigned long numWindows, bytesAfter; | |||||
| unsigned char* data = nullptr; | |||||
| int status = XGetWindowProperty(sd.display, DefaultRootWindow(sd.display), _ncl, 0L, (~0L), False, AnyPropertyType, &actualType, &actualFormat, &numWindows, &bytesAfter, &data); | |||||
| CARLA_SAFE_ASSERT_RETURN(data != nullptr, true); | |||||
| const ScopedFreeData sfd(data); | |||||
| CARLA_SAFE_ASSERT_RETURN(status == Success, true); | |||||
| CARLA_SAFE_ASSERT_RETURN(actualFormat == 32, true); | |||||
| CARLA_SAFE_ASSERT_RETURN(numWindows != 0, true); | |||||
| Window* windows = (Window*)data; | |||||
| Window lastGoodWindow = 0; | |||||
| for (ulong i = 0; i < numWindows; i++) | |||||
| { | |||||
| const Window window(windows[i]); | |||||
| CARLA_SAFE_ASSERT_CONTINUE(window != 0); | |||||
| unsigned long nameSize; | |||||
| unsigned char* nameData = nullptr; | |||||
| status = XGetWindowProperty(sd.display, window, _nwn, 0L, (~0L), False, utf8, &actualType, &actualFormat, &nameSize, &bytesAfter, &nameData); | |||||
| if (nameData == nullptr) | |||||
| continue; | |||||
| const ScopedFreeData sfd2(nameData); | |||||
| CARLA_SAFE_ASSERT_CONTINUE(status == Success); | |||||
| CARLA_SAFE_ASSERT_CONTINUE(nameSize != 0); | |||||
| if (std::strcmp((const char*)nameData, uiTitle) == 0) | |||||
| { | |||||
| CARLA_SAFE_ASSERT_RETURN(lastGoodWindow == 0, true); | |||||
| lastGoodWindow = window; | |||||
| } | |||||
| } | |||||
| if (lastGoodWindow == 0) | |||||
| return false; | |||||
| XSetTransientForHint(sd.display, lastGoodWindow, (Window)winId); | |||||
| XFlush(sd.display); | |||||
| return true; | |||||
| #endif | |||||
| } | |||||
| // ----------------------------------------------------- | |||||
| #ifdef CARLA_OS_MAC | #ifdef CARLA_OS_MAC | ||||
| CarlaPluginUi* CarlaPluginUi::newCocoa(CloseCallback* cb) | CarlaPluginUi* CarlaPluginUi::newCocoa(CloseCallback* cb) | ||||
| { | { | ||||
| @@ -41,6 +41,8 @@ public: | |||||
| virtual void setTransientWinId(const uintptr_t winId) = 0; | virtual void setTransientWinId(const uintptr_t winId) = 0; | ||||
| virtual void* getPtr() const noexcept = 0; | virtual void* getPtr() const noexcept = 0; | ||||
| static bool tryTransientWinIdMatch(const char* const uiTitle, const uintptr_t winId); | |||||
| #ifdef CARLA_OS_MAC | #ifdef CARLA_OS_MAC | ||||
| static CarlaPluginUi* newCocoa(CloseCallback*); | static CarlaPluginUi* newCocoa(CloseCallback*); | ||||
| #endif | #endif | ||||
| @@ -22,23 +22,18 @@ | |||||
| #include "CarlaDssiUtils.hpp" | #include "CarlaDssiUtils.hpp" | ||||
| #include "CarlaMathUtils.hpp" | #include "CarlaMathUtils.hpp" | ||||
| #include "CarlaPluginUi.hpp" | |||||
| #include "CarlaHost.h" | |||||
| #include <QtCore/QByteArray> | #include <QtCore/QByteArray> | ||||
| #include <QtCore/QString> | #include <QtCore/QString> | ||||
| #ifdef CARLA_OS_LINUX | |||||
| # include <X11/Xlib.h> | |||||
| #endif | |||||
| CARLA_BACKEND_START_NAMESPACE | CARLA_BACKEND_START_NAMESPACE | ||||
| #if 0 | #if 0 | ||||
| } | } | ||||
| #endif | #endif | ||||
| // TESTING | |||||
| static bool uiStarted = false; | |||||
| class DssiPlugin : public CarlaPlugin | class DssiPlugin : public CarlaPlugin | ||||
| { | { | ||||
| public: | public: | ||||
| @@ -52,7 +47,8 @@ public: | |||||
| fUiFilename(nullptr), | fUiFilename(nullptr), | ||||
| fAudioInBuffers(nullptr), | fAudioInBuffers(nullptr), | ||||
| fAudioOutBuffers(nullptr), | fAudioOutBuffers(nullptr), | ||||
| fParamBuffers(nullptr) | |||||
| fParamBuffers(nullptr), | |||||
| fTransientTryCounter(0) | |||||
| { | { | ||||
| carla_debug("DssiPlugin::DssiPlugin(%p, %i)", engine, id); | carla_debug("DssiPlugin::DssiPlugin(%p, %i)", engine, id); | ||||
| @@ -382,10 +378,11 @@ public: | |||||
| { | { | ||||
| pData->osc.data.free(); | pData->osc.data.free(); | ||||
| pData->osc.thread.start(); | pData->osc.thread.start(); | ||||
| uiStarted = true; | |||||
| } | } | ||||
| else | else | ||||
| { | { | ||||
| fTransientTryCounter = 0; | |||||
| if (pData->osc.data.target != nullptr) | if (pData->osc.data.target != nullptr) | ||||
| { | { | ||||
| osc_send_hide(pData->osc.data); | osc_send_hide(pData->osc.data); | ||||
| @@ -401,109 +398,18 @@ public: | |||||
| { | { | ||||
| CarlaPlugin::idle(); | CarlaPlugin::idle(); | ||||
| #ifdef CARLA_OS_LINUX | |||||
| if (! uiStarted) | |||||
| if (fTransientTryCounter == 0) | |||||
| return; | return; | ||||
| // TESTING | |||||
| static int counter = 0; | |||||
| if (++counter != 100) | |||||
| if (++fTransientTryCounter % 10 != 0) | |||||
| return; | |||||
| if (fTransientTryCounter >= 200) | |||||
| return; | return; | ||||
| if (const char* const win = getenv("CARLA_TRANSIENT_WINDOW")) | |||||
| { | |||||
| const long winl = std::atol(win); | |||||
| carla_stderr2("got transient, %s vs %li", win, winl); | |||||
| QString targetName(QString("%1 (GUI)").arg(pData->name)); | |||||
| Display* display = XOpenDisplay(0); | |||||
| //int screen = DefaultScreen(display); | |||||
| Window lastWindow = 0; | |||||
| #if 0 | |||||
| Window root, parent; | |||||
| Window* children = nullptr; | |||||
| uint num = 0; | |||||
| int status = XQueryTree(display, DefaultRootWindow(display), &root, &parent, &children, &num); | |||||
| carla_stdout("HERE001 %i, %i", status, num); | |||||
| for (uint i = 0; i < num; i++) | |||||
| { | |||||
| Window w = children[i]; | |||||
| carla_stdout("Scanned client window: %lu", w); | |||||
| char* name = nullptr; | |||||
| status = XFetchName(display, w, &name); | |||||
| if (name != nullptr) | |||||
| { | |||||
| XFree(name); | |||||
| carla_stdout("Found: %ul %s", w, name); | |||||
| } | |||||
| } | |||||
| #else | |||||
| Atom a = XInternAtom(display, "_NET_CLIENT_LIST" , true); | |||||
| Atom actualType; | |||||
| int actualFormat; | |||||
| unsigned long numItems, bytesAfter; | |||||
| unsigned char* data = nullptr; | |||||
| int status = XGetWindowProperty(display, DefaultRootWindow(display), a, 0L, (~0L), False, AnyPropertyType, &actualType, &actualFormat, &numItems, &bytesAfter, &data); | |||||
| carla_stdout("FOUND %i WINDOWS", numItems); | |||||
| if (status >= Success && numItems) | |||||
| { | |||||
| // success - we have data: Format should always be 32: | |||||
| CARLA_SAFE_ASSERT_RETURN(actualFormat == 32,); | |||||
| // cast to proper format, and iterate through values: | |||||
| Window* array = (Window*)data; | |||||
| for (uint32_t k = 0; k < numItems; k++) | |||||
| { | |||||
| // get window Id: | |||||
| Window w = array[k]; | |||||
| carla_stdout("Found at %i: %li", k+1, w); | |||||
| #if 1 | |||||
| if (w == 0) | |||||
| continue; | |||||
| Atom name = XInternAtom(display, "_NET_WM_NAME", False); | |||||
| // Atom utf8 = XInternAtom(display, "UTF8_STRING", False); | |||||
| unsigned long numNames; | |||||
| unsigned char* namesData = nullptr; | |||||
| status = XGetWindowProperty(display, w, name, 0L, (~0L), False, AnyPropertyType/*utf8*/, &actualType, &actualFormat, &numNames, &bytesAfter, &namesData); | |||||
| //if (status >= Success) | |||||
| { | |||||
| carla_stdout("Found at %i: %ul %ul %s", k+1, w, numNames, (const char*)namesData); | |||||
| } | |||||
| lastWindow = w; | |||||
| XFree(namesData); | |||||
| #endif | |||||
| } | |||||
| XFree(data); | |||||
| } | |||||
| carla_stdout("Trying to get window..."); | |||||
| if (lastWindow != 0) | |||||
| { | |||||
| XSetTransientForHint(display, lastWindow, (Window)winl); | |||||
| XFlush(display); | |||||
| } | |||||
| #endif | |||||
| } | |||||
| #endif | |||||
| QString uiTitle(QString("%1 (GUI)").arg(pData->name)); | |||||
| if (CarlaPluginUi::tryTransientWinIdMatch(uiTitle.toUtf8().constData(), carla_standalone_get_transient_win_id())) | |||||
| fTransientTryCounter = 0; | |||||
| } | } | ||||
| // ------------------------------------------------------------------- | // ------------------------------------------------------------------- | ||||
| @@ -1815,6 +1721,17 @@ public: | |||||
| carla_debug("DssiPlugin::clearBuffers() - end"); | carla_debug("DssiPlugin::clearBuffers() - end"); | ||||
| } | } | ||||
| // ------------------------------------------------------------------- | |||||
| // OSC stuff | |||||
| void updateOscData(const lo_address& source, const char* const url) override | |||||
| { | |||||
| CarlaPlugin::updateOscData(source, url); | |||||
| if (carla_standalone_get_transient_win_id() != 0) | |||||
| fTransientTryCounter = 1; | |||||
| } | |||||
| // ------------------------------------------------------------------- | // ------------------------------------------------------------------- | ||||
| // Post-poned UI Stuff | // Post-poned UI Stuff | ||||
| @@ -2088,6 +2005,11 @@ private: | |||||
| float* fParamBuffers; | float* fParamBuffers; | ||||
| snd_seq_event_t fMidiEvents[kPluginMaxMidiEvents]; | snd_seq_event_t fMidiEvents[kPluginMaxMidiEvents]; | ||||
| // used to try and set transient hint for external windows | |||||
| uint fTransientTryCounter; | |||||
| // ------------------------------------------------------------------- | |||||
| static LinkedList<const char*> sMultiSynthList; | static LinkedList<const char*> sMultiSynthList; | ||||
| static bool addUniqueMultiSynth(const char* const label) | static bool addUniqueMultiSynth(const char* const label) | ||||
| @@ -24,8 +24,6 @@ | |||||
| #include "CarlaMathUtils.hpp" | #include "CarlaMathUtils.hpp" | ||||
| #include "CarlaLv2Utils.hpp" | #include "CarlaLv2Utils.hpp" | ||||
| #include "CarlaHost.h" | |||||
| #include "CarlaPluginUi.hpp" | #include "CarlaPluginUi.hpp" | ||||
| #include "Lv2AtomQueue.hpp" | #include "Lv2AtomQueue.hpp" | ||||
| @@ -1160,20 +1158,8 @@ public: | |||||
| if (fUi.window == nullptr) | if (fUi.window == nullptr) | ||||
| return pData->engine->callback(ENGINE_CALLBACK_UI_STATE_CHANGED, pData->id, -1, 0, 0.0f, msg); | return pData->engine->callback(ENGINE_CALLBACK_UI_STATE_CHANGED, pData->id, -1, 0, 0.0f, msg); | ||||
| if (const uintptr_t transientId = carla_standalone_get_transient_win_id()) | |||||
| fUi.window->setTransientWinId(transientId); | |||||
| fUi.window->setTitle(fUi.title); | fUi.window->setTitle(fUi.title); | ||||
| #if 0 | |||||
| if (const char* const win = getenv("CARLA_TRANSIENT_WINDOW")) | |||||
| { | |||||
| const long winl = std::atol(win); | |||||
| carla_stderr2("got transient, %s vs %li", win, winl); | |||||
| fUi.window->setTransient(winl); | |||||
| } | |||||
| #endif | |||||
| fFeatures[kFeatureIdUiParent]->data = fUi.window->getPtr(); | fFeatures[kFeatureIdUiParent]->data = fUi.window->getPtr(); | ||||
| fUi.widget = nullptr; | fUi.widget = nullptr; | ||||
| @@ -63,7 +63,7 @@ CarlaPluginInternal.cpp.o: CarlaPluginInternal.cpp $(CARLA_PLUGIN_INTERNAL_HPP) | |||||
| CarlaPluginThread.cpp.o: CarlaPluginThread.cpp $(CARLA_PLUGIN_HPP) $(CARLA_PLUGIN_THREAD_HPP) $(CARLA_ENGINE_HPP) | CarlaPluginThread.cpp.o: CarlaPluginThread.cpp $(CARLA_PLUGIN_HPP) $(CARLA_PLUGIN_THREAD_HPP) $(CARLA_ENGINE_HPP) | ||||
| $(CXX) $< $(BUILD_CXX_FLAGS) $(QTCORE_FLAGS) -c -o $@ | $(CXX) $< $(BUILD_CXX_FLAGS) $(QTCORE_FLAGS) -c -o $@ | ||||
| CarlaPluginUi.cpp.o: CarlaPluginUi.cpp $(CARLA_PLUGIN_UI_HPP) $(CARLA_UTILS_HPP) | |||||
| CarlaPluginUi.cpp.o: CarlaPluginUi.cpp $(CARLA_PLUGIN_UI_HPP) $(CARLA_HOST_H) | |||||
| $(CXX) $< $(BUILD_CXX_FLAGS) -c -o $@ | $(CXX) $< $(BUILD_CXX_FLAGS) -c -o $@ | ||||
| NativePlugin.cpp.o: NativePlugin.cpp $(CARLA_PLUGIN_INTERNAL_HPP) $(CARLA_ENGINE_HPP) $(CARLA_MATH_UTILS_HPP) $(CARLA_NATIVE_H) $(CARLA_HOST_H) | NativePlugin.cpp.o: NativePlugin.cpp $(CARLA_PLUGIN_INTERNAL_HPP) $(CARLA_ENGINE_HPP) $(CARLA_MATH_UTILS_HPP) $(CARLA_NATIVE_H) $(CARLA_HOST_H) | ||||
| @@ -75,10 +75,10 @@ BridgePlugin.cpp.o: BridgePlugin.cpp $(CARLA_PLUGIN_INTERNAL_HPP) $(CARLA_ENGINE | |||||
| LadspaPlugin.cpp.o: LadspaPlugin.cpp $(CARLA_PLUGIN_INTERNAL_HPP) $(CARLA_ENGINE_HPP) $(CARLA_LADSPA_UTILS_HPP) $(CARLA_MATH_UTILS_HPP) | LadspaPlugin.cpp.o: LadspaPlugin.cpp $(CARLA_PLUGIN_INTERNAL_HPP) $(CARLA_ENGINE_HPP) $(CARLA_LADSPA_UTILS_HPP) $(CARLA_MATH_UTILS_HPP) | ||||
| $(CXX) $< $(BUILD_CXX_FLAGS) -c -o $@ | $(CXX) $< $(BUILD_CXX_FLAGS) -c -o $@ | ||||
| DssiPlugin.cpp.o: DssiPlugin.cpp $(CARLA_PLUGIN_INTERNAL_HPP) $(CARLA_ENGINE_HPP) $(CARLA_DSSI_UTILS_HPP) $(CARLA_MATH_UTILS_HPP) | |||||
| DssiPlugin.cpp.o: DssiPlugin.cpp $(CARLA_PLUGIN_INTERNAL_HPP) $(CARLA_ENGINE_HPP) $(CARLA_DSSI_UTILS_HPP) $(CARLA_MATH_UTILS_HPP) $(CARLA_PLUGIN_UI_HPP) | |||||
| $(CXX) $< $(BUILD_CXX_FLAGS) $(QTCORE_FLAGS) -c -o $@ | $(CXX) $< $(BUILD_CXX_FLAGS) $(QTCORE_FLAGS) -c -o $@ | ||||
| Lv2Plugin.cpp.o: Lv2Plugin.cpp $(CARLA_PLUGIN_INTERNAL_HPP) $(CARLA_ENGINE_HPP) $(CARLA_MATH_UTILS_HPP) $(CARLA_LV2_UTILS_HPP) $(CARLA_HOST_H) $(CARLA_PLUGIN_UI_HPP) $(LV2_ATOM_QUEUE_HPP) $(CARLA_ENGINE_OSC_HPP) | |||||
| Lv2Plugin.cpp.o: Lv2Plugin.cpp $(CARLA_PLUGIN_INTERNAL_HPP) $(CARLA_ENGINE_HPP) $(CARLA_MATH_UTILS_HPP) $(CARLA_LV2_UTILS_HPP) $(CARLA_PLUGIN_UI_HPP) $(LV2_ATOM_QUEUE_HPP) $(CARLA_ENGINE_OSC_HPP) | |||||
| $(CXX) $< $(BUILD_CXX_FLAGS) $(QTCORE_FLAGS) -c -o $@ | $(CXX) $< $(BUILD_CXX_FLAGS) $(QTCORE_FLAGS) -c -o $@ | ||||
| VstPlugin.cpp.o: VstPlugin.cpp $(CARLA_PLUGIN_INTERNAL_HPP) $(CARLA_ENGINE_HPP) $(CARLA_VST_UTILS_HPP) $(CARLA_MATH_UTILS_HPP) | VstPlugin.cpp.o: VstPlugin.cpp $(CARLA_PLUGIN_INTERNAL_HPP) $(CARLA_ENGINE_HPP) $(CARLA_VST_UTILS_HPP) $(CARLA_MATH_UTILS_HPP) | ||||