| @@ -16,6 +16,7 @@ | |||
| */ | |||
| #include "CarlaPluginUi.hpp" | |||
| #include "CarlaHost.h" | |||
| #ifdef HAVE_X11 | |||
| # include <X11/Xatom.h> | |||
| @@ -54,6 +55,9 @@ public: | |||
| Atom wmDelete = XInternAtom(fDisplay, "WM_DELETE_WINDOW", True); | |||
| XSetWMProtocols(fDisplay, fWindow, &wmDelete, 1); | |||
| if (const uintptr_t transientId = carla_standalone_get_transient_win_id()) | |||
| setTransientWinId(transientId); | |||
| } | |||
| ~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 | |||
| CarlaPluginUi* CarlaPluginUi::newCocoa(CloseCallback* cb) | |||
| { | |||
| @@ -41,6 +41,8 @@ public: | |||
| virtual void setTransientWinId(const uintptr_t winId) = 0; | |||
| virtual void* getPtr() const noexcept = 0; | |||
| static bool tryTransientWinIdMatch(const char* const uiTitle, const uintptr_t winId); | |||
| #ifdef CARLA_OS_MAC | |||
| static CarlaPluginUi* newCocoa(CloseCallback*); | |||
| #endif | |||
| @@ -22,23 +22,18 @@ | |||
| #include "CarlaDssiUtils.hpp" | |||
| #include "CarlaMathUtils.hpp" | |||
| #include "CarlaPluginUi.hpp" | |||
| #include "CarlaHost.h" | |||
| #include <QtCore/QByteArray> | |||
| #include <QtCore/QString> | |||
| #ifdef CARLA_OS_LINUX | |||
| # include <X11/Xlib.h> | |||
| #endif | |||
| CARLA_BACKEND_START_NAMESPACE | |||
| #if 0 | |||
| } | |||
| #endif | |||
| // TESTING | |||
| static bool uiStarted = false; | |||
| class DssiPlugin : public CarlaPlugin | |||
| { | |||
| public: | |||
| @@ -52,7 +47,8 @@ public: | |||
| fUiFilename(nullptr), | |||
| fAudioInBuffers(nullptr), | |||
| fAudioOutBuffers(nullptr), | |||
| fParamBuffers(nullptr) | |||
| fParamBuffers(nullptr), | |||
| fTransientTryCounter(0) | |||
| { | |||
| carla_debug("DssiPlugin::DssiPlugin(%p, %i)", engine, id); | |||
| @@ -382,10 +378,11 @@ public: | |||
| { | |||
| pData->osc.data.free(); | |||
| pData->osc.thread.start(); | |||
| uiStarted = true; | |||
| } | |||
| else | |||
| { | |||
| fTransientTryCounter = 0; | |||
| if (pData->osc.data.target != nullptr) | |||
| { | |||
| osc_send_hide(pData->osc.data); | |||
| @@ -401,109 +398,18 @@ public: | |||
| { | |||
| CarlaPlugin::idle(); | |||
| #ifdef CARLA_OS_LINUX | |||
| if (! uiStarted) | |||
| if (fTransientTryCounter == 0) | |||
| return; | |||
| // TESTING | |||
| static int counter = 0; | |||
| if (++counter != 100) | |||
| if (++fTransientTryCounter % 10 != 0) | |||
| return; | |||
| if (fTransientTryCounter >= 200) | |||
| 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"); | |||
| } | |||
| // ------------------------------------------------------------------- | |||
| // 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 | |||
| @@ -2088,6 +2005,11 @@ private: | |||
| float* fParamBuffers; | |||
| snd_seq_event_t fMidiEvents[kPluginMaxMidiEvents]; | |||
| // used to try and set transient hint for external windows | |||
| uint fTransientTryCounter; | |||
| // ------------------------------------------------------------------- | |||
| static LinkedList<const char*> sMultiSynthList; | |||
| static bool addUniqueMultiSynth(const char* const label) | |||
| @@ -24,8 +24,6 @@ | |||
| #include "CarlaMathUtils.hpp" | |||
| #include "CarlaLv2Utils.hpp" | |||
| #include "CarlaHost.h" | |||
| #include "CarlaPluginUi.hpp" | |||
| #include "Lv2AtomQueue.hpp" | |||
| @@ -1160,20 +1158,8 @@ public: | |||
| if (fUi.window == nullptr) | |||
| 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); | |||
| #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(); | |||
| 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) | |||
| $(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 $@ | |||
| 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) | |||
| $(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 $@ | |||
| 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 $@ | |||
| VstPlugin.cpp.o: VstPlugin.cpp $(CARLA_PLUGIN_INTERNAL_HPP) $(CARLA_ENGINE_HPP) $(CARLA_VST_UTILS_HPP) $(CARLA_MATH_UTILS_HPP) | |||