Audio plugin host https://kx.studio/carla
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

CarlaPluginUI.cpp 45KB

10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451
  1. /*
  2. * Carla Plugin UI
  3. * Copyright (C) 2014-2021 Filipe Coelho <falktx@falktx.com>
  4. *
  5. * This program is free software; you can redistribute it and/or
  6. * modify it under the terms of the GNU General Public License as
  7. * published by the Free Software Foundation; either version 2 of
  8. * the License, or any later version.
  9. *
  10. * This program is distributed in the hope that it will be useful,
  11. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  12. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  13. * GNU General Public License for more details.
  14. *
  15. * For a full copy of the GNU General Public License see the doc/GPL.txt file.
  16. */
  17. #include "CarlaJuceUtils.hpp"
  18. #include "CarlaPluginUI.hpp"
  19. #ifdef HAVE_X11
  20. # include <sys/types.h>
  21. # include <X11/Xatom.h>
  22. # include <X11/Xlib.h>
  23. # include <X11/Xutil.h>
  24. # include "CarlaPluginUI_X11Icon.hpp"
  25. #endif
  26. #ifdef CARLA_OS_MAC
  27. # include "CarlaMacUtils.hpp"
  28. # import <Cocoa/Cocoa.h>
  29. #endif
  30. #ifdef CARLA_OS_WIN
  31. # include <ctime>
  32. # include "water/common.hpp"
  33. #endif
  34. #ifndef CARLA_PLUGIN_UI_CLASS_PREFIX
  35. # error CARLA_PLUGIN_UI_CLASS_PREFIX undefined
  36. #endif
  37. // ---------------------------------------------------------------------------------------------------------------------
  38. // X11
  39. #ifdef HAVE_X11
  40. typedef void (*EventProcPtr)(XEvent* ev);
  41. static const uint X11Key_Escape = 9;
  42. static bool gErrorTriggered = false;
  43. # if defined(__GNUC__) && (__GNUC__ >= 5) && ! defined(__clang__)
  44. # pragma GCC diagnostic push
  45. # pragma GCC diagnostic ignored "-Wzero-as-null-pointer-constant"
  46. # endif
  47. static pthread_mutex_t gErrorMutex = PTHREAD_MUTEX_INITIALIZER;
  48. # if defined(__GNUC__) && (__GNUC__ >= 5) && ! defined(__clang__)
  49. # pragma GCC diagnostic pop
  50. # endif
  51. static int temporaryErrorHandler(Display*, XErrorEvent*)
  52. {
  53. gErrorTriggered = true;
  54. return 0;
  55. }
  56. class X11PluginUI : public CarlaPluginUI
  57. {
  58. public:
  59. X11PluginUI(Callback* const cb, const uintptr_t parentId,
  60. const bool isStandalone, const bool isResizable, const bool canMonitorChildren) noexcept
  61. : CarlaPluginUI(cb, isStandalone, isResizable),
  62. fDisplay(nullptr),
  63. fHostWindow(0),
  64. fChildWindow(0),
  65. fChildWindowConfigured(false),
  66. fChildWindowMonitoring(isResizable || canMonitorChildren),
  67. fIsVisible(false),
  68. fFirstShow(true),
  69. fSetSizeCalledAtLeastOnce(false),
  70. fEventProc(nullptr)
  71. {
  72. fDisplay = XOpenDisplay(nullptr);
  73. CARLA_SAFE_ASSERT_RETURN(fDisplay != nullptr,);
  74. const int screen = DefaultScreen(fDisplay);
  75. XSetWindowAttributes attr;
  76. carla_zeroStruct(attr);
  77. attr.event_mask = KeyPressMask|KeyReleaseMask|FocusChangeMask;
  78. if (fChildWindowMonitoring)
  79. attr.event_mask |= StructureNotifyMask|SubstructureNotifyMask;
  80. fHostWindow = XCreateWindow(fDisplay, RootWindow(fDisplay, screen),
  81. 0, 0, 300, 300, 0,
  82. DefaultDepth(fDisplay, screen),
  83. InputOutput,
  84. DefaultVisual(fDisplay, screen),
  85. CWBorderPixel|CWEventMask, &attr);
  86. CARLA_SAFE_ASSERT_RETURN(fHostWindow != 0,);
  87. XGrabKey(fDisplay, X11Key_Escape, AnyModifier, fHostWindow, 1, GrabModeAsync, GrabModeAsync);
  88. Atom wmDelete = XInternAtom(fDisplay, "WM_DELETE_WINDOW", True);
  89. XSetWMProtocols(fDisplay, fHostWindow, &wmDelete, 1);
  90. const pid_t pid = getpid();
  91. const Atom _nwp = XInternAtom(fDisplay, "_NET_WM_PID", False);
  92. XChangeProperty(fDisplay, fHostWindow, _nwp, XA_CARDINAL, 32, PropModeReplace, (const uchar*)&pid, 1);
  93. const Atom _nwi = XInternAtom(fDisplay, "_NET_WM_ICON", False);
  94. XChangeProperty(fDisplay, fHostWindow, _nwi, XA_CARDINAL, 32, PropModeReplace, (const uchar*)sCarlaX11Icon, sCarlaX11IconSize);
  95. const Atom _wt = XInternAtom(fDisplay, "_NET_WM_WINDOW_TYPE", False);
  96. // Setting the window to both dialog and normal will produce a decorated floating dialog
  97. // Order is important: DIALOG needs to come before NORMAL
  98. const Atom _wts[2] = {
  99. XInternAtom(fDisplay, "_NET_WM_WINDOW_TYPE_DIALOG", False),
  100. XInternAtom(fDisplay, "_NET_WM_WINDOW_TYPE_NORMAL", False)
  101. };
  102. XChangeProperty(fDisplay, fHostWindow, _wt, XA_ATOM, 32, PropModeReplace, (const uchar*)&_wts, 2);
  103. if (parentId != 0)
  104. setTransientWinId(parentId);
  105. }
  106. ~X11PluginUI() override
  107. {
  108. CARLA_SAFE_ASSERT(! fIsVisible);
  109. if (fDisplay == nullptr)
  110. return;
  111. if (fIsVisible)
  112. {
  113. XUnmapWindow(fDisplay, fHostWindow);
  114. fIsVisible = false;
  115. }
  116. if (fHostWindow != 0)
  117. {
  118. XDestroyWindow(fDisplay, fHostWindow);
  119. fHostWindow = 0;
  120. }
  121. XCloseDisplay(fDisplay);
  122. fDisplay = nullptr;
  123. }
  124. void show() override
  125. {
  126. CARLA_SAFE_ASSERT_RETURN(fDisplay != nullptr,);
  127. CARLA_SAFE_ASSERT_RETURN(fHostWindow != 0,);
  128. if (fFirstShow)
  129. {
  130. if (const Window childWindow = getChildWindow())
  131. {
  132. if (! fSetSizeCalledAtLeastOnce)
  133. {
  134. int width = 0;
  135. int height = 0;
  136. XWindowAttributes attrs;
  137. carla_zeroStruct(attrs);
  138. pthread_mutex_lock(&gErrorMutex);
  139. const XErrorHandler oldErrorHandler = XSetErrorHandler(temporaryErrorHandler);
  140. gErrorTriggered = false;
  141. if (XGetWindowAttributes(fDisplay, childWindow, &attrs))
  142. {
  143. width = attrs.width;
  144. height = attrs.height;
  145. }
  146. XSetErrorHandler(oldErrorHandler);
  147. pthread_mutex_unlock(&gErrorMutex);
  148. if (width == 0 && height == 0)
  149. {
  150. XSizeHints sizeHints;
  151. carla_zeroStruct(sizeHints);
  152. if (XGetNormalHints(fDisplay, childWindow, &sizeHints))
  153. {
  154. if (sizeHints.flags & PSize)
  155. {
  156. width = sizeHints.width;
  157. height = sizeHints.height;
  158. }
  159. else if (sizeHints.flags & PBaseSize)
  160. {
  161. width = sizeHints.base_width;
  162. height = sizeHints.base_height;
  163. }
  164. }
  165. }
  166. if (width > 1 && height > 1)
  167. setSize(static_cast<uint>(width), static_cast<uint>(height), false);
  168. }
  169. const Atom _xevp = XInternAtom(fDisplay, "_XEventProc", False);
  170. pthread_mutex_lock(&gErrorMutex);
  171. const XErrorHandler oldErrorHandler(XSetErrorHandler(temporaryErrorHandler));
  172. gErrorTriggered = false;
  173. Atom actualType;
  174. int actualFormat;
  175. ulong nitems, bytesAfter;
  176. uchar* data = nullptr;
  177. XGetWindowProperty(fDisplay, childWindow, _xevp, 0, 1, False, AnyPropertyType,
  178. &actualType, &actualFormat, &nitems, &bytesAfter, &data);
  179. XSetErrorHandler(oldErrorHandler);
  180. pthread_mutex_unlock(&gErrorMutex);
  181. if (nitems == 1 && ! gErrorTriggered)
  182. {
  183. fEventProc = *reinterpret_cast<EventProcPtr*>(data);
  184. XMapRaised(fDisplay, childWindow);
  185. }
  186. }
  187. }
  188. fIsVisible = true;
  189. fFirstShow = false;
  190. XMapRaised(fDisplay, fHostWindow);
  191. XSync(fDisplay, False);
  192. }
  193. void hide() override
  194. {
  195. CARLA_SAFE_ASSERT_RETURN(fDisplay != nullptr,);
  196. CARLA_SAFE_ASSERT_RETURN(fHostWindow != 0,);
  197. fIsVisible = false;
  198. XUnmapWindow(fDisplay, fHostWindow);
  199. XFlush(fDisplay);
  200. }
  201. void idle() override
  202. {
  203. // prevent recursion
  204. if (fIsIdling) return;
  205. int nextWidth = 0;
  206. int nextHeight = 0;
  207. fIsIdling = true;
  208. for (XEvent event; XPending(fDisplay) > 0;)
  209. {
  210. XNextEvent(fDisplay, &event);
  211. if (! fIsVisible)
  212. continue;
  213. char* type = nullptr;
  214. switch (event.type)
  215. {
  216. case ConfigureNotify:
  217. CARLA_SAFE_ASSERT_CONTINUE(fCallback != nullptr);
  218. CARLA_SAFE_ASSERT_CONTINUE(event.xconfigure.width > 0);
  219. CARLA_SAFE_ASSERT_CONTINUE(event.xconfigure.height > 0);
  220. if (event.xconfigure.window == fHostWindow)
  221. {
  222. const uint width = static_cast<uint>(event.xconfigure.width);
  223. const uint height = static_cast<uint>(event.xconfigure.height);
  224. if (fChildWindow != 0)
  225. {
  226. if (! fChildWindowConfigured)
  227. {
  228. pthread_mutex_lock(&gErrorMutex);
  229. const XErrorHandler oldErrorHandler = XSetErrorHandler(temporaryErrorHandler);
  230. gErrorTriggered = false;
  231. XSizeHints sizeHints;
  232. carla_zeroStruct(sizeHints);
  233. if (XGetNormalHints(fDisplay, fChildWindow, &sizeHints) && !gErrorTriggered)
  234. {
  235. XSetNormalHints(fDisplay, fHostWindow, &sizeHints);
  236. }
  237. else
  238. {
  239. carla_stdout("Caught errors while accessing child window");
  240. fChildWindow = 0;
  241. }
  242. fChildWindowConfigured = true;
  243. XSetErrorHandler(oldErrorHandler);
  244. pthread_mutex_unlock(&gErrorMutex);
  245. }
  246. if (fChildWindow != 0)
  247. XResizeWindow(fDisplay, fChildWindow, width, height);
  248. }
  249. fCallback->handlePluginUIResized(width, height);
  250. }
  251. else if (fChildWindowMonitoring && event.xconfigure.window == fChildWindow && fChildWindow != 0)
  252. {
  253. nextWidth = event.xconfigure.width;
  254. nextHeight = event.xconfigure.height;
  255. }
  256. break;
  257. case ClientMessage:
  258. type = XGetAtomName(fDisplay, event.xclient.message_type);
  259. CARLA_SAFE_ASSERT_CONTINUE(type != nullptr);
  260. if (std::strcmp(type, "WM_PROTOCOLS") == 0)
  261. {
  262. fIsVisible = false;
  263. CARLA_SAFE_ASSERT_CONTINUE(fCallback != nullptr);
  264. fCallback->handlePluginUIClosed();
  265. }
  266. break;
  267. case KeyRelease:
  268. if (event.xkey.keycode == X11Key_Escape)
  269. {
  270. fIsVisible = false;
  271. CARLA_SAFE_ASSERT_CONTINUE(fCallback != nullptr);
  272. fCallback->handlePluginUIClosed();
  273. }
  274. break;
  275. case FocusIn:
  276. if (fChildWindow == 0)
  277. fChildWindow = getChildWindow();
  278. if (fChildWindow != 0)
  279. XSetInputFocus(fDisplay, fChildWindow, RevertToPointerRoot, CurrentTime);
  280. break;
  281. }
  282. if (type != nullptr)
  283. XFree(type);
  284. else if (fEventProc != nullptr && event.type != FocusIn && event.type != FocusOut)
  285. fEventProc(&event);
  286. }
  287. if (nextWidth != 0 && nextHeight != 0)
  288. {
  289. XSizeHints sizeHints;
  290. carla_zeroStruct(sizeHints);
  291. if (XGetNormalHints(fDisplay, fChildWindow, &sizeHints))
  292. XSetNormalHints(fDisplay, fHostWindow, &sizeHints);
  293. XResizeWindow(fDisplay, fHostWindow, static_cast<uint>(nextWidth), static_cast<uint>(nextHeight));
  294. XFlush(fDisplay);
  295. }
  296. fIsIdling = false;
  297. }
  298. void focus() override
  299. {
  300. CARLA_SAFE_ASSERT_RETURN(fDisplay != nullptr,);
  301. CARLA_SAFE_ASSERT_RETURN(fHostWindow != 0,);
  302. XWindowAttributes wa;
  303. carla_zeroStruct(wa);
  304. CARLA_SAFE_ASSERT_RETURN(XGetWindowAttributes(fDisplay, fHostWindow, &wa),);
  305. if (wa.map_state == IsViewable)
  306. {
  307. XRaiseWindow(fDisplay, fHostWindow);
  308. XSetInputFocus(fDisplay, fHostWindow, RevertToPointerRoot, CurrentTime);
  309. XSync(fDisplay, False);
  310. }
  311. }
  312. void setSize(const uint width, const uint height, const bool forceUpdate) override
  313. {
  314. CARLA_SAFE_ASSERT_RETURN(fDisplay != nullptr,);
  315. CARLA_SAFE_ASSERT_RETURN(fHostWindow != 0,);
  316. fSetSizeCalledAtLeastOnce = true;
  317. XResizeWindow(fDisplay, fHostWindow, width, height);
  318. if (fChildWindow != 0)
  319. XResizeWindow(fDisplay, fChildWindow, width, height);
  320. if (! fIsResizable)
  321. {
  322. XSizeHints sizeHints;
  323. carla_zeroStruct(sizeHints);
  324. sizeHints.flags = PSize|PMinSize|PMaxSize;
  325. sizeHints.width = static_cast<int>(width);
  326. sizeHints.height = static_cast<int>(height);
  327. sizeHints.min_width = static_cast<int>(width);
  328. sizeHints.min_height = static_cast<int>(height);
  329. sizeHints.max_width = static_cast<int>(width);
  330. sizeHints.max_height = static_cast<int>(height);
  331. XSetNormalHints(fDisplay, fHostWindow, &sizeHints);
  332. }
  333. if (forceUpdate)
  334. XSync(fDisplay, False);
  335. }
  336. void setTitle(const char* const title) override
  337. {
  338. CARLA_SAFE_ASSERT_RETURN(fDisplay != nullptr,);
  339. CARLA_SAFE_ASSERT_RETURN(fHostWindow != 0,);
  340. XStoreName(fDisplay, fHostWindow, title);
  341. }
  342. void setTransientWinId(const uintptr_t winId) override
  343. {
  344. CARLA_SAFE_ASSERT_RETURN(fDisplay != nullptr,);
  345. CARLA_SAFE_ASSERT_RETURN(fHostWindow != 0,);
  346. XSetTransientForHint(fDisplay, fHostWindow, static_cast<Window>(winId));
  347. }
  348. void setChildWindow(void* const winId) override
  349. {
  350. CARLA_SAFE_ASSERT_RETURN(winId != nullptr,);
  351. fChildWindow = (Window)winId;
  352. }
  353. void* getPtr() const noexcept override
  354. {
  355. return (void*)fHostWindow;
  356. }
  357. void* getDisplay() const noexcept override
  358. {
  359. return fDisplay;
  360. }
  361. protected:
  362. Window getChildWindow() const
  363. {
  364. CARLA_SAFE_ASSERT_RETURN(fDisplay != nullptr, 0);
  365. CARLA_SAFE_ASSERT_RETURN(fHostWindow != 0, 0);
  366. Window rootWindow, parentWindow, ret = 0;
  367. Window* childWindows = nullptr;
  368. uint numChildren = 0;
  369. XQueryTree(fDisplay, fHostWindow, &rootWindow, &parentWindow, &childWindows, &numChildren);
  370. if (numChildren > 0 && childWindows != nullptr)
  371. {
  372. ret = childWindows[0];
  373. XFree(childWindows);
  374. }
  375. return ret;
  376. }
  377. private:
  378. Display* fDisplay;
  379. Window fHostWindow;
  380. Window fChildWindow;
  381. bool fChildWindowConfigured;
  382. bool fChildWindowMonitoring;
  383. bool fIsVisible;
  384. bool fFirstShow;
  385. bool fSetSizeCalledAtLeastOnce;
  386. EventProcPtr fEventProc;
  387. CARLA_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR(X11PluginUI)
  388. };
  389. #endif // HAVE_X11
  390. // ---------------------------------------------------------------------------------------------------------------------
  391. // MacOS / Cocoa
  392. #ifdef CARLA_OS_MAC
  393. #if defined(BUILD_BRIDGE_ALTERNATIVE_ARCH)
  394. # define CarlaPluginWindow CARLA_JOIN_MACRO(CarlaPluginWindowBridgedArch, CARLA_PLUGIN_UI_CLASS_PREFIX)
  395. #elif defined(BUILD_BRIDGE)
  396. # define CarlaPluginWindow CARLA_JOIN_MACRO(CarlaPluginWindowBridged, CARLA_PLUGIN_UI_CLASS_PREFIX)
  397. #else
  398. # define CarlaPluginWindow CARLA_JOIN_MACRO(CarlaPluginWindow, CARLA_PLUGIN_UI_CLASS_PREFIX)
  399. #endif
  400. @interface CarlaPluginWindow : NSWindow
  401. - (id) initWithContentRect:(NSRect)contentRect
  402. styleMask:(unsigned int)aStyle
  403. backing:(NSBackingStoreType)bufferingType
  404. defer:(BOOL)flag;
  405. - (BOOL) canBecomeKeyWindow;
  406. - (BOOL) canBecomeMainWindow;
  407. @end
  408. @implementation CarlaPluginWindow
  409. - (id)initWithContentRect:(NSRect)contentRect
  410. styleMask:(unsigned int)aStyle
  411. backing:(NSBackingStoreType)bufferingType
  412. defer:(BOOL)flag
  413. {
  414. NSWindow* result = [super initWithContentRect:contentRect
  415. styleMask:aStyle
  416. backing:bufferingType
  417. defer:flag];
  418. [result setAcceptsMouseMovedEvents:YES];
  419. return (CarlaPluginWindow*)result;
  420. // unused
  421. (void)flag;
  422. }
  423. - (BOOL)canBecomeKeyWindow
  424. {
  425. return YES;
  426. }
  427. - (BOOL)canBecomeMainWindow
  428. {
  429. return NO;
  430. }
  431. @end
  432. @interface CarlaPluginWindowDelegate : NSObject<NSWindowDelegate>
  433. {
  434. CarlaPluginUI::Callback* callback;
  435. CarlaPluginWindow* window;
  436. }
  437. - (instancetype)initWithWindowAndCallback:(CarlaPluginWindow*)window
  438. callback:(CarlaPluginUI::Callback*)callback2;
  439. - (BOOL)windowShouldClose:(id)sender;
  440. @end
  441. @implementation CarlaPluginWindowDelegate
  442. - (instancetype)initWithWindowAndCallback:(CarlaPluginWindow*)window2
  443. callback:(CarlaPluginUI::Callback*)callback2
  444. {
  445. if ((self = [super init]))
  446. {
  447. callback = callback2;
  448. window = window2;
  449. }
  450. return self;
  451. }
  452. - (BOOL)windowShouldClose:(id)sender
  453. {
  454. if (callback != nil)
  455. callback->handlePluginUIClosed();
  456. return NO;
  457. // unused
  458. (void)sender;
  459. }
  460. @end
  461. @interface CarlaPluginView : NSView
  462. - (void)resizeWithOldSuperviewSize:(NSSize)oldSize;
  463. @end
  464. @implementation CarlaPluginView
  465. - (void)resizeWithOldSuperviewSize:(NSSize)oldSize
  466. {
  467. [super resizeWithOldSuperviewSize:oldSize];
  468. /*
  469. for (NSView* subview in [self subviews])
  470. {
  471. [subview setFrame:NSMakeRect(0, 0, oldSize.width, oldSize.height)];
  472. break;
  473. }
  474. */
  475. }
  476. @end
  477. class CocoaPluginUI : public CarlaPluginUI
  478. {
  479. public:
  480. CocoaPluginUI(Callback* const callback, const uintptr_t parentId, const bool isStandalone, const bool isResizable) noexcept
  481. : CarlaPluginUI(callback, isStandalone, isResizable),
  482. fView(nullptr),
  483. fParentWindow(nullptr),
  484. fWindow(nullptr)
  485. {
  486. carla_debug("CocoaPluginUI::CocoaPluginUI(%p, " P_UINTPTR, "%s)", callback, parentId, bool2str(isResizable));
  487. const CarlaBackend::AutoNSAutoreleasePool arp;
  488. [NSApplication sharedApplication];
  489. fView = [[CarlaPluginView new]retain];
  490. CARLA_SAFE_ASSERT_RETURN(fView != nullptr,)
  491. uint style = NSClosableWindowMask | NSTitledWindowMask;
  492. /*
  493. if (isResizable)
  494. style |= NSResizableWindowMask;
  495. */
  496. const NSRect frame = NSMakeRect(0, 0, 100, 100);
  497. fWindow = [[[CarlaPluginWindow alloc]
  498. initWithContentRect:frame
  499. styleMask:style
  500. backing:NSBackingStoreBuffered
  501. defer:NO
  502. ] retain];
  503. if (fWindow == nullptr)
  504. {
  505. [fView release];
  506. fView = nullptr;
  507. return;
  508. }
  509. ((NSWindow*)fWindow).delegate = [[[CarlaPluginWindowDelegate alloc]
  510. initWithWindowAndCallback:fWindow
  511. callback:callback] retain];
  512. // if (! isResizable)
  513. {
  514. [fView setAutoresizingMask:NSViewNotSizable];
  515. [[fWindow standardWindowButton:NSWindowZoomButton] setHidden:YES];
  516. }
  517. [fWindow setContentView:fView];
  518. [fWindow makeFirstResponder:fView];
  519. [fView setHidden:NO];
  520. if (parentId != 0)
  521. setTransientWinId(parentId);
  522. }
  523. ~CocoaPluginUI() override
  524. {
  525. carla_debug("CocoaPluginUI::~CocoaPluginUI()");
  526. if (fView == nullptr)
  527. return;
  528. [fView setHidden:YES];
  529. [fView removeFromSuperview];
  530. [fWindow close];
  531. [fView release];
  532. [fWindow release];
  533. }
  534. void show() override
  535. {
  536. carla_debug("CocoaPluginUI::show()");
  537. CARLA_SAFE_ASSERT_RETURN(fView != nullptr,);
  538. if (fParentWindow != nullptr)
  539. {
  540. [fParentWindow addChildWindow:fWindow
  541. ordered:NSWindowAbove];
  542. }
  543. else
  544. {
  545. [fWindow setIsVisible:YES];
  546. }
  547. }
  548. void hide() override
  549. {
  550. carla_debug("CocoaPluginUI::hide()");
  551. CARLA_SAFE_ASSERT_RETURN(fView != nullptr,);
  552. [fWindow setIsVisible:NO];
  553. if (fParentWindow != nullptr)
  554. [fParentWindow removeChildWindow:fWindow];
  555. }
  556. void idle() override
  557. {
  558. // carla_debug("CocoaPluginUI::idle()");
  559. }
  560. void focus() override
  561. {
  562. carla_debug("CocoaPluginUI::focus()");
  563. CARLA_SAFE_ASSERT_RETURN(fWindow != nullptr,);
  564. [fWindow makeKeyAndOrderFront:fWindow];
  565. [fWindow orderFrontRegardless];
  566. [NSApp activateIgnoringOtherApps:YES];
  567. }
  568. void setSize(const uint width, const uint height, const bool forceUpdate) override
  569. {
  570. carla_debug("CocoaPluginUI::setSize(%u, %u, %s)", width, height, bool2str(forceUpdate));
  571. CARLA_SAFE_ASSERT_RETURN(fWindow != nullptr,);
  572. CARLA_SAFE_ASSERT_RETURN(fView != nullptr,);
  573. [fView setFrame:NSMakeRect(0, 0, width, height)];
  574. for (NSView* subview in [fView subviews])
  575. {
  576. [subview setFrame:NSMakeRect(0, 0, width, height)];
  577. break;
  578. }
  579. const NSSize size = NSMakeSize(width, height);
  580. [fWindow setContentSize:size];
  581. /*
  582. if (fIsResizable)
  583. {
  584. [fWindow setContentMinSize:NSMakeSize(1, 1)];
  585. [fWindow setContentMaxSize:NSMakeSize(99999, 99999)];
  586. }
  587. else
  588. {
  589. [fWindow setContentMinSize:size];
  590. [fWindow setContentMaxSize:size];
  591. }
  592. */
  593. if (forceUpdate)
  594. {
  595. // FIXME, not enough
  596. [fView setNeedsDisplay:YES];
  597. }
  598. }
  599. void setTitle(const char* const title) override
  600. {
  601. carla_debug("CocoaPluginUI::setTitle(\"%s\")", title);
  602. CARLA_SAFE_ASSERT_RETURN(fWindow != nullptr,);
  603. NSString* titleString = [[NSString alloc]
  604. initWithBytes:title
  605. length:strlen(title)
  606. encoding:NSUTF8StringEncoding];
  607. [fWindow setTitle:titleString];
  608. }
  609. void setTransientWinId(const uintptr_t winId) override
  610. {
  611. carla_debug("CocoaPluginUI::setTransientWinId(" P_UINTPTR ")", winId);
  612. CARLA_SAFE_ASSERT_RETURN(fWindow != nullptr,);
  613. NSWindow* const parentWindow = [NSApp windowWithWindowNumber:winId];
  614. CARLA_SAFE_ASSERT_RETURN(parentWindow != nullptr,);
  615. fParentWindow = parentWindow;
  616. if ([fWindow isVisible])
  617. [fParentWindow addChildWindow:fWindow
  618. ordered:NSWindowAbove];
  619. }
  620. void setChildWindow(void* const childWindow) override
  621. {
  622. carla_debug("CocoaPluginUI::setChildWindow(%p)", childWindow);
  623. CARLA_SAFE_ASSERT_RETURN(childWindow != nullptr,);
  624. NSView* const view = (NSView*)childWindow;
  625. const NSRect frame = [view frame];
  626. [fWindow setContentSize:frame.size];
  627. [fView setFrame:frame];
  628. [fView setNeedsDisplay:YES];
  629. }
  630. void* getPtr() const noexcept override
  631. {
  632. carla_debug("CocoaPluginUI::getPtr()");
  633. return (void*)fView;
  634. }
  635. void* getDisplay() const noexcept
  636. {
  637. carla_debug("CocoaPluginUI::getDisplay()");
  638. return (void*)fWindow;
  639. }
  640. private:
  641. NSView* fView;
  642. NSWindow* fParentWindow;
  643. CarlaPluginWindow* fWindow;
  644. CARLA_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR(CocoaPluginUI)
  645. };
  646. #endif // CARLA_OS_MAC
  647. // ---------------------------------------------------------------------------------------------------------------------
  648. // Windows
  649. #ifdef CARLA_OS_WIN
  650. #define CARLA_LOCAL_CLOSE_MSG (WM_USER + 50)
  651. static LRESULT CALLBACK wndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam);
  652. class WindowsPluginUI : public CarlaPluginUI
  653. {
  654. public:
  655. WindowsPluginUI(Callback* const cb, const uintptr_t parentId, const bool isStandalone, const bool isResizable) noexcept
  656. : CarlaPluginUI(cb, isStandalone, isResizable),
  657. fWindow(nullptr),
  658. fChildWindow(nullptr),
  659. fParentWindow(nullptr),
  660. fIsVisible(false),
  661. fFirstShow(true)
  662. {
  663. // FIXME
  664. static int wc_count = 0;
  665. char classNameBuf[32];
  666. std::srand((std::time(nullptr)));
  667. std::snprintf(classNameBuf, 32, "CarlaWin-%d-%d", ++wc_count, std::rand());
  668. classNameBuf[31] = '\0';
  669. const HINSTANCE hInstance = water::getCurrentModuleInstanceHandle();
  670. carla_zeroStruct(fWindowClass);
  671. fWindowClass.style = CS_OWNDC;
  672. fWindowClass.lpfnWndProc = wndProc;
  673. fWindowClass.hInstance = hInstance;
  674. fWindowClass.hIcon = LoadIcon(hInstance, IDI_APPLICATION);
  675. fWindowClass.hCursor = LoadCursor(hInstance, IDC_ARROW);
  676. fWindowClass.lpszClassName = strdup(classNameBuf);
  677. if (!RegisterClass(&fWindowClass)) {
  678. free((void*)fWindowClass.lpszClassName);
  679. return;
  680. }
  681. int winFlags = WS_POPUPWINDOW | WS_CAPTION;
  682. if (isResizable)
  683. winFlags |= WS_SIZEBOX;
  684. #ifdef BUILDING_CARLA_FOR_WINE
  685. const uint winType = WS_EX_DLGMODALFRAME;
  686. const HWND parent = nullptr;
  687. #else
  688. const uint winType = WS_EX_TOOLWINDOW;
  689. const HWND parent = (HWND)parentId;
  690. #endif
  691. fWindow = CreateWindowEx(winType,
  692. classNameBuf, "Carla Plugin UI", winFlags,
  693. CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT,
  694. parent, nullptr,
  695. hInstance, nullptr);
  696. if (fWindow == nullptr)
  697. {
  698. const DWORD errorCode = ::GetLastError();
  699. carla_stderr2("CreateWindowEx failed with error code 0x%x, class name was '%s'",
  700. errorCode, fWindowClass.lpszClassName);
  701. UnregisterClass(fWindowClass.lpszClassName, nullptr);
  702. free((void*)fWindowClass.lpszClassName);
  703. return;
  704. }
  705. SetWindowLongPtr(fWindow, GWLP_USERDATA, (LONG_PTR)this);
  706. #ifndef BUILDING_CARLA_FOR_WINE
  707. if (parentId != 0)
  708. setTransientWinId(parentId);
  709. #endif
  710. return;
  711. // maybe unused
  712. (void)parentId;
  713. }
  714. ~WindowsPluginUI() override
  715. {
  716. CARLA_SAFE_ASSERT(! fIsVisible);
  717. if (fWindow != 0)
  718. {
  719. if (fIsVisible)
  720. ShowWindow(fWindow, SW_HIDE);
  721. DestroyWindow(fWindow);
  722. fWindow = 0;
  723. }
  724. // FIXME
  725. UnregisterClass(fWindowClass.lpszClassName, nullptr);
  726. free((void*)fWindowClass.lpszClassName);
  727. }
  728. void show() override
  729. {
  730. CARLA_SAFE_ASSERT_RETURN(fWindow != nullptr,);
  731. if (fFirstShow)
  732. {
  733. fFirstShow = false;
  734. RECT rectChild, rectParent;
  735. if (fChildWindow != nullptr && GetWindowRect(fChildWindow, &rectChild))
  736. setSize(rectChild.right - rectChild.left, rectChild.bottom - rectChild.top, false);
  737. if (fParentWindow != nullptr &&
  738. GetWindowRect(fWindow, &rectChild) &&
  739. GetWindowRect(fParentWindow, &rectParent))
  740. {
  741. SetWindowPos(fWindow, fParentWindow,
  742. rectParent.left + (rectChild.right-rectChild.left)/2,
  743. rectParent.top + (rectChild.bottom-rectChild.top)/2,
  744. 0, 0, SWP_SHOWWINDOW|SWP_NOSIZE);
  745. }
  746. else
  747. {
  748. ShowWindow(fWindow, SW_SHOWNORMAL);
  749. }
  750. }
  751. else
  752. {
  753. ShowWindow(fWindow, SW_RESTORE);
  754. }
  755. fIsVisible = true;
  756. UpdateWindow(fWindow);
  757. }
  758. void hide() override
  759. {
  760. CARLA_SAFE_ASSERT_RETURN(fWindow != nullptr,);
  761. ShowWindow(fWindow, SW_HIDE);
  762. fIsVisible = false;
  763. UpdateWindow(fWindow);
  764. }
  765. void idle() override
  766. {
  767. if (fIsIdling || fWindow == nullptr)
  768. return;
  769. MSG msg;
  770. fIsIdling = true;
  771. while (::PeekMessage(&msg, fWindow, 0, 0, PM_REMOVE))
  772. {
  773. switch (msg.message)
  774. {
  775. case WM_QUIT:
  776. case CARLA_LOCAL_CLOSE_MSG:
  777. fIsVisible = false;
  778. CARLA_SAFE_ASSERT_BREAK(fCallback != nullptr);
  779. fCallback->handlePluginUIClosed();
  780. break;
  781. }
  782. DispatchMessageA(&msg);
  783. }
  784. fIsIdling = false;
  785. }
  786. LRESULT checkAndHandleMessage(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
  787. {
  788. if (fWindow == hwnd)
  789. {
  790. switch (message)
  791. {
  792. case WM_SIZE:
  793. if (fChildWindow != nullptr)
  794. {
  795. RECT rect;
  796. GetClientRect(fWindow, &rect);
  797. SetWindowPos(fChildWindow, 0, 0, 0, rect.right, rect.bottom,
  798. SWP_NOACTIVATE|SWP_NOMOVE|SWP_NOOWNERZORDER|SWP_NOZORDER);
  799. }
  800. break;
  801. case WM_QUIT:
  802. case CARLA_LOCAL_CLOSE_MSG:
  803. fIsVisible = false;
  804. CARLA_SAFE_ASSERT_BREAK(fCallback != nullptr);
  805. fCallback->handlePluginUIClosed();
  806. break;
  807. }
  808. }
  809. return DefWindowProcA(hwnd, message, wParam, lParam);
  810. }
  811. void focus() override
  812. {
  813. CARLA_SAFE_ASSERT_RETURN(fWindow != nullptr,);
  814. SetForegroundWindow(fWindow);
  815. SetActiveWindow(fWindow);
  816. SetFocus(fWindow);
  817. }
  818. void setSize(const uint width, const uint height, const bool forceUpdate) override
  819. {
  820. CARLA_SAFE_ASSERT_RETURN(fWindow != nullptr,);
  821. const int winFlags = WS_POPUPWINDOW | WS_CAPTION | (fIsResizable ? WS_SIZEBOX : 0x0);
  822. RECT wr = { 0, 0, static_cast<long>(width), static_cast<long>(height) };
  823. AdjustWindowRectEx(&wr, winFlags, FALSE, WS_EX_TOPMOST);
  824. SetWindowPos(fWindow, 0, 0, 0, wr.right-wr.left, wr.bottom-wr.top,
  825. SWP_NOACTIVATE|SWP_NOMOVE|SWP_NOOWNERZORDER|SWP_NOZORDER);
  826. if (forceUpdate)
  827. UpdateWindow(fWindow);
  828. }
  829. void setTitle(const char* const title) override
  830. {
  831. CARLA_SAFE_ASSERT_RETURN(fWindow != nullptr,);
  832. SetWindowTextA(fWindow, title);
  833. }
  834. void setTransientWinId(const uintptr_t winId) override
  835. {
  836. CARLA_SAFE_ASSERT_RETURN(fWindow != nullptr,);
  837. fParentWindow = (HWND)winId;
  838. SetWindowLongPtr(fWindow, GWLP_HWNDPARENT, (LONG_PTR)winId);
  839. }
  840. void setChildWindow(void* const winId) override
  841. {
  842. CARLA_SAFE_ASSERT_RETURN(winId != nullptr,);
  843. fChildWindow = (HWND)winId;
  844. }
  845. void* getPtr() const noexcept override
  846. {
  847. return (void*)fWindow;
  848. }
  849. void* getDisplay() const noexcept
  850. {
  851. return nullptr;
  852. }
  853. private:
  854. HWND fWindow;
  855. HWND fChildWindow;
  856. HWND fParentWindow;
  857. WNDCLASS fWindowClass;
  858. bool fIsVisible;
  859. bool fFirstShow;
  860. CARLA_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR(WindowsPluginUI)
  861. };
  862. LRESULT CALLBACK wndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
  863. {
  864. switch (message)
  865. {
  866. case WM_CLOSE:
  867. PostMessage(hwnd, CARLA_LOCAL_CLOSE_MSG, wParam, lParam);
  868. return 0;
  869. #if 0
  870. case WM_CREATE:
  871. PostMessage(hwnd, WM_SHOWWINDOW, TRUE, 0);
  872. return 0;
  873. case WM_DESTROY:
  874. return 0;
  875. #endif
  876. default:
  877. if (WindowsPluginUI* const ui = (WindowsPluginUI*)GetWindowLongPtr(hwnd, GWLP_USERDATA))
  878. return ui->checkAndHandleMessage(hwnd, message, wParam, lParam);
  879. return DefWindowProcA(hwnd, message, wParam, lParam);
  880. }
  881. }
  882. #endif // CARLA_OS_WIN
  883. // -----------------------------------------------------
  884. #ifndef BUILD_BRIDGE_ALTERNATIVE_ARCH
  885. bool CarlaPluginUI::tryTransientWinIdMatch(const uintptr_t pid, const char* const uiTitle, const uintptr_t winId, const bool centerUI)
  886. {
  887. CARLA_SAFE_ASSERT_RETURN(uiTitle != nullptr && uiTitle[0] != '\0', true);
  888. CARLA_SAFE_ASSERT_RETURN(winId != 0, true);
  889. #if defined(HAVE_X11)
  890. struct ScopedDisplay {
  891. Display* display;
  892. ScopedDisplay() : display(XOpenDisplay(nullptr)) {}
  893. ~ScopedDisplay() { if (display!=nullptr) XCloseDisplay(display); }
  894. // c++ compat stuff
  895. CARLA_PREVENT_HEAP_ALLOCATION
  896. CARLA_DECLARE_NON_COPY_CLASS(ScopedDisplay)
  897. };
  898. struct ScopedFreeData {
  899. union {
  900. char* data;
  901. uchar* udata;
  902. };
  903. ScopedFreeData(char* d) : data(d) {}
  904. ScopedFreeData(uchar* d) : udata(d) {}
  905. ~ScopedFreeData() { XFree(data); }
  906. // c++ compat stuff
  907. CARLA_PREVENT_HEAP_ALLOCATION
  908. CARLA_DECLARE_NON_COPY_CLASS(ScopedFreeData)
  909. };
  910. const ScopedDisplay sd;
  911. CARLA_SAFE_ASSERT_RETURN(sd.display != nullptr, true);
  912. const Window rootWindow(DefaultRootWindow(sd.display));
  913. const Atom _ncl = XInternAtom(sd.display, "_NET_CLIENT_LIST" , False);
  914. const Atom _nwn = XInternAtom(sd.display, "_NET_WM_NAME", False);
  915. const Atom _nwp = XInternAtom(sd.display, "_NET_WM_PID", False);
  916. const Atom utf8 = XInternAtom(sd.display, "UTF8_STRING", True);
  917. Atom actualType;
  918. int actualFormat;
  919. ulong numWindows, bytesAfter;
  920. uchar* data = nullptr;
  921. int status = XGetWindowProperty(sd.display, rootWindow, _ncl, 0L, (~0L), False, AnyPropertyType, &actualType, &actualFormat, &numWindows, &bytesAfter, &data);
  922. CARLA_SAFE_ASSERT_RETURN(data != nullptr, true);
  923. const ScopedFreeData sfd(data);
  924. CARLA_SAFE_ASSERT_RETURN(status == Success, true);
  925. CARLA_SAFE_ASSERT_RETURN(actualFormat == 32, true);
  926. CARLA_SAFE_ASSERT_RETURN(numWindows != 0, true);
  927. Window* windows = (Window*)data;
  928. Window lastGoodWindowPID = 0, lastGoodWindowNameSimple = 0, lastGoodWindowNameUTF8 = 0;
  929. for (ulong i = 0; i < numWindows; i++)
  930. {
  931. const Window window(windows[i]);
  932. CARLA_SAFE_ASSERT_CONTINUE(window != 0);
  933. // ------------------------------------------------
  934. // try using pid
  935. if (pid != 0)
  936. {
  937. ulong pidSize;
  938. uchar* pidData = nullptr;
  939. status = XGetWindowProperty(sd.display, window, _nwp, 0L, (~0L), False, XA_CARDINAL, &actualType, &actualFormat, &pidSize, &bytesAfter, &pidData);
  940. if (pidData != nullptr)
  941. {
  942. const ScopedFreeData sfd2(pidData);
  943. CARLA_SAFE_ASSERT_CONTINUE(status == Success);
  944. CARLA_SAFE_ASSERT_CONTINUE(pidSize != 0);
  945. if (*(ulong*)pidData == static_cast<ulong>(pid))
  946. lastGoodWindowPID = window;
  947. }
  948. }
  949. // ------------------------------------------------
  950. // try using name (UTF-8)
  951. ulong nameSize;
  952. uchar* nameData = nullptr;
  953. status = XGetWindowProperty(sd.display, window, _nwn, 0L, (~0L), False, utf8, &actualType, &actualFormat, &nameSize, &bytesAfter, &nameData);
  954. if (nameData != nullptr)
  955. {
  956. const ScopedFreeData sfd2(nameData);
  957. CARLA_SAFE_ASSERT_CONTINUE(status == Success);
  958. if (nameSize != 0 && std::strstr((const char*)nameData, uiTitle) != nullptr)
  959. lastGoodWindowNameUTF8 = window;
  960. }
  961. // ------------------------------------------------
  962. // try using name (simple)
  963. char* wmName = nullptr;
  964. status = XFetchName(sd.display, window, &wmName);
  965. if (wmName != nullptr)
  966. {
  967. const ScopedFreeData sfd2(wmName);
  968. CARLA_SAFE_ASSERT_CONTINUE(status != 0);
  969. if (std::strstr(wmName, uiTitle) != nullptr)
  970. lastGoodWindowNameSimple = window;
  971. }
  972. }
  973. if (lastGoodWindowPID == 0 && lastGoodWindowNameSimple == 0 && lastGoodWindowNameUTF8 == 0)
  974. return false;
  975. Window windowToMap;
  976. if (lastGoodWindowPID != 0)
  977. {
  978. if (lastGoodWindowPID == lastGoodWindowNameSimple && lastGoodWindowPID == lastGoodWindowNameUTF8)
  979. {
  980. carla_stdout("Match found using pid, simple and UTF-8 name all at once, nice!");
  981. windowToMap = lastGoodWindowPID;
  982. }
  983. else if (lastGoodWindowPID == lastGoodWindowNameUTF8)
  984. {
  985. carla_stdout("Match found using pid and UTF-8 name");
  986. windowToMap = lastGoodWindowPID;
  987. }
  988. else if (lastGoodWindowPID == lastGoodWindowNameSimple)
  989. {
  990. carla_stdout("Match found using pid and simple name");
  991. windowToMap = lastGoodWindowPID;
  992. }
  993. else if (lastGoodWindowNameUTF8 != 0)
  994. {
  995. if (lastGoodWindowNameUTF8 == lastGoodWindowNameSimple)
  996. {
  997. carla_stdout("Match found using simple and UTF-8 name (ignoring pid)");
  998. windowToMap = lastGoodWindowNameUTF8;
  999. }
  1000. else
  1001. {
  1002. carla_stdout("Match found using UTF-8 name (ignoring pid)");
  1003. windowToMap = lastGoodWindowNameUTF8;
  1004. }
  1005. }
  1006. else
  1007. {
  1008. carla_stdout("Match found using pid");
  1009. windowToMap = lastGoodWindowPID;
  1010. }
  1011. }
  1012. else if (lastGoodWindowNameUTF8 != 0)
  1013. {
  1014. if (lastGoodWindowNameUTF8 == lastGoodWindowNameSimple)
  1015. {
  1016. carla_stdout("Match found using simple and UTF-8 name");
  1017. windowToMap = lastGoodWindowNameUTF8;
  1018. }
  1019. else
  1020. {
  1021. carla_stdout("Match found using UTF-8 name");
  1022. windowToMap = lastGoodWindowNameUTF8;
  1023. }
  1024. }
  1025. else
  1026. {
  1027. carla_stdout("Match found using simple name");
  1028. windowToMap = lastGoodWindowNameSimple;
  1029. }
  1030. const Atom _nwt = XInternAtom(sd.display ,"_NET_WM_STATE", False);
  1031. const Atom _nws[2] = {
  1032. XInternAtom(sd.display, "_NET_WM_STATE_SKIP_TASKBAR", False),
  1033. XInternAtom(sd.display, "_NET_WM_STATE_SKIP_PAGER", False)
  1034. };
  1035. XChangeProperty(sd.display, windowToMap, _nwt, XA_ATOM, 32, PropModeAppend, (const uchar*)_nws, 2);
  1036. const Atom _nwi = XInternAtom(sd.display, "_NET_WM_ICON", False);
  1037. XChangeProperty(sd.display, windowToMap, _nwi, XA_CARDINAL, 32, PropModeReplace, (const uchar*)sCarlaX11Icon, sCarlaX11IconSize);
  1038. const Window hostWinId((Window)winId);
  1039. XSetTransientForHint(sd.display, windowToMap, hostWinId);
  1040. if (centerUI && false /* moving the window after being shown isn't pretty... */)
  1041. {
  1042. int hostX, hostY, pluginX, pluginY;
  1043. uint hostWidth, hostHeight, pluginWidth, pluginHeight, border, depth;
  1044. Window retWindow;
  1045. if (XGetGeometry(sd.display, hostWinId, &retWindow, &hostX, &hostY, &hostWidth, &hostHeight, &border, &depth) != 0 &&
  1046. XGetGeometry(sd.display, windowToMap, &retWindow, &pluginX, &pluginY, &pluginWidth, &pluginHeight, &border, &depth) != 0)
  1047. {
  1048. if (XTranslateCoordinates(sd.display, hostWinId, rootWindow, hostX, hostY, &hostX, &hostY, &retWindow) == True &&
  1049. XTranslateCoordinates(sd.display, windowToMap, rootWindow, pluginX, pluginY, &pluginX, &pluginY, &retWindow) == True)
  1050. {
  1051. const int newX = hostX + int(hostWidth/2 - pluginWidth/2);
  1052. const int newY = hostY + int(hostHeight/2 - pluginHeight/2);
  1053. XMoveWindow(sd.display, windowToMap, newX, newY);
  1054. }
  1055. }
  1056. }
  1057. // focusing the host UI and then the plugin UI forces the WM to repaint the plugin window icon
  1058. XRaiseWindow(sd.display, hostWinId);
  1059. XSetInputFocus(sd.display, hostWinId, RevertToPointerRoot, CurrentTime);
  1060. XRaiseWindow(sd.display, windowToMap);
  1061. XSetInputFocus(sd.display, windowToMap, RevertToPointerRoot, CurrentTime);
  1062. XFlush(sd.display);
  1063. return true;
  1064. #endif
  1065. #ifdef CARLA_OS_MAC
  1066. uint const hints = kCGWindowListOptionOnScreenOnly|kCGWindowListExcludeDesktopElements;
  1067. CFArrayRef const windowListRef = CGWindowListCopyWindowInfo(hints, kCGNullWindowID);
  1068. const NSArray* const windowList = (const NSArray*)windowListRef;
  1069. int windowToMap, windowWithPID = 0, windowWithNameAndPID = 0;
  1070. const NSDictionary* entry;
  1071. for (entry in windowList)
  1072. {
  1073. // FIXME: is this needed? is old version safe?
  1074. #if defined (MAC_OS_X_VERSION_10_6) && MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_6
  1075. if ([entry[(id)kCGWindowSharingState] intValue] == kCGWindowSharingNone)
  1076. continue;
  1077. NSString* const windowName = entry[(id)kCGWindowName];
  1078. int const windowNumber = [entry[(id)kCGWindowNumber] intValue];
  1079. uintptr_t const windowPID = [entry[(id)kCGWindowOwnerPID] intValue];
  1080. #else
  1081. if ([[entry objectForKey:(id)kCGWindowSharingState] intValue] == kCGWindowSharingNone)
  1082. continue;
  1083. NSString* const windowName = [entry objectForKey:(id)kCGWindowName];
  1084. int const windowNumber = [[entry objectForKey:(id)kCGWindowNumber] intValue];
  1085. uintptr_t const windowPID = [[entry objectForKey:(id)kCGWindowOwnerPID] intValue];
  1086. #endif
  1087. if (windowPID != pid)
  1088. continue;
  1089. windowWithPID = windowNumber;
  1090. if (windowName != nullptr && std::strcmp([windowName UTF8String], uiTitle) == 0)
  1091. windowWithNameAndPID = windowNumber;
  1092. }
  1093. CFRelease(windowListRef);
  1094. if (windowWithNameAndPID != 0)
  1095. {
  1096. carla_stdout("Match found using pid and name");
  1097. windowToMap = windowWithNameAndPID;
  1098. }
  1099. else if (windowWithPID != 0)
  1100. {
  1101. carla_stdout("Match found using pid");
  1102. windowToMap = windowWithPID;
  1103. }
  1104. else
  1105. {
  1106. return false;
  1107. }
  1108. NSWindow* const parentWindow = [NSApp windowWithWindowNumber:winId];
  1109. CARLA_SAFE_ASSERT_RETURN(parentWindow != nullptr, false);
  1110. [parentWindow orderWindow:NSWindowBelow
  1111. relativeTo:windowToMap];
  1112. return true;
  1113. #endif
  1114. #ifdef CARLA_OS_WIN
  1115. if (HWND const childWindow = FindWindowA(nullptr, uiTitle))
  1116. {
  1117. HWND const parentWindow = (HWND)winId;
  1118. SetWindowLongPtr(childWindow, GWLP_HWNDPARENT, (LONG_PTR)parentWindow);
  1119. if (centerUI)
  1120. {
  1121. RECT rectChild, rectParent;
  1122. if (GetWindowRect(childWindow, &rectChild) && GetWindowRect(parentWindow, &rectParent))
  1123. {
  1124. SetWindowPos(childWindow, parentWindow,
  1125. rectParent.left + (rectChild.right-rectChild.left)/2,
  1126. rectParent.top + (rectChild.bottom-rectChild.top)/2,
  1127. 0, 0, SWP_NOSIZE);
  1128. }
  1129. }
  1130. carla_stdout("Match found using window name");
  1131. return true;
  1132. }
  1133. return false;
  1134. #endif
  1135. // fallback, may be unused
  1136. return true;
  1137. (void)pid; (void)centerUI;
  1138. }
  1139. #endif // BUILD_BRIDGE_ALTERNATIVE_ARCH
  1140. // -----------------------------------------------------
  1141. #ifdef HAVE_X11
  1142. CarlaPluginUI* CarlaPluginUI::newX11(Callback* const cb,
  1143. const uintptr_t parentId,
  1144. const bool isStandalone,
  1145. const bool isResizable,
  1146. const bool isLV2)
  1147. {
  1148. return new X11PluginUI(cb, parentId, isStandalone, isResizable, isLV2);
  1149. }
  1150. #endif
  1151. #ifdef CARLA_OS_MAC
  1152. CarlaPluginUI* CarlaPluginUI::newCocoa(Callback* const cb,
  1153. const uintptr_t parentId,
  1154. const bool isStandalone,
  1155. const bool isResizable)
  1156. {
  1157. return new CocoaPluginUI(cb, parentId, isStandalone, isResizable);
  1158. }
  1159. #endif
  1160. #ifdef CARLA_OS_WIN
  1161. CarlaPluginUI* CarlaPluginUI::newWindows(Callback* const cb,
  1162. const uintptr_t parentId,
  1163. const bool isStandalone,
  1164. const bool isResizable)
  1165. {
  1166. return new WindowsPluginUI(cb, parentId, isStandalone, isResizable);
  1167. }
  1168. #endif
  1169. // -----------------------------------------------------