LMMS
Loading...
Searching...
No Matches
CarlaPluginUI.cpp
Go to the documentation of this file.
1/*
2 * Carla Plugin UI
3 * Copyright (C) 2014-2022 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
18#include "CarlaJuceUtils.hpp"
19#include "CarlaPluginUI.hpp"
20
21#ifdef HAVE_X11
22# include <pthread.h>
23# include <sys/types.h>
24# include <X11/Xatom.h>
25# include <X11/Xlib.h>
26# include <X11/Xutil.h>
27# include "CarlaPluginUI_X11Icon.hpp"
28#endif
29
30#ifdef CARLA_OS_MAC
31# include "CarlaMacUtils.hpp"
32# import <Cocoa/Cocoa.h>
33#endif
34
35#ifdef CARLA_OS_WIN
36# include <ctime>
37# include "water/common.hpp"
38#endif
39
40#ifndef CARLA_PLUGIN_UI_CLASS_PREFIX
41# error CARLA_PLUGIN_UI_CLASS_PREFIX undefined
42#endif
43
44// ---------------------------------------------------------------------------------------------------------------------
45// X11
46
47#ifdef HAVE_X11
48static constexpr const uint X11Key_Escape = 9;
49
50typedef void (*EventProcPtr)(XEvent* ev);
51
52// FIXME put all this inside a scoped class
53static bool gErrorTriggered = false;
54# if defined(__GNUC__) && (__GNUC__ >= 5) && ! defined(__clang__)
55# pragma GCC diagnostic push
56# pragma GCC diagnostic ignored "-Wzero-as-null-pointer-constant"
57# endif
58static pthread_mutex_t gErrorMutex = PTHREAD_MUTEX_INITIALIZER;
59# if defined(__GNUC__) && (__GNUC__ >= 5) && ! defined(__clang__)
60# pragma GCC diagnostic pop
61# endif
62
63static int temporaryErrorHandler(Display*, XErrorEvent*)
64{
65 gErrorTriggered = true;
66 return 0;
67}
68
69class X11PluginUI : public CarlaPluginUI
70{
71public:
72 X11PluginUI(Callback* const cb, const uintptr_t parentId,
73 const bool isStandalone, const bool isResizable, const bool canMonitorChildren) noexcept
74 : CarlaPluginUI(cb, isStandalone, isResizable),
75 fDisplay(nullptr),
76 fHostWindow(0),
77 fChildWindow(0),
78 fChildWindowConfigured(false),
79 fChildWindowMonitoring(isResizable || canMonitorChildren),
80 fIsVisible(false),
81 fFirstShow(true),
82 fSetSizeCalledAtLeastOnce(false),
83 fMinimumWidth(0),
84 fMinimumHeight(0),
85 fEventProc(nullptr)
86 {
87 fDisplay = XOpenDisplay(nullptr);
88 CARLA_SAFE_ASSERT_RETURN(fDisplay != nullptr,);
89
90 const int screen = DefaultScreen(fDisplay);
91
92 XSetWindowAttributes attr;
93 carla_zeroStruct(attr);
94
95 attr.event_mask = KeyPressMask|KeyReleaseMask|FocusChangeMask;
96
97 if (fChildWindowMonitoring)
98 attr.event_mask |= StructureNotifyMask|SubstructureNotifyMask;
99
100 fHostWindow = XCreateWindow(fDisplay, RootWindow(fDisplay, screen),
101 0, 0, 300, 300, 0,
102 DefaultDepth(fDisplay, screen),
103 InputOutput,
104 DefaultVisual(fDisplay, screen),
105 CWBorderPixel|CWEventMask, &attr);
106
107 CARLA_SAFE_ASSERT_RETURN(fHostWindow != 0,);
108
109 XGrabKey(fDisplay, X11Key_Escape, AnyModifier, fHostWindow, 1, GrabModeAsync, GrabModeAsync);
110
111 Atom wmDelete = XInternAtom(fDisplay, "WM_DELETE_WINDOW", True);
112 XSetWMProtocols(fDisplay, fHostWindow, &wmDelete, 1);
113
114 const pid_t pid = getpid();
115 const Atom _nwp = XInternAtom(fDisplay, "_NET_WM_PID", False);
116 XChangeProperty(fDisplay, fHostWindow, _nwp, XA_CARDINAL, 32, PropModeReplace, (const uchar*)&pid, 1);
117
118 const Atom _nwi = XInternAtom(fDisplay, "_NET_WM_ICON", False);
119 XChangeProperty(fDisplay, fHostWindow, _nwi, XA_CARDINAL, 32, PropModeReplace, (const uchar*)sCarlaX11Icon, sCarlaX11IconSize);
120
121 const Atom _wt = XInternAtom(fDisplay, "_NET_WM_WINDOW_TYPE", False);
122
123 // Setting the window to both dialog and normal will produce a decorated floating dialog
124 // Order is important: DIALOG needs to come before NORMAL
125 const Atom _wts[2] = {
126 XInternAtom(fDisplay, "_NET_WM_WINDOW_TYPE_DIALOG", False),
127 XInternAtom(fDisplay, "_NET_WM_WINDOW_TYPE_NORMAL", False)
128 };
129 XChangeProperty(fDisplay, fHostWindow, _wt, XA_ATOM, 32, PropModeReplace, (const uchar*)&_wts, 2);
130
131 if (parentId != 0)
132 setTransientWinId(parentId);
133 }
134
135 ~X11PluginUI() override
136 {
137 CARLA_SAFE_ASSERT(! fIsVisible);
138
139 if (fDisplay == nullptr)
140 return;
141
142 if (fIsVisible)
143 {
144 XUnmapWindow(fDisplay, fHostWindow);
145 fIsVisible = false;
146 }
147
148 if (fHostWindow != 0)
149 {
150 XDestroyWindow(fDisplay, fHostWindow);
151 fHostWindow = 0;
152 }
153
154 XCloseDisplay(fDisplay);
155 fDisplay = nullptr;
156 }
157
158 void show() override
159 {
160 CARLA_SAFE_ASSERT_RETURN(fDisplay != nullptr,);
161 CARLA_SAFE_ASSERT_RETURN(fHostWindow != 0,);
162
163 if (fFirstShow)
164 {
165 if (const Window childWindow = getChildWindow())
166 {
167 if (! fSetSizeCalledAtLeastOnce)
168 {
169 int width = 0;
170 int height = 0;
171
172 XWindowAttributes attrs = {};
173
174 pthread_mutex_lock(&gErrorMutex);
175 const XErrorHandler oldErrorHandler = XSetErrorHandler(temporaryErrorHandler);
176 gErrorTriggered = false;
177
178 if (XGetWindowAttributes(fDisplay, childWindow, &attrs))
179 {
180 width = attrs.width;
181 height = attrs.height;
182 }
183
184 XSetErrorHandler(oldErrorHandler);
185 pthread_mutex_unlock(&gErrorMutex);
186
187 if (width == 0 && height == 0)
188 {
189 XSizeHints sizeHints = {};
190
191 if (XGetNormalHints(fDisplay, childWindow, &sizeHints))
192 {
193 if (sizeHints.flags & PSize)
194 {
195 width = sizeHints.width;
196 height = sizeHints.height;
197 }
198 else if (sizeHints.flags & PBaseSize)
199 {
200 width = sizeHints.base_width;
201 height = sizeHints.base_height;
202 }
203 }
204 }
205
206 if (width > 1 && height > 1)
207 setSize(static_cast<uint>(width), static_cast<uint>(height), false, false);
208 }
209
210 const Atom _xevp = XInternAtom(fDisplay, "_XEventProc", False);
211
212 pthread_mutex_lock(&gErrorMutex);
213 const XErrorHandler oldErrorHandler(XSetErrorHandler(temporaryErrorHandler));
214 gErrorTriggered = false;
215
216 Atom actualType;
217 int actualFormat;
218 ulong nitems, bytesAfter;
219 uchar* data = nullptr;
220
221 XGetWindowProperty(fDisplay, childWindow, _xevp, 0, 1, False, AnyPropertyType,
222 &actualType, &actualFormat, &nitems, &bytesAfter, &data);
223
224 XSetErrorHandler(oldErrorHandler);
225 pthread_mutex_unlock(&gErrorMutex);
226
227 if (nitems == 1 && ! gErrorTriggered)
228 {
229 fEventProc = *reinterpret_cast<EventProcPtr*>(data);
230 XMapRaised(fDisplay, childWindow);
231 }
232 }
233 }
234
235 fIsVisible = true;
236 fFirstShow = false;
237
238 XMapRaised(fDisplay, fHostWindow);
239 XSync(fDisplay, False);
240 }
241
242 void hide() override
243 {
244 CARLA_SAFE_ASSERT_RETURN(fDisplay != nullptr,);
245 CARLA_SAFE_ASSERT_RETURN(fHostWindow != 0,);
246
247 fIsVisible = false;
248 XUnmapWindow(fDisplay, fHostWindow);
249 XFlush(fDisplay);
250 }
251
252 void idle() override
253 {
254 // prevent recursion
255 if (fIsIdling) return;
256
257 uint nextChildWidth = 0;
258 uint nextChildHeight = 0;
259
260 uint nextHostWidth = 0;
261 uint nextHostHeight = 0;
262
263 fIsIdling = true;
264
265 for (XEvent event; XPending(fDisplay) > 0;)
266 {
267 XNextEvent(fDisplay, &event);
268
269 if (! fIsVisible)
270 continue;
271
272 char* type = nullptr;
273
274 switch (event.type)
275 {
276 case ConfigureNotify:
277 CARLA_SAFE_ASSERT_CONTINUE(fCallback != nullptr);
278 CARLA_SAFE_ASSERT_CONTINUE(event.xconfigure.width > 0);
279 CARLA_SAFE_ASSERT_CONTINUE(event.xconfigure.height > 0);
280
281 if (event.xconfigure.window == fHostWindow && fHostWindow != 0)
282 {
283 nextHostWidth = static_cast<uint>(event.xconfigure.width);
284 nextHostHeight = static_cast<uint>(event.xconfigure.height);
285 }
286 else if (event.xconfigure.window == fChildWindow && fChildWindow != 0)
287 {
288 nextChildWidth = static_cast<uint>(event.xconfigure.width);
289 nextChildHeight = static_cast<uint>(event.xconfigure.height);
290 }
291 break;
292
293 case ClientMessage:
294 type = XGetAtomName(fDisplay, event.xclient.message_type);
296
297 if (std::strcmp(type, "WM_PROTOCOLS") == 0)
298 {
299 fIsVisible = false;
300 CARLA_SAFE_ASSERT_CONTINUE(fCallback != nullptr);
301 fCallback->handlePluginUIClosed();
302 }
303 break;
304
305 case KeyRelease:
306 if (event.xkey.keycode == X11Key_Escape)
307 {
308 fIsVisible = false;
309 CARLA_SAFE_ASSERT_CONTINUE(fCallback != nullptr);
310 fCallback->handlePluginUIClosed();
311 }
312 break;
313
314 case FocusIn:
315 if (fChildWindow == 0)
316 fChildWindow = getChildWindow();
317
318 if (fChildWindow != 0)
319 {
320 XWindowAttributes wa;
321 carla_zeroStruct(wa);
322
323 if (XGetWindowAttributes(fDisplay, fChildWindow, &wa) && wa.map_state == IsViewable)
324 XSetInputFocus(fDisplay, fChildWindow, RevertToPointerRoot, CurrentTime);
325 }
326 break;
327 }
328
329 if (type != nullptr)
330 XFree(type);
331 else if (fEventProc != nullptr && event.type != FocusIn && event.type != FocusOut)
332 fEventProc(&event);
333 }
334
335 if (nextChildWidth != 0 && nextChildHeight != 0 && fChildWindow != 0)
336 {
337 applyHintsFromChildWindow();
338 XResizeWindow(fDisplay, fHostWindow, nextChildWidth, nextChildHeight);
339 // XFlush(fDisplay);
340 }
341 else if (nextHostWidth != 0 && nextHostHeight != 0)
342 {
343 if (fChildWindow != 0 && ! fChildWindowConfigured)
344 {
345 applyHintsFromChildWindow();
346 fChildWindowConfigured = true;
347 }
348
349 if (fChildWindow != 0)
350 XResizeWindow(fDisplay, fChildWindow, nextHostWidth, nextHostHeight);
351
352 fCallback->handlePluginUIResized(nextHostWidth, nextHostHeight);
353 }
354
355 fIsIdling = false;
356 }
357
358 void focus() override
359 {
360 CARLA_SAFE_ASSERT_RETURN(fDisplay != nullptr,);
361 CARLA_SAFE_ASSERT_RETURN(fHostWindow != 0,);
362
363 XWindowAttributes wa;
364 carla_zeroStruct(wa);
365
366 CARLA_SAFE_ASSERT_RETURN(XGetWindowAttributes(fDisplay, fHostWindow, &wa),);
367
368 if (wa.map_state == IsViewable)
369 {
370 XRaiseWindow(fDisplay, fHostWindow);
371 XSetInputFocus(fDisplay, fHostWindow, RevertToPointerRoot, CurrentTime);
372 XSync(fDisplay, False);
373 }
374 }
375
376 void setMinimumSize(const uint width, const uint height) override
377 {
378 CARLA_SAFE_ASSERT_RETURN(fDisplay != nullptr,);
379 CARLA_SAFE_ASSERT_RETURN(fHostWindow != 0,);
380
381 fMinimumWidth = width;
382 fMinimumHeight = height;
383
384 XSizeHints sizeHints = {};
385 if (XGetNormalHints(fDisplay, fHostWindow, &sizeHints))
386 {
387 sizeHints.flags |= PMinSize;
388 sizeHints.min_width = static_cast<int>(width);
389 sizeHints.min_height = static_cast<int>(height);
390 XSetNormalHints(fDisplay, fHostWindow, &sizeHints);
391 }
392 }
393
394 void setSize(const uint width, const uint height, const bool forceUpdate, const bool resizeChild) override
395 {
396 CARLA_SAFE_ASSERT_RETURN(fDisplay != nullptr,);
397 CARLA_SAFE_ASSERT_RETURN(fHostWindow != 0,);
398
399 fSetSizeCalledAtLeastOnce = true;
400 XResizeWindow(fDisplay, fHostWindow, width, height);
401
402 if (fChildWindow != 0 && resizeChild)
403 XResizeWindow(fDisplay, fChildWindow, width, height);
404
405 if (! fIsResizable)
406 {
407 XSizeHints sizeHints = {};
408 sizeHints.flags = PSize|PMinSize|PMaxSize;
409 sizeHints.width = static_cast<int>(width);
410 sizeHints.height = static_cast<int>(height);
411 sizeHints.min_width = static_cast<int>(width);
412 sizeHints.min_height = static_cast<int>(height);
413 sizeHints.max_width = static_cast<int>(width);
414 sizeHints.max_height = static_cast<int>(height);
415
416 XSetNormalHints(fDisplay, fHostWindow, &sizeHints);
417 }
418
419 if (forceUpdate)
420 XSync(fDisplay, False);
421 }
422
423 void setTitle(const char* const title) override
424 {
425 CARLA_SAFE_ASSERT_RETURN(fDisplay != nullptr,);
426 CARLA_SAFE_ASSERT_RETURN(fHostWindow != 0,);
427
428 XStoreName(fDisplay, fHostWindow, title);
429
430 const Atom _nwn = XInternAtom(fDisplay, "_NET_WM_NAME", False);
431 const Atom utf8 = XInternAtom(fDisplay, "UTF8_STRING", True);
432
433 XChangeProperty(fDisplay, fHostWindow, _nwn, utf8, 8,
434 PropModeReplace,
435 (const uchar*)(title),
436 (int)strlen(title));
437 }
438
439 void setTransientWinId(const uintptr_t winId) override
440 {
441 CARLA_SAFE_ASSERT_RETURN(fDisplay != nullptr,);
442 CARLA_SAFE_ASSERT_RETURN(fHostWindow != 0,);
443
444 XSetTransientForHint(fDisplay, fHostWindow, static_cast<Window>(winId));
445 }
446
447 void setChildWindow(void* const winId) override
448 {
449 CARLA_SAFE_ASSERT_RETURN(winId != nullptr,);
450
451 fChildWindow = (Window)winId;
452 }
453
454 void* getPtr() const noexcept override
455 {
456 return (void*)fHostWindow;
457 }
458
459 void* getDisplay() const noexcept override
460 {
461 return fDisplay;
462 }
463
464protected:
465 void applyHintsFromChildWindow()
466 {
467 pthread_mutex_lock(&gErrorMutex);
468 const XErrorHandler oldErrorHandler = XSetErrorHandler(temporaryErrorHandler);
469 gErrorTriggered = false;
470
471 XSizeHints sizeHints = {};
472 if (XGetNormalHints(fDisplay, fChildWindow, &sizeHints) && !gErrorTriggered)
473 {
474 if (fMinimumWidth != 0 && fMinimumHeight != 0)
475 {
476 sizeHints.flags |= PMinSize;
477 sizeHints.min_width = fMinimumWidth;
478 sizeHints.min_height = fMinimumHeight;
479 }
480
481 XSetNormalHints(fDisplay, fHostWindow, &sizeHints);
482 }
483
484 if (gErrorTriggered)
485 {
486 carla_stdout("Caught errors while accessing child window");
487 fChildWindow = 0;
488 }
489
490 XSetErrorHandler(oldErrorHandler);
491 pthread_mutex_unlock(&gErrorMutex);
492 }
493
494 Window getChildWindow() const
495 {
496 CARLA_SAFE_ASSERT_RETURN(fDisplay != nullptr, 0);
497 CARLA_SAFE_ASSERT_RETURN(fHostWindow != 0, 0);
498
499 Window rootWindow, parentWindow, ret = 0;
500 Window* childWindows = nullptr;
501 uint numChildren = 0;
502
503 XQueryTree(fDisplay, fHostWindow, &rootWindow, &parentWindow, &childWindows, &numChildren);
504
505 if (numChildren > 0 && childWindows != nullptr)
506 {
507 ret = childWindows[0];
508 XFree(childWindows);
509 }
510
511 return ret;
512 }
513
514private:
515 Display* fDisplay;
516 Window fHostWindow;
517 Window fChildWindow;
518 bool fChildWindowConfigured;
519 bool fChildWindowMonitoring;
520 bool fIsVisible;
521 bool fFirstShow;
522 bool fSetSizeCalledAtLeastOnce;
523 uint fMinimumWidth;
524 uint fMinimumHeight;
525 EventProcPtr fEventProc;
526
527 CARLA_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR(X11PluginUI)
528};
529#endif // HAVE_X11
530
531// ---------------------------------------------------------------------------------------------------------------------
532// MacOS / Cocoa
533
534#ifdef CARLA_OS_MAC
535
536#if defined(BUILD_BRIDGE_ALTERNATIVE_ARCH)
537# define CarlaPluginWindow CARLA_JOIN_MACRO3(CarlaPluginWindowBridgedArch, CARLA_BACKEND_NAMESPACE, CARLA_PLUGIN_UI_CLASS_PREFIX)
538# define CarlaPluginWindowDelegate CARLA_JOIN_MACRO3(CarlaPluginWindowDelegateBridgedArch, CARLA_BACKEND_NAMESPACE, CARLA_PLUGIN_UI_CLASS_PREFIX)
539#elif defined(BUILD_BRIDGE)
540# define CarlaPluginWindow CARLA_JOIN_MACRO3(CarlaPluginWindowBridged, CARLA_BACKEND_NAMESPACE, CARLA_PLUGIN_UI_CLASS_PREFIX)
541# define CarlaPluginWindowDelegate CARLA_JOIN_MACRO3(CarlaPluginWindowDelegateBridged, CARLA_BACKEND_NAMESPACE, CARLA_PLUGIN_UI_CLASS_PREFIX)
542#else
543# define CarlaPluginWindow CARLA_JOIN_MACRO3(CarlaPluginWindow, CARLA_BACKEND_NAMESPACE, CARLA_PLUGIN_UI_CLASS_PREFIX)
544# define CarlaPluginWindowDelegate CARLA_JOIN_MACRO3(CarlaPluginWindowDelegate, CARLA_BACKEND_NAMESPACE, CARLA_PLUGIN_UI_CLASS_PREFIX)
545#endif
546
547@interface CarlaPluginWindow : NSWindow
548- (id) initWithContentRect:(NSRect)contentRect
549 styleMask:(unsigned int)aStyle
550 backing:(NSBackingStoreType)bufferingType
551 defer:(BOOL)flag;
552- (BOOL) canBecomeKeyWindow;
553- (BOOL) canBecomeMainWindow;
554@end
555
556@implementation CarlaPluginWindow
557
558- (id)initWithContentRect:(NSRect)contentRect
559 styleMask:(unsigned int)aStyle
560 backing:(NSBackingStoreType)bufferingType
561 defer:(BOOL)flag
562{
563 NSWindow* result = [super initWithContentRect:contentRect
564 styleMask:aStyle
565 backing:bufferingType
566 defer:flag];
567
568 [result setAcceptsMouseMovedEvents:YES];
569
570 return (CarlaPluginWindow*)result;
571
572 // unused
573 (void)flag;
574}
575
576- (BOOL)canBecomeKeyWindow
577{
578 return YES;
579}
580
581- (BOOL)canBecomeMainWindow
582{
583 return NO;
584}
585
586@end
587
588@interface CarlaPluginWindowDelegate : NSObject<NSWindowDelegate>
589{
590 CarlaPluginUI::Callback* callback;
591 CarlaPluginWindow* window;
592}
593- (instancetype)initWithWindowAndCallback:(CarlaPluginWindow*)window
594 callback:(CarlaPluginUI::Callback*)callback2;
595- (BOOL)windowShouldClose:(id)sender;
596- (NSSize)windowWillResize:(NSWindow*)sender toSize:(NSSize)frameSize;
597@end
598
599@implementation CarlaPluginWindowDelegate
600
601- (instancetype)initWithWindowAndCallback:(CarlaPluginWindow*)window2
602 callback:(CarlaPluginUI::Callback*)callback2
603{
604 if ((self = [super init]))
605 {
606 callback = callback2;
607 window = window2;
608 }
609
610 return self;
611}
612
613- (BOOL)windowShouldClose:(id)sender
614{
615 if (callback != nil)
616 callback->handlePluginUIClosed();
617
618 return NO;
619
620 // unused
621 (void)sender;
622}
623
624- (NSSize)windowWillResize:(NSWindow*)sender toSize:(NSSize)frameSize
625{
626 for (NSView* subview in [[window contentView] subviews])
627 {
628 const NSSize minSize = [subview fittingSize];
629 if (frameSize.width < minSize.width)
630 frameSize.width = minSize.width;
631 if (frameSize.height < minSize.height)
632 frameSize.height = minSize.height;
633 break;
634 }
635
636 return frameSize;
637}
638
639/*
640- (void)windowDidResize:(NSWindow*)sender
641{
642 carla_stdout("window did resize %p %f %f", sender, [window frame].size.width, [window frame].size.height);
643
644 const NSSize size = [window frame].size;
645 NSView* const view = [window contentView];
646
647 for (NSView* subview in [view subviews])
648 {
649 [subview setFrameSize:size];
650 break;
651 }
652}
653*/
654
655@end
656
657class CocoaPluginUI : public CarlaPluginUI
658{
659public:
660 CocoaPluginUI(Callback* const callback, const uintptr_t parentId, const bool isStandalone, const bool isResizable) noexcept
661 : CarlaPluginUI(callback, isStandalone, isResizable),
662 fView(nullptr),
663 fParentWindow(nullptr),
664 fWindow(nullptr)
665 {
666 carla_debug("CocoaPluginUI::CocoaPluginUI(%p, " P_UINTPTR, "%s)", callback, parentId, bool2str(isResizable));
667 const CARLA_BACKEND_NAMESPACE::AutoNSAutoreleasePool arp;
668 [NSApplication sharedApplication];
669
670 fView = [[NSView new]retain];
671 CARLA_SAFE_ASSERT_RETURN(fView != nullptr,)
672
673 #ifdef __MAC_10_12
674 uint style = NSWindowStyleMaskClosable | NSWindowStyleMaskTitled;
675 #else
676 uint style = NSClosableWindowMask | NSTitledWindowMask;
677 #endif
678 /*
679 if (isResizable)
680 style |= NSResizableWindowMask;
681 */
682
683 const NSRect frame = NSMakeRect(0, 0, 100, 100);
684
685 fWindow = [[[CarlaPluginWindow alloc]
686 initWithContentRect:frame
687 styleMask:style
688 backing:NSBackingStoreBuffered
689 defer:NO
690 ] retain];
691
692 if (fWindow == nullptr)
693 {
694 [fView release];
695 fView = nullptr;
696 return;
697 }
698
699 ((NSWindow*)fWindow).delegate = [[[CarlaPluginWindowDelegate alloc]
700 initWithWindowAndCallback:fWindow
701 callback:callback] retain];
702
703 /*
704 if (isResizable)
705 {
706 [fView setAutoresizingMask:(NSViewWidthSizable |
707 NSViewHeightSizable |
708 NSViewMinXMargin |
709 NSViewMaxXMargin |
710 NSViewMinYMargin |
711 NSViewMaxYMargin)];
712 [fView setAutoresizesSubviews:YES];
713 }
714 else
715 */
716 {
717 [fView setAutoresizingMask:NSViewNotSizable];
718 [fView setAutoresizesSubviews:NO];
719 [[fWindow standardWindowButton:NSWindowZoomButton] setHidden:YES];
720 }
721
722 [fWindow setContentView:fView];
723 [fWindow makeFirstResponder:fView];
724
725 [fView setHidden:NO];
726
727 if (parentId != 0)
728 setTransientWinId(parentId);
729 }
730
731 ~CocoaPluginUI() override
732 {
733 carla_debug("CocoaPluginUI::~CocoaPluginUI()");
734 if (fView == nullptr)
735 return;
736
737 [fView setHidden:YES];
738 [fView removeFromSuperview];
739 [fWindow close];
740 [fView release];
741 [fWindow release];
742 }
743
744 void show() override
745 {
746 carla_debug("CocoaPluginUI::show()");
747 CARLA_SAFE_ASSERT_RETURN(fView != nullptr,);
748
749 if (fParentWindow != nullptr)
750 {
751 [fParentWindow addChildWindow:fWindow
752 ordered:NSWindowAbove];
753 }
754 else
755 {
756 [fWindow setIsVisible:YES];
757 }
758 }
759
760 void hide() override
761 {
762 carla_debug("CocoaPluginUI::hide()");
763 CARLA_SAFE_ASSERT_RETURN(fView != nullptr,);
764
765 [fWindow setIsVisible:NO];
766
767 if (fParentWindow != nullptr)
768 [fParentWindow removeChildWindow:fWindow];
769 }
770
771 void idle() override
772 {
773 // carla_debug("CocoaPluginUI::idle()");
774
775 for (NSView* subview in [fView subviews])
776 {
777 const NSSize viewSize = [fView frame].size;
778 const NSSize subviewSize = [subview frame].size;
779
780 if (viewSize.width != subviewSize.width || viewSize.height != subviewSize.height)
781 {
782 [fView setFrameSize:subviewSize];
783 [fWindow setContentSize:subviewSize];
784 }
785 break;
786 }
787 }
788
789 void focus() override
790 {
791 carla_debug("CocoaPluginUI::focus()");
792 CARLA_SAFE_ASSERT_RETURN(fWindow != nullptr,);
793
794 [fWindow makeKeyAndOrderFront:fWindow];
795 [fWindow orderFrontRegardless];
796 [NSApp activateIgnoringOtherApps:YES];
797 }
798
799 void setMinimumSize(uint, uint) override
800 {
801 // TODO
802 }
803
804 void setSize(const uint width, const uint height, const bool forceUpdate, const bool resizeChild) override
805 {
806 carla_debug("CocoaPluginUI::setSize(%u, %u, %s)", width, height, bool2str(forceUpdate));
807 CARLA_SAFE_ASSERT_RETURN(fWindow != nullptr,);
808 CARLA_SAFE_ASSERT_RETURN(fView != nullptr,);
809
810 const NSSize size = NSMakeSize(width, height);
811
812 [fView setFrameSize:size];
813 [fWindow setContentSize:size];
814
815 // this is needed for a few plugins
816 if (forceUpdate && resizeChild)
817 {
818 for (NSView* subview in [fView subviews])
819 {
820 [subview setFrame:[fView frame]];
821 break;
822 }
823 }
824
825 /*
826 if (fIsResizable)
827 {
828 [fWindow setContentMinSize:NSMakeSize(1, 1)];
829 [fWindow setContentMaxSize:NSMakeSize(99999, 99999)];
830 }
831 else
832 {
833 [fWindow setContentMinSize:size];
834 [fWindow setContentMaxSize:size];
835 }
836 */
837
838 if (forceUpdate)
839 {
840 // FIXME, not enough
841 [fView setNeedsDisplay:YES];
842 }
843 }
844
845 void setTitle(const char* const title) override
846 {
847 carla_debug("CocoaPluginUI::setTitle(\"%s\")", title);
848 CARLA_SAFE_ASSERT_RETURN(fWindow != nullptr,);
849
850 NSString* titleString = [[NSString alloc]
851 initWithBytes:title
852 length:strlen(title)
853 encoding:NSUTF8StringEncoding];
854
855 [fWindow setTitle:titleString];
856 }
857
858 void setTransientWinId(const uintptr_t winId) override
859 {
860 carla_debug("CocoaPluginUI::setTransientWinId(" P_UINTPTR ")", winId);
861 CARLA_SAFE_ASSERT_RETURN(fWindow != nullptr,);
862
863 NSWindow* const parentWindow = [NSApp windowWithWindowNumber:winId];
864 CARLA_SAFE_ASSERT_RETURN(parentWindow != nullptr,);
865
866 fParentWindow = parentWindow;
867
868 if ([fWindow isVisible])
869 [fParentWindow addChildWindow:fWindow
870 ordered:NSWindowAbove];
871 }
872
873 void setChildWindow(void* const childWindow) override
874 {
875 carla_debug("CocoaPluginUI::setChildWindow(%p)", childWindow);
876 CARLA_SAFE_ASSERT_RETURN(childWindow != nullptr,);
877
878 NSView* const view = (NSView*)childWindow;
879 const NSRect frame = [view frame];
880
881 [fWindow setContentSize:frame.size];
882 [fView setFrame:frame];
883 [fView setNeedsDisplay:YES];
884 }
885
886 void* getPtr() const noexcept override
887 {
888 carla_debug("CocoaPluginUI::getPtr()");
889 return (void*)fView;
890 }
891
892 void* getDisplay() const noexcept
893 {
894 carla_debug("CocoaPluginUI::getDisplay()");
895 return (void*)fWindow;
896 }
897
898private:
899 NSView* fView;
900 NSWindow* fParentWindow;
901 CarlaPluginWindow* fWindow;
902
903 CARLA_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR(CocoaPluginUI)
904};
905
906#endif // CARLA_OS_MAC
907
908// ---------------------------------------------------------------------------------------------------------------------
909// Windows
910
911#ifdef CARLA_OS_WIN
912
913#define CARLA_LOCAL_CLOSE_MSG (WM_USER + 50)
914
915static LRESULT CALLBACK wndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam);
916
917class WindowsPluginUI : public CarlaPluginUI
918{
919public:
920 WindowsPluginUI(Callback* const cb, const uintptr_t parentId, const bool isStandalone, const bool isResizable) noexcept
921 : CarlaPluginUI(cb, isStandalone, isResizable),
922 fWindow(nullptr),
923 fChildWindow(nullptr),
924 fParentWindow(nullptr),
925 fIsVisible(false),
926 fFirstShow(true)
927 {
928 // FIXME
929 static int wc_count = 0;
930 char classNameBuf[32];
931 std::srand((std::time(nullptr)));
932 std::snprintf(classNameBuf, 32, "CarlaWin-%d-%d", ++wc_count, std::rand());
933 classNameBuf[31] = '\0';
934
935 const HINSTANCE hInstance = water::getCurrentModuleInstanceHandle();
936
937 carla_zeroStruct(fWindowClass);
938 fWindowClass.style = CS_OWNDC;
939 fWindowClass.lpfnWndProc = wndProc;
940 fWindowClass.hInstance = hInstance;
941 fWindowClass.hIcon = LoadIcon(hInstance, IDI_APPLICATION);
942 fWindowClass.hCursor = LoadCursor(hInstance, IDC_ARROW);
943 fWindowClass.lpszClassName = strdup(classNameBuf);
944
945 if (!RegisterClassA(&fWindowClass)) {
946 free((void*)fWindowClass.lpszClassName);
947 return;
948 }
949
950 int winFlags = WS_POPUPWINDOW | WS_CAPTION;
951
952 if (isResizable)
953 winFlags |= WS_SIZEBOX;
954
955#ifdef BUILDING_CARLA_FOR_WINE
956 const uint winType = WS_EX_DLGMODALFRAME;
957 const HWND parent = nullptr;
958#else
959 const uint winType = WS_EX_TOOLWINDOW;
960 const HWND parent = (HWND)parentId;
961#endif
962
963 fWindow = CreateWindowExA(winType,
964 classNameBuf, "Carla Plugin UI", winFlags,
965 CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT,
966 parent, nullptr,
967 hInstance, nullptr);
968
969 if (fWindow == nullptr)
970 {
971 const DWORD errorCode = ::GetLastError();
972 carla_stderr2("CreateWindowEx failed with error code 0x%x, class name was '%s'",
973 errorCode, fWindowClass.lpszClassName);
974 UnregisterClassA(fWindowClass.lpszClassName, nullptr);
975 free((void*)fWindowClass.lpszClassName);
976 return;
977 }
978
979 SetWindowLongPtr(fWindow, GWLP_USERDATA, (LONG_PTR)this);
980
981#ifndef BUILDING_CARLA_FOR_WINE
982 if (parentId != 0)
983 setTransientWinId(parentId);
984#endif
985 return;
986
987 // maybe unused
988 (void)parentId;
989 }
990
991 ~WindowsPluginUI() override
992 {
993 CARLA_SAFE_ASSERT(! fIsVisible);
994
995 if (fWindow != 0)
996 {
997 if (fIsVisible)
998 ShowWindow(fWindow, SW_HIDE);
999
1000 DestroyWindow(fWindow);
1001 fWindow = 0;
1002 }
1003
1004 // FIXME
1005 UnregisterClassA(fWindowClass.lpszClassName, nullptr);
1006 free((void*)fWindowClass.lpszClassName);
1007 }
1008
1009 void show() override
1010 {
1011 CARLA_SAFE_ASSERT_RETURN(fWindow != nullptr,);
1012
1013 if (fFirstShow)
1014 {
1015 fFirstShow = false;
1016 RECT rectChild, rectParent;
1017
1018 if (fChildWindow != nullptr && GetWindowRect(fChildWindow, &rectChild))
1019 setSize(rectChild.right - rectChild.left, rectChild.bottom - rectChild.top, false, false);
1020
1021 if (fParentWindow != nullptr &&
1022 GetWindowRect(fWindow, &rectChild) &&
1023 GetWindowRect(fParentWindow, &rectParent))
1024 {
1025 SetWindowPos(fWindow, fParentWindow,
1026 rectParent.left + (rectChild.right-rectChild.left)/2,
1027 rectParent.top + (rectChild.bottom-rectChild.top)/2,
1029 }
1030 else
1031 {
1032 ShowWindow(fWindow, SW_SHOWNORMAL);
1033 }
1034 }
1035 else
1036 {
1037 ShowWindow(fWindow, SW_RESTORE);
1038 }
1039
1040 fIsVisible = true;
1041 UpdateWindow(fWindow);
1042 }
1043
1044 void hide() override
1045 {
1046 CARLA_SAFE_ASSERT_RETURN(fWindow != nullptr,);
1047
1048 ShowWindow(fWindow, SW_HIDE);
1049 fIsVisible = false;
1050 UpdateWindow(fWindow);
1051 }
1052
1053 void idle() override
1054 {
1055 if (fIsIdling || fWindow == nullptr)
1056 return;
1057
1058 MSG msg;
1059 fIsIdling = true;
1060
1061 while (::PeekMessage(&msg, fWindow, 0, 0, PM_REMOVE))
1062 {
1063 switch (msg.message)
1064 {
1065 case WM_QUIT:
1066 case CARLA_LOCAL_CLOSE_MSG:
1067 fIsVisible = false;
1068 CARLA_SAFE_ASSERT_BREAK(fCallback != nullptr);
1069 fCallback->handlePluginUIClosed();
1070 break;
1071 }
1072
1073 DispatchMessageA(&msg);
1074 }
1075
1076 fIsIdling = false;
1077 }
1078
1079 LRESULT checkAndHandleMessage(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
1080 {
1081 if (fWindow == hwnd)
1082 {
1083 switch (message)
1084 {
1085 case WM_SIZE:
1086 if (fChildWindow != nullptr)
1087 {
1088 RECT rect;
1089 GetClientRect(fWindow, &rect);
1090 SetWindowPos(fChildWindow, 0, 0, 0, rect.right, rect.bottom,
1091 SWP_NOACTIVATE|SWP_NOMOVE|SWP_NOOWNERZORDER|SWP_NOZORDER);
1092 }
1093 break;
1094 case WM_QUIT:
1095 case CARLA_LOCAL_CLOSE_MSG:
1096 fIsVisible = false;
1097 CARLA_SAFE_ASSERT_BREAK(fCallback != nullptr);
1098 fCallback->handlePluginUIClosed();
1099 break;
1100 }
1101 }
1102
1103 return DefWindowProcA(hwnd, message, wParam, lParam);
1104 }
1105
1106 void focus() override
1107 {
1108 CARLA_SAFE_ASSERT_RETURN(fWindow != nullptr,);
1109
1110 SetForegroundWindow(fWindow);
1111 SetActiveWindow(fWindow);
1112 SetFocus(fWindow);
1113 }
1114
1115 void setMinimumSize(uint, uint) override
1116 {
1117 // TODO
1118 }
1119
1120 void setSize(const uint width, const uint height, const bool forceUpdate, bool) override
1121 {
1122 CARLA_SAFE_ASSERT_RETURN(fWindow != nullptr,);
1123
1124 const int winFlags = WS_POPUPWINDOW | WS_CAPTION | (fIsResizable ? WS_SIZEBOX : 0x0);
1125 RECT wr = { 0, 0, static_cast<long>(width), static_cast<long>(height) };
1126 AdjustWindowRectEx(&wr, winFlags, FALSE, WS_EX_TOPMOST);
1127
1128 SetWindowPos(fWindow, 0, 0, 0, wr.right-wr.left, wr.bottom-wr.top,
1129 SWP_NOACTIVATE|SWP_NOMOVE|SWP_NOOWNERZORDER|SWP_NOZORDER);
1130
1131 if (forceUpdate)
1132 UpdateWindow(fWindow);
1133 }
1134
1135 void setTitle(const char* const title) override
1136 {
1137 CARLA_SAFE_ASSERT_RETURN(fWindow != nullptr,);
1138
1139 SetWindowTextA(fWindow, title);
1140 }
1141
1142 void setTransientWinId(const uintptr_t winId) override
1143 {
1144 CARLA_SAFE_ASSERT_RETURN(fWindow != nullptr,);
1145
1146 fParentWindow = (HWND)winId;
1147 SetWindowLongPtr(fWindow, GWLP_HWNDPARENT, (LONG_PTR)winId);
1148 }
1149
1150 void setChildWindow(void* const winId) override
1151 {
1152 CARLA_SAFE_ASSERT_RETURN(winId != nullptr,);
1153
1154 fChildWindow = (HWND)winId;
1155 }
1156
1157 void* getPtr() const noexcept override
1158 {
1159 return (void*)fWindow;
1160 }
1161
1162 void* getDisplay() const noexcept
1163 {
1164 return nullptr;
1165 }
1166
1167private:
1168 HWND fWindow;
1169 HWND fChildWindow;
1170 HWND fParentWindow;
1171 WNDCLASSA fWindowClass;
1172
1173 bool fIsVisible;
1174 bool fFirstShow;
1175
1176 CARLA_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR(WindowsPluginUI)
1177};
1178
1180{
1181 switch (message)
1182 {
1183 case WM_CLOSE:
1184 PostMessage(hwnd, CARLA_LOCAL_CLOSE_MSG, wParam, lParam);
1185 return 0;
1186
1187#if 0
1188 case WM_CREATE:
1190 return 0;
1191
1192 case WM_DESTROY:
1193 return 0;
1194#endif
1195
1196 default:
1197 if (WindowsPluginUI* const ui = (WindowsPluginUI*)GetWindowLongPtr(hwnd, GWLP_USERDATA))
1198 return ui->checkAndHandleMessage(hwnd, message, wParam, lParam);
1199 return DefWindowProcA(hwnd, message, wParam, lParam);
1200 }
1201}
1202#endif // CARLA_OS_WIN
1203
1204// -----------------------------------------------------
1205
1206#ifndef BUILD_BRIDGE_ALTERNATIVE_ARCH
1207bool CarlaPluginUI::tryTransientWinIdMatch(const uintptr_t pid, const char* const uiTitle, const uintptr_t winId, const bool centerUI)
1208{
1209 CARLA_SAFE_ASSERT_RETURN(uiTitle != nullptr && uiTitle[0] != '\0', true);
1210 CARLA_SAFE_ASSERT_RETURN(winId != 0, true);
1211
1212#if defined(HAVE_X11)
1213 struct ScopedDisplay {
1214 Display* display;
1215 ScopedDisplay() : display(XOpenDisplay(nullptr)) {}
1216 ~ScopedDisplay() { if (display!=nullptr) XCloseDisplay(display); }
1217 // c++ compat stuff
1219 CARLA_DECLARE_NON_COPYABLE(ScopedDisplay)
1220 };
1221 struct ScopedFreeData {
1222 union {
1223 char* data;
1224 uchar* udata;
1225 };
1226 ScopedFreeData(char* d) : data(d) {}
1227 ScopedFreeData(uchar* d) : udata(d) {}
1228 ~ScopedFreeData() { XFree(data); }
1229 // c++ compat stuff
1231 CARLA_DECLARE_NON_COPYABLE(ScopedFreeData)
1232 };
1233
1234 const ScopedDisplay sd;
1235 CARLA_SAFE_ASSERT_RETURN(sd.display != nullptr, true);
1236
1237 const Window rootWindow(DefaultRootWindow(sd.display));
1238
1239 const Atom _ncl = XInternAtom(sd.display, "_NET_CLIENT_LIST" , False);
1240 const Atom _nwn = XInternAtom(sd.display, "_NET_WM_NAME", False);
1241 const Atom _nwp = XInternAtom(sd.display, "_NET_WM_PID", False);
1242 const Atom utf8 = XInternAtom(sd.display, "UTF8_STRING", True);
1243
1244 Atom actualType;
1245 int actualFormat;
1246 ulong numWindows, bytesAfter;
1247 uchar* data = nullptr;
1248
1249 int status = XGetWindowProperty(sd.display, rootWindow, _ncl, 0L, (~0L), False, AnyPropertyType, &actualType, &actualFormat, &numWindows, &bytesAfter, &data);
1250
1251 CARLA_SAFE_ASSERT_RETURN(data != nullptr, true);
1252 const ScopedFreeData sfd(data);
1253
1254 CARLA_SAFE_ASSERT_RETURN(status == Success, true);
1255 CARLA_SAFE_ASSERT_RETURN(actualFormat == 32, true);
1256 CARLA_SAFE_ASSERT_RETURN(numWindows != 0, true);
1257
1258 Window* windows = (Window*)data;
1259 Window lastGoodWindowPID = 0, lastGoodWindowNameSimple = 0, lastGoodWindowNameUTF8 = 0;
1260
1261 for (ulong i = 0; i < numWindows; i++)
1262 {
1263 const Window window(windows[i]);
1264 CARLA_SAFE_ASSERT_CONTINUE(window != 0);
1265
1266 // ------------------------------------------------
1267 // try using pid
1268
1269 if (pid != 0)
1270 {
1271 ulong pidSize;
1272 uchar* pidData = nullptr;
1273
1274 status = XGetWindowProperty(sd.display, window, _nwp, 0L, (~0L), False, XA_CARDINAL, &actualType, &actualFormat, &pidSize, &bytesAfter, &pidData);
1275
1276 if (pidData != nullptr)
1277 {
1278 const ScopedFreeData sfd2(pidData);
1279 CARLA_SAFE_ASSERT_CONTINUE(status == Success);
1280 CARLA_SAFE_ASSERT_CONTINUE(pidSize != 0);
1281
1282 if (*(ulong*)pidData == static_cast<ulong>(pid))
1283 lastGoodWindowPID = window;
1284 }
1285 }
1286
1287 // ------------------------------------------------
1288 // try using name (UTF-8)
1289
1290 ulong nameSize;
1291 uchar* nameData = nullptr;
1292
1293 status = XGetWindowProperty(sd.display, window, _nwn, 0L, (~0L), False, utf8, &actualType, &actualFormat, &nameSize, &bytesAfter, &nameData);
1294
1295 if (nameData != nullptr)
1296 {
1297 const ScopedFreeData sfd2(nameData);
1298 CARLA_SAFE_ASSERT_CONTINUE(status == Success);
1299
1300 if (nameSize != 0 && std::strstr((const char*)nameData, uiTitle) != nullptr)
1301 lastGoodWindowNameUTF8 = window;
1302 }
1303
1304 // ------------------------------------------------
1305 // try using name (simple)
1306
1307 char* wmName = nullptr;
1308
1309 status = XFetchName(sd.display, window, &wmName);
1310
1311 if (wmName != nullptr)
1312 {
1313 const ScopedFreeData sfd2(wmName);
1314 CARLA_SAFE_ASSERT_CONTINUE(status != 0);
1315
1316 if (std::strstr(wmName, uiTitle) != nullptr)
1317 lastGoodWindowNameSimple = window;
1318 }
1319 }
1320
1321 if (lastGoodWindowPID == 0 && lastGoodWindowNameSimple == 0 && lastGoodWindowNameUTF8 == 0)
1322 return false;
1323
1324 Window windowToMap;
1325
1326 if (lastGoodWindowPID != 0)
1327 {
1328 if (lastGoodWindowPID == lastGoodWindowNameSimple && lastGoodWindowPID == lastGoodWindowNameUTF8)
1329 {
1330 carla_stdout("Match found using pid, simple and UTF-8 name all at once, nice!");
1331 windowToMap = lastGoodWindowPID;
1332 }
1333 else if (lastGoodWindowPID == lastGoodWindowNameUTF8)
1334 {
1335 carla_stdout("Match found using pid and UTF-8 name");
1336 windowToMap = lastGoodWindowPID;
1337 }
1338 else if (lastGoodWindowPID == lastGoodWindowNameSimple)
1339 {
1340 carla_stdout("Match found using pid and simple name");
1341 windowToMap = lastGoodWindowPID;
1342 }
1343 else if (lastGoodWindowNameUTF8 != 0)
1344 {
1345 if (lastGoodWindowNameUTF8 == lastGoodWindowNameSimple)
1346 {
1347 carla_stdout("Match found using simple and UTF-8 name (ignoring pid)");
1348 windowToMap = lastGoodWindowNameUTF8;
1349 }
1350 else
1351 {
1352 carla_stdout("Match found using UTF-8 name (ignoring pid)");
1353 windowToMap = lastGoodWindowNameUTF8;
1354 }
1355 }
1356 else
1357 {
1358 carla_stdout("Match found using pid");
1359 windowToMap = lastGoodWindowPID;
1360 }
1361 }
1362 else if (lastGoodWindowNameUTF8 != 0)
1363 {
1364 if (lastGoodWindowNameUTF8 == lastGoodWindowNameSimple)
1365 {
1366 carla_stdout("Match found using simple and UTF-8 name");
1367 windowToMap = lastGoodWindowNameUTF8;
1368 }
1369 else
1370 {
1371 carla_stdout("Match found using UTF-8 name");
1372 windowToMap = lastGoodWindowNameUTF8;
1373 }
1374 }
1375 else
1376 {
1377 carla_stdout("Match found using simple name");
1378 windowToMap = lastGoodWindowNameSimple;
1379 }
1380
1381 const Atom _nwt = XInternAtom(sd.display ,"_NET_WM_STATE", False);
1382 const Atom _nws[2] = {
1383 XInternAtom(sd.display, "_NET_WM_STATE_SKIP_TASKBAR", False),
1384 XInternAtom(sd.display, "_NET_WM_STATE_SKIP_PAGER", False)
1385 };
1386 XChangeProperty(sd.display, windowToMap, _nwt, XA_ATOM, 32, PropModeAppend, (const uchar*)_nws, 2);
1387
1388 const Atom _nwi = XInternAtom(sd.display, "_NET_WM_ICON", False);
1389 XChangeProperty(sd.display, windowToMap, _nwi, XA_CARDINAL, 32, PropModeReplace, (const uchar*)sCarlaX11Icon, sCarlaX11IconSize);
1390
1391 const Window hostWinId((Window)winId);
1392
1393 XSetTransientForHint(sd.display, windowToMap, hostWinId);
1394
1395 if (centerUI && false /* moving the window after being shown isn't pretty... */)
1396 {
1397 int hostX, hostY, pluginX, pluginY;
1398 uint hostWidth, hostHeight, pluginWidth, pluginHeight, border, depth;
1399 Window retWindow;
1400
1401 if (XGetGeometry(sd.display, hostWinId, &retWindow, &hostX, &hostY, &hostWidth, &hostHeight, &border, &depth) != 0 &&
1402 XGetGeometry(sd.display, windowToMap, &retWindow, &pluginX, &pluginY, &pluginWidth, &pluginHeight, &border, &depth) != 0)
1403 {
1404 if (XTranslateCoordinates(sd.display, hostWinId, rootWindow, hostX, hostY, &hostX, &hostY, &retWindow) == True &&
1405 XTranslateCoordinates(sd.display, windowToMap, rootWindow, pluginX, pluginY, &pluginX, &pluginY, &retWindow) == True)
1406 {
1407 const int newX = hostX + int(hostWidth/2 - pluginWidth/2);
1408 const int newY = hostY + int(hostHeight/2 - pluginHeight/2);
1409
1410 XMoveWindow(sd.display, windowToMap, newX, newY);
1411 }
1412 }
1413 }
1414
1415 // focusing the host UI and then the plugin UI forces the WM to repaint the plugin window icon
1416 XRaiseWindow(sd.display, hostWinId);
1417 XSetInputFocus(sd.display, hostWinId, RevertToPointerRoot, CurrentTime);
1418
1419 XRaiseWindow(sd.display, windowToMap);
1420 XSetInputFocus(sd.display, windowToMap, RevertToPointerRoot, CurrentTime);
1421
1422 XFlush(sd.display);
1423 return true;
1424#endif
1425
1426#ifdef CARLA_OS_MAC
1427 uint const hints = kCGWindowListOptionOnScreenOnly|kCGWindowListExcludeDesktopElements;
1428
1429 CFArrayRef const windowListRef = CGWindowListCopyWindowInfo(hints, kCGNullWindowID);
1430 const NSArray* const windowList = (const NSArray*)windowListRef;
1431
1432 int windowToMap, windowWithPID = 0, windowWithNameAndPID = 0;
1433
1434 const NSDictionary* entry;
1435 for (entry in windowList)
1436 {
1437 // FIXME: is this needed? is old version safe?
1438#if defined (MAC_OS_X_VERSION_10_6) && MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_6
1439 if ([entry[(id)kCGWindowSharingState] intValue] == kCGWindowSharingNone)
1440 continue;
1441
1442 NSString* const windowName = entry[(id)kCGWindowName];
1443 int const windowNumber = [entry[(id)kCGWindowNumber] intValue];
1444 uintptr_t const windowPID = [entry[(id)kCGWindowOwnerPID] intValue];
1445#else
1446 if ([[entry objectForKey:(id)kCGWindowSharingState] intValue] == kCGWindowSharingNone)
1447 continue;
1448
1449 NSString* const windowName = [entry objectForKey:(id)kCGWindowName];
1450 int const windowNumber = [[entry objectForKey:(id)kCGWindowNumber] intValue];
1451 uintptr_t const windowPID = [[entry objectForKey:(id)kCGWindowOwnerPID] intValue];
1452#endif
1453
1454 if (windowPID != pid)
1455 continue;
1456
1457 windowWithPID = windowNumber;
1458
1459 if (windowName != nullptr && std::strcmp([windowName UTF8String], uiTitle) == 0)
1460 windowWithNameAndPID = windowNumber;
1461 }
1462
1463 CFRelease(windowListRef);
1464
1465 if (windowWithNameAndPID != 0)
1466 {
1467 carla_stdout("Match found using pid and name");
1468 windowToMap = windowWithNameAndPID;
1469 }
1470 else if (windowWithPID != 0)
1471 {
1472 carla_stdout("Match found using pid");
1473 windowToMap = windowWithPID;
1474 }
1475 else
1476 {
1477 return false;
1478 }
1479
1480 NSWindow* const parentWindow = [NSApp windowWithWindowNumber:winId];
1481 CARLA_SAFE_ASSERT_RETURN(parentWindow != nullptr, false);
1482
1483 [parentWindow orderWindow:NSWindowBelow
1484 relativeTo:windowToMap];
1485 return true;
1486#endif
1487
1488#ifdef CARLA_OS_WIN
1489 if (HWND const childWindow = FindWindowA(nullptr, uiTitle))
1490 {
1491 HWND const parentWindow = (HWND)winId;
1492 SetWindowLongPtr(childWindow, GWLP_HWNDPARENT, (LONG_PTR)parentWindow);
1493
1494 if (centerUI)
1495 {
1496 RECT rectChild, rectParent;
1497
1498 if (GetWindowRect(childWindow, &rectChild) && GetWindowRect(parentWindow, &rectParent))
1499 {
1500 SetWindowPos(childWindow, parentWindow,
1501 rectParent.left + (rectChild.right-rectChild.left)/2,
1502 rectParent.top + (rectChild.bottom-rectChild.top)/2,
1503 0, 0, SWP_NOSIZE);
1504 }
1505 }
1506
1507 carla_stdout("Match found using window name");
1508 return true;
1509 }
1510
1511 return false;
1512#endif
1513
1514 // fallback, may be unused
1515 return true;
1516 (void)pid; (void)centerUI;
1517}
1518#endif // BUILD_BRIDGE_ALTERNATIVE_ARCH
1519
1520// -----------------------------------------------------
1521
1522#ifdef HAVE_X11
1523CarlaPluginUI* CarlaPluginUI::newX11(Callback* const cb,
1524 const uintptr_t parentId,
1525 const bool isStandalone,
1526 const bool isResizable,
1527 const bool isLV2)
1528{
1529 return new X11PluginUI(cb, parentId, isStandalone, isResizable, isLV2);
1530}
1531#endif
1532
1533#ifdef CARLA_OS_MAC
1534CarlaPluginUI* CarlaPluginUI::newCocoa(Callback* const cb,
1535 const uintptr_t parentId,
1536 const bool isStandalone,
1537 const bool isResizable)
1538{
1539 return new CocoaPluginUI(cb, parentId, isStandalone, isResizable);
1540}
1541#endif
1542
1543#ifdef CARLA_OS_WIN
1544CarlaPluginUI* CarlaPluginUI::newWindows(Callback* const cb,
1545 const uintptr_t parentId,
1546 const bool isStandalone,
1547 const bool isResizable)
1548{
1549 return new WindowsPluginUI(cb, parentId, isStandalone, isResizable);
1550}
1551#endif
1552
1553// -----------------------------------------------------
#define CARLA_SAFE_ASSERT_CONTINUE(cond)
Definition CarlaDefines.h:189
unsigned char uchar
Definition CarlaDefines.h:325
#define CARLA_SAFE_ASSERT_RETURN(cond, ret)
Definition CarlaDefines.h:190
unsigned int uint
Definition CarlaDefines.h:327
#define CARLA_SAFE_ASSERT_BREAK(cond)
Definition CarlaDefines.h:188
unsigned long int ulong
Definition CarlaDefines.h:328
#define CARLA_PREVENT_HEAP_ALLOCATION
Definition CarlaDefines.h:257
#define P_UINTPTR
Definition CarlaDefines.h:139
#define CARLA_SAFE_ASSERT(cond)
Definition CarlaDefines.h:182
#define CARLA_DECLARE_NON_COPYABLE(ClassName)
Definition CarlaDefines.h:242
class MasterUI * ui
Definition Connection.cpp:39
#define noexcept
Definition DistrhoDefines.h:72
#define nullptr
Definition DistrhoDefines.h:75
CAdPlugDatabase::CRecord::RecordType type
Definition adplugdb.cpp:93
static void message(int level, const char *fmt,...)
Definition adplugdb.cpp:120
unsigned d
Definition inflate.c:940
register unsigned i
Definition inflate.c:1575
static ZCONST unsigned border[]
Definition inflate.c:749
static const char * title
Definition pugl.h:1747
static int int height
Definition pugl.h:1594
static int width
Definition pugl.h:1593
static uintptr_t parent
Definition pugl.h:1644
CARLA_PLUGIN_EXPORT int XUnmapWindow(Display *display, Window window)
Definition interposer-x11.cpp:124
CARLA_PLUGIN_EXPORT int XMapRaised(Display *display, Window window)
Definition interposer-x11.cpp:110
JSAMPIMAGE data
Definition jpeglib.h:945
static const SerdStyle style
Definition sratom.c:36
float in
Definition lilv_test.c:1460
const char * msg
Definition missing_descriptor.c:20
@ focus
Definition juce_AccessibilityActions.h:54
@ window
Definition juce_AccessibilityRole.h:63
Definition swell-types.h:231
LONG bottom
Definition swell-types.h:232
LONG left
Definition swell-types.h:232
LONG top
Definition swell-types.h:232
LONG right
Definition swell-types.h:232
#define SW_SHOWNORMAL
#define WS_CAPTION
#define SW_HIDE
#define WM_CLOSE
#define SWP_NOACTIVATE
#define WM_CREATE
#define WM_SIZE
#define SWP_NOMOVE
#define IDC_ARROW
#define SWP_NOSIZE
#define WM_SHOWWINDOW
#define SWP_SHOWWINDOW
#define SW_RESTORE
#define WM_DESTROY
#define SWP_NOZORDER
RECT const char void(* callback)(const char *droppath))) SWELL_API_DEFINE(BOOL
Definition swell-functions.h:1004
#define LoadCursor(a, x)
RECT const char void HWND hwnd
Definition swell-functions.h:1066
#define SetActiveWindow(x)
Definition swell-functions.h:225
bool GetWindowRect(HWND hwnd, RECT *r)
Definition swell-generic-headless.cpp:130
void UpdateWindow(HWND hwnd)
Definition swell-generic-headless.cpp:151
void * HINSTANCE
Definition swell-types.h:212
LONG_PTR LRESULT
Definition swell-types.h:171
unsigned int UINT
Definition swell-types.h:166
LONG_PTR LPARAM
Definition swell-types.h:170
signed char BOOL
Definition swell-types.h:160
intptr_t LONG_PTR
Definition swell-types.h:42
unsigned int DWORD
Definition swell-types.h:164
ULONG_PTR WPARAM
Definition swell-types.h:169
struct HWND__ * HWND
Definition swell-types.h:210
#define CALLBACK
Definition swell-types.h:635
void SetForegroundWindow(HWND hwnd)
Definition swell-wnd-generic.cpp:489
void DestroyWindow(HWND hwnd)
Definition swell-wnd-generic.cpp:449
void SetWindowPos(HWND hwnd, HWND zorder, int x, int y, int cx, int cy, int flags)
Definition swell-wnd-generic.cpp:637
void SetFocus(HWND hwnd)
Definition swell-wnd-generic.cpp:506
BOOL PostMessage(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
Definition swell-wnd-generic.cpp:7561
void GetClientRect(HWND hwnd, RECT *r)
Definition swell-wnd-generic.cpp:621
void ShowWindow(HWND hwnd, int cmd)
Definition swell-wnd-generic.cpp:1034
ZCONST uch * init
Definition extract.c:2392
ulg size
Definition extract.c:2350
int result
Definition process.c:1455
int flag
Definition unix.c:754
typedef int(UZ_EXP MsgFn)()
#define void
Definition unzip.h:396
#define TRUE
Definition unzpriv.h:1295
#define FALSE
Definition unzpriv.h:1298
#define GetWindowLongPtr(a, b)
Definition wdltypes.h:63
#define SetWindowLongPtr(a, b, c)
Definition wdltypes.h:62
#define GWLP_HWNDPARENT
Definition wdltypes.h:58
#define GWLP_USERDATA
Definition win.c:30
LRESULT CALLBACK wndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
Definition win.c:953
#define const
Definition zconf.h:137