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

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