1 /**
2 * Copyright: Copyright Auburn Sounds 2015 - 2017.
3 * Copyright Richard Andrew Cattermole 2017.
4 *
5 * License: $(LINK2 http://www.boost.org/LICENSE_1_0.txt, Boost License 1.0)
6 * Authors: Guillaume Piolat
7 */8 moduledplug.window.window;
9 10 importdplug.math.box;
11 12 importdplug.core.nogc;
13 importdplug.graphics.image;
14 15 enumKey16 {
17 space,
18 upArrow,
19 downArrow,
20 leftArrow,
21 rightArrow,
22 digit0,
23 digit1,
24 digit2,
25 digit3,
26 digit4,
27 digit5,
28 digit6,
29 digit7,
30 digit8,
31 digit9,
32 a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p, q, r, s, t, u, v, w, x, y, z,
33 A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P, Q, R, S, T, U, V, W, X, Y, Z,
34 backspace,
35 enter,
36 escape,
37 suppr,
38 unsupported// special value, means "other"39 };
40 41 publicdchargetCharFromKey(Keykey) nothrow @nogc42 {
43 switch(key)
44 {
45 caseKey.backspace: return'\t';
46 caseKey.suppr: return'\x7f';
47 caseKey.digit0: .. caseKey.digit9: returncast(dchar)('0' + (key - Key.digit0));
48 caseKey.a: .. caseKey.z: returncast(dchar)('a' + (key - Key.a));
49 caseKey.A: .. caseKey.Z: returncast(dchar)('A' + (key - Key.A));
50 caseKey.space : return' ';
51 default: return'\0';
52 }
53 }
54 55 enumMouseButton56 {
57 left,
58 right,
59 middle,
60 x1,
61 x262 }
63 64 structMouseState65 {
66 boolleftButtonDown;
67 boolrightButtonDown;
68 boolmiddleButtonDown;
69 boolx1ButtonDown;
70 boolx2ButtonDown;
71 boolctrlPressed;
72 boolshiftPressed;
73 boolaltPressed;
74 }
75 76 enumMouseCursor77 {
78 /// Default cursor79 pointer,
80 81 /// Indicates that the underlying item can be clicked like an hyperlink, and will "jump".82 linkSelect,
83 84 /// Indicates that the underlying item can be clicked and then dragged, in no particular directions.85 /// When an open-hand is available, this is an open-hand cursor.86 move,
87 88 /// Indicates that clicked item can be moved in ether vertical or hozizontal directions.89 /// When an closed-hand is available, this is a closed-hand cursor. 90 drag,
91 92 /// Indicated vertical resize abilities.93 verticalResize,
94 95 /// Indicated horizontal resize abilities.96 horizontalResize,
97 98 /// Indicated diagonalResize resize abilities.99 diagonalResize,
100 101 /// Cursor is hidden102 hidden103 }
104 105 /// Is this window intended as a plug-in window running inside a host,106 /// or a host window itself possibly hosting a plug-in?107 enumWindowUsage108 {
109 /// This window is intended to be for displaying a plugin UI.110 /// Event pumping is done by the host (except in the X11 case where it's111 /// done by an internal thread).112 plugin,
113 114 /// This window is intended to be top-level, for hosting another OS window.115 /// Event pumping will be done by the caller manually through `waitEventAndDispatch()`116 /// Important: This case is not the nominal case.117 /// Some calls to the `IWindowListener` will make no sense.118 host119 }
120 121 /// Giving commands to a window.122 interfaceIWindow123 {
124 nothrow:
125 @nogc:
126 /// To put in your message loop.127 /// This call should only be used if the window was128 /// created with `WindowUsage.host`.129 /// Else, event pumping is managed by the host or internally (X11).130 voidwaitEventAndDispatch();
131 132 /// If exit was requested.133 /// This call should only be used if the window was134 /// created with `WindowUsage.host`.135 /// In the case of a plug-in, the plugin client will request136 /// termination of the window through its destructor.137 boolterminated();
138 139 /// Profile-purpose: get time in milliseconds.140 /// Use the results of this function for deltas only.141 uintgetTimeMs();
142 143 /// Gets the window's OS handle.144 void* systemHandle();
145 146 /// Request a resize from the native window.147 /// If successful, onResized` should be called after with _some_ width148 /// and height.149 /// Note: DPI unaware. This doesn't check size constraints.150 /// Do not call this with a size that isn't compatible with your desired 151 /// user pixel size, after GUIGraphics _userArea adjustments.152 boolrequestResize(intwidthLogicalPixels, intheightLogicalPixels, boolalsoResizeParentWindow);
153 }
154 155 enumWindowPixelFormat156 {
157 BGRA8,
158 ARGB8,
159 RGBA8160 }
161 162 /// Receiving commands from a window.163 ///164 /// IMPORTANT The IWindow implementation should not call these callback without care.165 /// In particular, there are two sets of calls that are assumed will NOT be called concurrently.166 ///167 /// Set 1:168 /// - `onAnimate`169 /// - key events170 /// - mouse events171 /// - `onMouseCaptureCancelled` 172 /// MUST NOT be called concurrently.173 ///174 /// Set 2:175 /// - `onDraw`176 /// - `onResized`177 /// - `recomputeDirtyAreas` <---- this particular set has birthed many data races178 /// - `getDirtyRectangle`179 /// MUST NOT be called concurrently.180 ///181 /// Some IWindow implentation ensure that unicity through passing in an event queue, others182 /// like the X11 implementation have to use locks.183 ///184 /// TODO: clarify this, additionally onDraw and onAnimate are NOT called concurrently, on purpose:185 /// https://github.com/AuburnSounds/Dplug/issues/453186 interfaceIWindowListener187 {
188 nothrow @nogc:
189 /// Called on mouse click.190 /// Returns: true if the event was handled.191 boolonMouseClick(intx, inty, MouseButtonmb, boolisDoubleClick, MouseStatemstate);
192 193 /// Called on mouse button release194 /// Returns: true if the event was handled.195 boolonMouseRelease(intx, inty, MouseButtonmb, MouseStatemstate);
196 197 /// Called on mouse wheel movement198 /// Returns: true if the event was handled.199 boolonMouseWheel(intx, inty, intwheelDeltaX, intwheelDeltaY, MouseStatemstate);
200 201 /// Called on mouse movement (might not be within the window)202 voidonMouseMove(intx, inty, intdx, intdy, MouseStatemstate);
203 204 /// Called on keyboard press.205 /// Returns: true if the event was handled.206 boolonKeyDown(Keykey);
207 208 /// Called on keyboard release.209 /// Returns: true if the event was handled.210 boolonKeyUp(Keyup);
211 212 /// Render the window in software in the buffer previously returned by `onResized`.213 /// At the end of this function, the whole buffer should be a valid, coherent UI.214 ///215 /// recomputeDirtyAreas() MUST have been called before this is called.216 /// The pixel format cannot change over the lifetime of the window.217 ///218 /// `onDraw` guarantees the pixels to be in the format requested by `pf`, and it also219 /// guarantees that the alpha channel will be filled with 255.220 voidonDraw(WindowPixelFormatpf);
221 222 /// The drawing area size has changed.223 /// Always called at least once before onDraw.224 /// Returns: the location of the full rendered framebuffer.225 ImageRef!RGBAonResized(intwidth, intheight);
226 227 /// Recompute internally what needs be done for the next onDraw.228 /// This function MUST have been called before calling `onDraw` and `getDirtyRectangle`.229 /// This method exists to allow the Window to recompute these draw lists less.230 /// And because cache invalidation was easier on user code than internally in the UI.231 /// Important: once you've called `recomputeDirtyAreas()` you COMMIT to redraw the232 /// corresponding area given by `getDirtyRectangle()`.233 /// IMPORTANT: Two calls to `recomputeDirtyAreas()` will not yield the same area.234 /// VERY IMPORTANT: See the above note about concurrent calls.235 voidrecomputeDirtyAreas();
236 237 /// Returns: Minimal rectangle that contains dirty UIELement in UI + their graphical extent.238 /// Empty box if nothing to update.239 /// recomputeDirtyAreas() MUST have been called before.240 box2igetDirtyRectangle();
241 242 /// Called whenever mouse capture was canceled (ALT + TAB, SetForegroundWindow...)243 voidonMouseCaptureCancelled();
244 245 /// Called whenever mouse exited the window (but a capture could still be in action).246 voidonMouseExitedWindow();
247 248 /// Must be called periodically (ideally 60 times per second but this is not mandatory).249 /// `time` must refer to the window creation time.250 /// `dt` and `time` are expressed in seconds (not milliseconds).251 voidonAnimate(doubledt, doubletime);
252 253 /// Must be called to get the current mouse cursor state for the plugin254 MouseCursorgetMouseCursor();
255 }
256 257 /// Various backends for windowing.258 enumWindowBackend259 {
260 autodetect,
261 win32,
262 carbon, // Legacy, now unsupported263 cocoa,
264 x11265 }
266 267 /// Returns: `true` if that windowing backend is supported on this platform.268 staticisWindowBackendSupported(WindowBackendbackend) nothrow @nogc269 {
270 version(Windows)
271 return (backend == WindowBackend.win32);
272 elseversion(OSX)
273 {
274 version(AArch64)
275 return (backend == WindowBackend.cocoa);
276 elseversion(X86_64)
277 return (backend == WindowBackend.cocoa);
278 elseversion(X86)
279 return (backend == WindowBackend.cocoa);
280 else281 staticassert(false, "unsupported arch");
282 }
283 elseversion(linux)
284 return (backend == WindowBackend.x11);
285 else286 staticassert(false, "Unsupported OS");
287 }
288 289 290 291 /// Factory function to create windows.292 ///293 /// The window is allocated with `mallocNew` and should be destroyed with `destroyFree`.294 ///295 /// Returns: null if this backend isn't available on this platform.296 ///297 /// Params:298 /// usage = Intended usage of the window.299 ///300 /// parentInfo = OS handle of the parent window.301 /// For `WindowBackend.win32` it's a HWND.302 /// For `WindowBackend.carbon` it's a NSWindow.303 /// For `WindowBackend.x11` it's _unused_.304 ///305 /// controlInfo = only used in Carbon Audio Units, an additional parenting information.306 /// Can be `null` otherwise.307 ///308 /// listener = A `IWindowListener` which listens to events by this window. Can be `null` for the moment.309 /// Must outlive the created window.310 ///311 /// backend = Which windowing sub-system is used. Only Mac has any choice in this.312 /// Should be `WindowBackend.autodetect` in almost all cases313 ///314 /// width = Initial width of the window.315 ///316 /// height = Initial height of the window.317 ///318 nothrow @nogc319 IWindowcreateWindow(WindowUsageusage,
320 void* parentInfo,
321 void* controlInfo,
322 IWindowListenerlistener,
323 WindowBackendbackend,
324 intwidth,
325 intheight)
326 {
327 //MAYDO `null` listeners not accepted anymore.328 //assert(listener !is null);329 330 staticWindowBackendautoDetectBackend() nothrow @nogc331 {
332 version(Windows)
333 returnWindowBackend.win32;
334 elseversion(OSX)
335 {
336 returnWindowBackend.cocoa;
337 }
338 elseversion(linux)
339 {
340 returnWindowBackend.x11;
341 }
342 else343 staticassert(false, "Unsupported OS");
344 }
345 346 if (backend == WindowBackend.autodetect)
347 backend = autoDetectBackend();
348 349 version(Windows)
350 {
351 if (backend == WindowBackend.win32)
352 {
353 importcore.sys.windows.windef;
354 importdplug.window.win32window;
355 HWNDparent = cast(HWND)parentInfo;
356 returnmallocNew!Win32Window(parent, listener, width, height);
357 }
358 else359 returnnull;
360 }
361 elseversion(OSX)
362 {
363 if (backend == WindowBackend.cocoa)
364 {
365 importdplug.window.cocoawindow;
366 returnmallocNew!CocoaWindow(usage, parentInfo, listener, width, height);
367 }
368 else369 returnnull;
370 }
371 elseversion(linux)
372 {
373 if (backend == WindowBackend.x11)
374 {
375 importdplug.window.x11window;
376 returnmallocNew!X11Window(usage, parentInfo, listener, width, height);
377 }
378 else379 returnnull;
380 }
381 else382 {
383 staticassert(false, "Unsupported OS.");
384 }
385 }