Loading...
Searching...
No Matches
InvisibleWindow.hpp
1#pragma once
2
3/* Microscopic platform abstraction to enable showing a
4 * borderless hidden window, necessary for scanning VST plug-ins.
5 *
6 * Based on knowledge from https://github.com/zserge/fenster
7 *
8 * MIT License
9 *
10 * Copyright (c) 2022 Serge Zaitsev
11 *
12 * Permission is hereby granted, free of charge, to any person obtaining a copy
13 * of this software and associated documentation files (the "Software"), to deal
14 * in the Software without restriction, including without limitation the rights
15 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
16 * copies of the Software, and to permit persons to whom the Software is
17 * furnished to do so, subject to the following conditions:
18 *
19 * The above copyright notice and this permission notice shall be included in all
20 * copies or substantial portions of the Software.
21 *
22 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
23 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
24 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
25 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
26 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
27 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
28 * SOFTWARE.
29 */
30// macOS: -framework CoreFoundation
31// X11: -lX11
32// Win32: -luser32 -lgdi32
33
34#if defined(__APPLE__)
35#include <CoreGraphics/CoreGraphics.h>
36#include <objc/NSObjCRuntime.h>
37
38#include <unistd.h>
39
40#include <objc/objc-runtime.h>
41#define msg(r, o, s) ((r(*)(id, SEL))objc_msgSend)(o, sel_getUid(s))
42#define msg1(r, o, s, A, a) ((r(*)(id, SEL, A))objc_msgSend)(o, sel_getUid(s), a)
43#define msg4(r, o, s, A, a, B, b, C, c, D, d) \
44 ((r(*)(id, SEL, A, B, C, D))objc_msgSend)(o, sel_getUid(s), a, b, c, d)
45
46#define cls(x) ((id)objc_getClass(x))
47
48extern id const NSDefaultRunLoopMode;
49extern id const NSApp;
50
51#elif defined(_WIN32)
52#if !defined(WIN32_LEAN_AND_MEAN)
53#define WIN32_LEAN_AND_MEAN
54#endif
55#if !defined(NOMINMAX)
56#define NOMINMAX
57#endif
58#if !defined(UNICODE)
59#define UNICODE 1
60#endif
61#if !defined(_UNICODE)
62#define _UNICODE 1
63#endif
64#include <windows.h>
65#elif __has_include(<X11/Xlib.h>)
66#define _DEFAULT_SOURCE 1
67#include <X11/XKBlib.h>
68#include <X11/Xlib.h>
69#include <X11/keysym.h>
70
71#include <dlfcn.h>
72#include <unistd.h>
73
74#include <ctime>
75#endif
76
77#include <cstdint>
78#include <cstdlib>
79#include <cstring>
80
82{
83#if defined(__APPLE__)
84 id wnd{};
85#elif defined(_WIN32)
86 HWND hwnd{};
87#elif __has_include(<X11/Xlib.h>)
88 void* x11 = dlopen("libX11.so.6", RTLD_LAZY | RTLD_LOCAL);
89 Display* dpy{};
90 Window w{};
91 GC gc{};
92#endif
93
95 {
96#if defined(__APPLE__)
97 auto pool = msg(id, cls("NSAutoreleasePool"), "alloc");
98 msg(void, pool, "init");
99
100 msg(id, cls("NSApplication"), "sharedApplication");
101
102 assert(NSApp);
103 // Prevent activation
104 msg1(
105 void, NSApp, "setActivationPolicy:", NSInteger,
106 2 /* NSApplicationActivationPolicyProhibited */);
107
108 // Create the window
109 wnd = msg4(
110 id, msg(id, cls("NSWindow"), "alloc"),
111 "initWithContentRect:styleMask:backing:defer:", CGRect, CGRectMake(0, 0, 1, 1),
112 NSUInteger, 3, NSUInteger, 2, BOOL, NO);
113 assert(wnd);
114
115 // Make the window transparent
116 msg1(void, wnd, "setOpaque:", NSInteger, NO);
117 id clearColor = msg(id, cls("NSColor"), "clearColor");
118 msg1(void, wnd, "setBackgroundColor:", id, clearColor);
119
120#elif defined(_WIN32)
121 HINSTANCE hInstance = GetModuleHandle(NULL);
122 WNDCLASSEXA wc = {0};
123 wc.cbSize = sizeof(WNDCLASSEX);
124 wc.style = CS_VREDRAW | CS_HREDRAW;
125 struct WndProc
126 {
127 static LRESULT CALLBACK run(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
128 {
129 switch(msg)
130 {
131 case WM_CLOSE:
132 DestroyWindow(hwnd);
133 break;
134 case WM_DESTROY:
135 PostQuitMessage(0);
136 break;
137 default:
138 return DefWindowProc(hwnd, msg, wParam, lParam);
139 }
140 return 0;
141 }
142 };
143 wc.lpfnWndProc = &WndProc::run;
144 wc.hInstance = hInstance;
145 wc.lpszClassName = "window";
146 RegisterClassExA(&wc);
147
148 // Create the window
149 hwnd = CreateWindowExA(
150 WS_EX_TRANSPARENT, "window", "window", WS_POPUP, 0, 0, 0, 0, nullptr, nullptr,
151 hInstance, nullptr);
152
153 if(hwnd)
154 UpdateWindow(hwnd);
155
156#elif __has_include(<X11/Xlib.h>)
157 if(!x11)
158 return;
159
160 if(auto sym = reinterpret_cast<int (*)()>(dlsym(x11, "XInitThreads")))
161 sym();
162
163 decltype(XOpenDisplay)* d_XOpenDisplay
164 = (decltype(XOpenDisplay)*)dlsym(x11, "XOpenDisplay");
165 decltype(XCreateWindow)* d_XCreateWindow
166 = (decltype(XCreateWindow)*)dlsym(x11, "XCreateWindow");
167 decltype(XCreateGC)* d_XCreateGC = (decltype(XCreateGC)*)dlsym(x11, "XCreateGC");
168 decltype(XSelectInput)* d_XSelectInput
169 = (decltype(XSelectInput)*)dlsym(x11, "XSelectInput");
170 decltype(XMapWindow)* d_XMapWindow = (decltype(XMapWindow)*)dlsym(x11, "XMapWindow");
171 decltype(XSync)* d_XSync = (decltype(XSync)*)dlsym(x11, "XSync");
172 decltype(XInternAtom)* d_XInternAtom
173 = (decltype(XInternAtom)*)dlsym(x11, "XInternAtom");
174 decltype(XChangeProperty)* d_XChangeProperty
175 = (decltype(XChangeProperty)*)dlsym(x11, "XChangeProperty");
176
177 dpy = d_XOpenDisplay(NULL);
178 if(!dpy)
179 throw;
180
181 XSetWindowAttributes attrs{};
182 attrs.override_redirect = True;
183
184 w = d_XCreateWindow(
185 dpy, RootWindow(dpy, DefaultScreen(dpy)), 0, 0, 1, 1, 0, CopyFromParent,
186 InputOutput, CopyFromParent, CWOverrideRedirect, &attrs);
187
188 gc = d_XCreateGC(dpy, w, 0, 0);
189 d_XSelectInput(
190 dpy, w,
191 ExposureMask | KeyPressMask | KeyReleaseMask | ButtonPressMask
192 | ButtonReleaseMask | PointerMotionMask);
193 d_XMapWindow(dpy, w);
194 d_XSync(dpy, w);
195
196 // Disable decorations
197 struct
198 {
199 unsigned long flags = 2;
200 unsigned long functions = 0;
201 unsigned long decorations = 0;
202 long inputMode = 0;
203 unsigned long status = 0;
204 } hints{};
205 auto property = d_XInternAtom(dpy, "_MOTIF_WM_HINTS", true);
206 d_XChangeProperty(
207 dpy, w, property, property, 32, PropModeReplace, (unsigned char*)&hints, 5);
208 d_XMapWindow(dpy, w);
209#endif
210 }
211
212 int loop()
213 {
214#if defined(__APPLE__)
215 msg1(void, msg(id, wnd, "contentView"), "setNeedsDisplay:", BOOL, YES);
216 id ev = msg4(
217 id, NSApp, "nextEventMatchingMask:untilDate:inMode:dequeue:", NSUInteger,
218 NSUIntegerMax, id, NULL, id, NSDefaultRunLoopMode, BOOL, YES);
219 if(!ev)
220 return 0;
221 msg1(void, NSApp, "sendEvent:", id, ev);
222 return 0;
223#elif defined(_WIN32)
224 MSG msg;
225 while(PeekMessage(&msg, NULL, 0, 0, PM_REMOVE))
226 {
227 if(msg.message == WM_QUIT)
228 return -1;
229 TranslateMessage(&msg);
230 DispatchMessage(&msg);
231 }
232 InvalidateRect(hwnd, NULL, TRUE);
233
234#elif __has_include(<X11/Xlib.h>)
235 if(x11)
236 {
237 if(decltype(XFlush)* d_XFlush = (decltype(XFlush)*)dlsym(x11, "XFlush"))
238 d_XFlush(dpy);
239 }
240#endif
241 return 0;
242 }
243
245 {
246#if defined(__APPLE__)
247 msg(void, wnd, "close");
248 msg1(void, NSApp, "terminate:", id, NSApp);
249
250#elif defined(_WIN32)
251 // nothing to do
252
253#elif __has_include(<X11/Xlib.h>)
254 if(x11)
255 {
256 if(decltype(XCloseDisplay)* d_XCloseDisplay
257 = (decltype(XCloseDisplay)*)dlsym(x11, "XCloseDisplay"))
258 d_XCloseDisplay(dpy);
259 }
260#endif
261 }
262};
Definition InvisibleWindow.hpp:82