1 module nanogui.sdlbackend; 2 3 import std.datetime : Clock; 4 import std.exception: enforce; 5 6 import std.experimental.logger: Logger; 7 8 import gfm.sdl2: SDL_Event, SDL_Cursor, SDL_SetCursor, SDL_FreeCursor; 9 10 import arsd.nanovega : kill, NVGContextFlag; 11 12 import nanogui.screen : Screen; 13 import nanogui.theme : Theme; 14 import nanogui.common : NanoContext, Vector2i, MouseButton, MouseAction, Cursor; 15 import nanogui.sdlapp : SdlApp; 16 17 class SdlBackend : Screen 18 { 19 this(int w, int h, string title) 20 { 21 _sdlApp = new SdlApp(w, h, title); 22 23 _sdlApp.onBeforeLoopStart = () 24 { 25 import std.datetime : dur; 26 27 currTime = Clock.currTime.stdTime; 28 if (currTime - mBlinkingCursorTimestamp > dur!"msecs"(500).total!"hnsecs") 29 { 30 mBlinkingCursorVisible = !mBlinkingCursorVisible; 31 _sdlApp.invalidate(); 32 mBlinkingCursorTimestamp = currTime; 33 } 34 35 if (_onBeforeLoopStart) 36 _onBeforeLoopStart(); 37 }; 38 39 _sdlApp.onDraw = () 40 { 41 if (mNeedToDraw) 42 _sdlApp.invalidate; 43 size = Vector2i(width, height); 44 super.draw(ctx); 45 }; 46 47 _sdlApp.onKeyDown = (ref const(SDL_Event) event) 48 { 49 import nanogui.common : KeyAction; 50 51 _sdlApp.invalidate; 52 53 auto key = event.key.keysym.sym.convertSdlKeyToNanoguiKey; 54 int modifiers = event.key.keysym.mod.convertSdlModifierToNanoguiModifier; 55 return super.keyboardEvent(key, event.key.keysym.scancode, KeyAction.Press, modifiers); 56 }; 57 58 _sdlApp.onMouseWheel = (ref const(SDL_Event) event) 59 { 60 _sdlApp.invalidate; 61 if (event.wheel.y > 0) 62 { 63 btn = MouseButton.WheelUp; 64 return super.scrollCallbackEvent(0, +1, Clock.currTime.stdTime); 65 } 66 else if (event.wheel.y < 0) 67 { 68 btn = MouseButton.WheelDown; 69 return super.scrollCallbackEvent(0, -1, Clock.currTime.stdTime); 70 } 71 return false; 72 }; 73 74 _sdlApp.onMouseMotion = (ref const(SDL_Event) event) 75 { 76 import gfm.sdl2 : SDL_BUTTON_LMASK, SDL_BUTTON_RMASK, SDL_BUTTON_MMASK; 77 78 _sdlApp.invalidate; 79 80 ctx.mouse.x = event.motion.x; 81 ctx.mouse.y = event.motion.y; 82 83 if (event.motion.state & SDL_BUTTON_LMASK) 84 btn = MouseButton.Left; 85 else if (event.motion.state & SDL_BUTTON_RMASK) 86 btn = MouseButton.Right; 87 else if (event.motion.state & SDL_BUTTON_MMASK) 88 btn = MouseButton.Middle; 89 90 if (event.motion.state & SDL_BUTTON_LMASK) 91 modifiers |= MouseButton.Left; 92 if (event.motion.state & SDL_BUTTON_RMASK) 93 modifiers |= MouseButton.Right; 94 if (event.motion.state & SDL_BUTTON_MMASK) 95 modifiers |= MouseButton.Middle; 96 97 action = MouseAction.Motion; 98 return super.cursorPosCallbackEvent(ctx.mouse.x, ctx.mouse.y, Clock.currTime.stdTime); 99 }; 100 101 _sdlApp.onMouseUp = (ref const(SDL_Event) event) 102 { 103 import gfm.sdl2 : SDL_BUTTON_LEFT, SDL_BUTTON_RIGHT, SDL_BUTTON_MIDDLE; 104 105 _sdlApp.invalidate; 106 107 switch(event.button.button) 108 { 109 case SDL_BUTTON_LEFT: 110 btn = MouseButton.Left; 111 break; 112 case SDL_BUTTON_RIGHT: 113 btn = MouseButton.Right; 114 break; 115 case SDL_BUTTON_MIDDLE: 116 btn = MouseButton.Middle; 117 break; 118 default: 119 } 120 action = MouseAction.Release; 121 return super.mouseButtonCallbackEvent(btn, action, modifiers, Clock.currTime.stdTime); 122 }; 123 124 _sdlApp.onMouseDown = (ref const(SDL_Event) event) 125 { 126 import gfm.sdl2 : SDL_BUTTON_LEFT, SDL_BUTTON_RIGHT, SDL_BUTTON_MIDDLE; 127 128 _sdlApp.invalidate; 129 130 switch(event.button.button) 131 { 132 case SDL_BUTTON_LEFT: 133 btn = MouseButton.Left; 134 break; 135 case SDL_BUTTON_RIGHT: 136 btn = MouseButton.Right; 137 break; 138 case SDL_BUTTON_MIDDLE: 139 btn = MouseButton.Middle; 140 break; 141 default: 142 } 143 action = MouseAction.Press; 144 return super.mouseButtonCallbackEvent(btn, action, modifiers, Clock.currTime.stdTime); 145 }; 146 147 _sdlApp.onKeyboardChar = delegate(dchar codepoint) 148 { 149 return keyboardCharacterEvent(codepoint); 150 }; 151 152 _sdlApp.onClose = () 153 { 154 if (_onClose) 155 return _onClose(); 156 157 return true; 158 }; 159 160 ctx = NanoContext(NVGContextFlag.Debug); 161 enforce(ctx !is null, "cannot initialize NanoGui"); 162 163 import gfm.sdl2; 164 mCursorSet[Cursor.Arrow] = SDL_CreateSystemCursor(SDL_SYSTEM_CURSOR_ARROW); 165 mCursorSet[Cursor.IBeam] = SDL_CreateSystemCursor(SDL_SYSTEM_CURSOR_IBEAM); 166 mCursorSet[Cursor.Crosshair] = SDL_CreateSystemCursor(SDL_SYSTEM_CURSOR_CROSSHAIR); 167 mCursorSet[Cursor.Hand] = SDL_CreateSystemCursor(SDL_SYSTEM_CURSOR_HAND); 168 mCursorSet[Cursor.HResize] = SDL_CreateSystemCursor(SDL_SYSTEM_CURSOR_SIZEWE); 169 mCursorSet[Cursor.VResize] = SDL_CreateSystemCursor(SDL_SYSTEM_CURSOR_SIZENS); 170 171 super(w, h, Clock.currTime.stdTime); 172 theme = new Theme(ctx); 173 } 174 175 ~this() 176 { 177 SDL_FreeCursor(mCursorSet[Cursor.Arrow]); 178 SDL_FreeCursor(mCursorSet[Cursor.IBeam]); 179 SDL_FreeCursor(mCursorSet[Cursor.Crosshair]); 180 SDL_FreeCursor(mCursorSet[Cursor.Hand]); 181 SDL_FreeCursor(mCursorSet[Cursor.HResize]); 182 SDL_FreeCursor(mCursorSet[Cursor.VResize]); 183 184 ctx.kill(); 185 destroy(_sdlApp); 186 } 187 188 private void delegate () _onBeforeLoopStart; 189 void onBeforeLoopStart(void delegate () dg) 190 { 191 _onBeforeLoopStart = dg; 192 } 193 194 private bool delegate() _onClose; 195 void onClose(bool delegate() dg) @safe 196 { 197 _onClose = dg; 198 } 199 200 void run() 201 { 202 onVisibleForTheFirstTime(); 203 204 _sdlApp.run(); 205 } 206 207 void close() 208 { 209 _sdlApp.close(); 210 } 211 212 auto invalidate() { _sdlApp.invalidate; } 213 214 abstract void onVisibleForTheFirstTime(); 215 216 override Logger logger() { return _sdlApp.logger; } 217 218 protected: 219 SdlApp _sdlApp; 220 221 MouseButton btn; 222 MouseAction action; 223 int modifiers; 224 225 NanoContext ctx; 226 227 SDL_Cursor*[6] mCursorSet; 228 229 override void cursor(Cursor value) 230 { 231 mCursor = value; 232 SDL_SetCursor(mCursorSet[mCursor]); 233 } 234 235 override Cursor cursor() const 236 { 237 return mCursor; 238 } 239 } 240 241 private auto convertSdlKeyToNanoguiKey(int sdlkey) 242 { 243 import gfm.sdl2; 244 import nanogui.common : KeyAction, Key; 245 246 int nanogui_key; 247 switch(sdlkey) 248 { 249 case SDLK_LEFT: 250 nanogui_key = Key.Left; 251 break; 252 case SDLK_RIGHT: 253 nanogui_key = Key.Right; 254 break; 255 case SDLK_UP: 256 nanogui_key = Key.Up; 257 break; 258 case SDLK_DOWN: 259 nanogui_key = Key.Down; 260 break; 261 case SDLK_BACKSPACE: 262 nanogui_key = Key.Backspace; 263 break; 264 case SDLK_DELETE: 265 nanogui_key = Key.Delete; 266 break; 267 case SDLK_HOME: 268 nanogui_key = Key.Home; 269 break; 270 case SDLK_END: 271 nanogui_key = Key.End; 272 break; 273 case SDLK_RETURN: 274 nanogui_key = Key.Enter; 275 break; 276 case SDLK_a: 277 nanogui_key = Key.A; 278 break; 279 case SDLK_x: 280 nanogui_key = Key.X; 281 break; 282 case SDLK_c: 283 nanogui_key = Key.C; 284 break; 285 case SDLK_v: 286 nanogui_key = Key.V; 287 break; 288 case SDLK_ESCAPE: 289 nanogui_key = Key.Esc; 290 break; 291 default: 292 nanogui_key = sdlkey; 293 } 294 295 return nanogui_key; 296 } 297 298 private auto convertSdlModifierToNanoguiModifier(int mod) 299 { 300 import gfm.sdl2; 301 import nanogui.common : KeyMod; 302 303 int nanogui_mod; 304 305 if (mod & KMOD_LCTRL) 306 nanogui_mod |= KeyMod.Ctrl; 307 if (mod & KMOD_LSHIFT) 308 nanogui_mod |= KeyMod.Shift; 309 if (mod & KMOD_LALT) 310 nanogui_mod |= KeyMod.Alt; 311 if (mod & KMOD_RCTRL) 312 nanogui_mod |= KeyMod.Ctrl; 313 if (mod & KMOD_RSHIFT) 314 nanogui_mod |= KeyMod.Shift; 315 if (mod & KMOD_RALT) 316 nanogui_mod |= KeyMod.Alt; 317 318 return nanogui_mod; 319 }