@@ -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) | |||