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 39KB

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