1 /** 2 * Copyright: Copyright Auburn Sounds 2015 and later. 3 * License: $(LINK2 http://www.boost.org/LICENSE_1_0.txt, Boost License 1.0) 4 * Authors: Guillaume Piolat 5 */ 6 module dplug.window.window; 7 8 import gfm.math.box; 9 10 import dplug.core.nogc; 11 import dplug.graphics.image; 12 import dplug.graphics.view; 13 14 enum Key 15 { 16 space, 17 upArrow, 18 downArrow, 19 leftArrow, 20 rightArrow, 21 digit0, 22 digit1, 23 digit2, 24 digit3, 25 digit4, 26 digit5, 27 digit6, 28 digit7, 29 digit8, 30 digit9, 31 enter, 32 escape, 33 unsupported // special value, means "other" 34 }; 35 36 enum MouseButton 37 { 38 left, 39 right, 40 middle, 41 x1, 42 x2 43 } 44 45 struct MouseState 46 { 47 bool leftButtonDown; 48 bool rightButtonDown; 49 bool middleButtonDown; 50 bool x1ButtonDown; 51 bool x2ButtonDown; 52 bool ctrlPressed; 53 bool shiftPressed; 54 bool altPressed; 55 } 56 57 // Giving commands to a window 58 interface IWindow 59 { 60 nothrow: 61 @nogc: 62 // To put in your message loop 63 void waitEventAndDispatch(); 64 65 // If exit was requested 66 bool terminated(); 67 68 // Profile-purpose: get time in milliseconds. 69 uint getTimeMs(); 70 71 // Get the OS window handle. 72 void* systemHandle(); 73 } 74 75 enum WindowPixelFormat 76 { 77 BGRA8, 78 ARGB8, 79 RGBA8 80 } 81 82 // Receiving commands from a window 83 interface IWindowListener 84 { 85 nothrow @nogc: 86 /// Called on mouse click. 87 /// Returns: true if the event was handled. 88 bool onMouseClick(int x, int y, MouseButton mb, bool isDoubleClick, MouseState mstate); 89 90 /// Called on mouse button release 91 /// Returns: true if the event was handled. 92 bool onMouseRelease(int x, int y, MouseButton mb, MouseState mstate); 93 94 /// Called on mouse wheel movement 95 /// Returns: true if the event was handled. 96 bool onMouseWheel(int x, int y, int wheelDeltaX, int wheelDeltaY, MouseState mstate); 97 98 /// Called on mouse movement (might not be within the window) 99 void onMouseMove(int x, int y, int dx, int dy, MouseState mstate); 100 101 /// Called on keyboard press. 102 /// Returns: true if the event was handled. 103 bool onKeyDown(Key key); 104 105 /// Called on keyboard release. 106 /// Returns: true if the event was handled. 107 bool onKeyUp(Key up); 108 109 /// An image you have to draw to, or return that nothing has changed. 110 /// The location of this image is given before-hand by onResized. 111 /// recomputeDirtyAreas() MUST have been called before. 112 /// The pixel format cannot change over the lifetime of the window. 113 void onDraw(WindowPixelFormat pf); 114 115 /// The drawing area size has changed. 116 /// Always called at least once before onDraw. 117 /// Returns: the location of the full rendered framebuffer. 118 ImageRef!RGBA onResized(int width, int height); 119 120 /// Recompute internally what needs be done for the next onDraw. 121 /// This function MUST be called before calling `onDraw` and `getDirtyRectangle`. 122 /// This method exists to allow the Window to recompute these draw lists less. 123 /// And because cache invalidation was easier on user code than internally in the UI. 124 void recomputeDirtyAreas(); 125 126 /// Returns: Minimal rectangle that contains dirty UIELement in UI + their graphical extent. 127 /// Empty box if nothing to update. 128 /// recomputeDirtyAreas() MUST have been called before. 129 box2i getDirtyRectangle(); 130 131 /// Called whenever mouse capture was canceled (ALT + TAB, SetForegroundWindow...) 132 void onMouseCaptureCancelled(); 133 134 /// Must be called periodically (ideally 60 times per second but this is not mandatory). 135 /// `time` must refer to the window creation time. 136 void onAnimate(double dt, double time); 137 } 138 139 140 enum WindowBackend 141 { 142 autodetect, 143 win32, 144 carbon, 145 cocoa 146 } 147 148 149 /// Factory function to create windows. 150 /// The window is allocated with `mallocEmplace` and should be destroyed with `destroyFree`. 151 /// Returns: null if this backend isn't available on this platform. 152 /// Note: controlInfo is only used by Carbon + AU, can be null otherwise 153 nothrow @nogc 154 IWindow createWindow(void* parentInfo, void* controlInfo, IWindowListener listener, WindowBackend backend, int width, int height) 155 { 156 static WindowBackend autoDetectBackend() nothrow @nogc 157 { 158 version(Windows) 159 return WindowBackend.win32; 160 else version(linux) 161 return WindowBackend.autodetect; 162 else version(OSX) 163 { 164 version(X86_64) 165 { 166 return WindowBackend.cocoa; 167 } 168 else 169 { 170 return WindowBackend.carbon; 171 } 172 } 173 } 174 175 if (backend == WindowBackend.autodetect) 176 backend = autoDetectBackend(); 177 178 version(Windows) 179 { 180 if (backend == WindowBackend.win32) 181 { 182 import core.sys.windows.windef; 183 import dplug.window.win32window; 184 HWND parent = cast(HWND)parentInfo; 185 return mallocEmplace!Win32Window(parent, listener, width, height); 186 } 187 else 188 return null; 189 } 190 else version(linux) 191 { 192 return null; // see linux-windowing branch 193 } 194 else version(OSX) 195 { 196 if (backend == WindowBackend.cocoa) 197 { 198 import dplug.window.cocoawindow; 199 return mallocEmplace!CocoaWindow(parentInfo, listener, width, height); 200 } 201 else if (backend == WindowBackend.carbon) 202 { 203 import dplug.window.carbonwindow; 204 return mallocEmplace!CarbonWindow(parentInfo, controlInfo, listener, width, height); 205 } 206 else 207 return null; 208 } 209 else 210 { 211 static assert(false, "Unsupported OS."); 212 } 213 } 214