1 // Written in the D programming language. 2 3 /** 4 This module contains implementation of SDL2 based backend for dlang library. 5 6 7 Synopsis: 8 9 ---- 10 import dlangui.platforms.sdl.sdlapp; 11 12 ---- 13 14 Copyright: Vadim Lopatin, 2014 15 License: Boost License 1.0 16 Authors: Vadim Lopatin, coolreader.org@gmail.com 17 */ 18 module dlangui.platforms.sdl.sdlapp; 19 20 public import dlangui.core.config; 21 static if (BACKEND_SDL): 22 23 import core.runtime; 24 import std.conv; 25 import std.string; 26 import std.utf : toUTF32, toUTF16z; 27 import std.stdio; 28 import std.algorithm; 29 import std.file; 30 31 import dlangui.core.logger; 32 import dlangui.core.events; 33 import dlangui.core.files; 34 import dlangui.graphics.drawbuf; 35 import dlangui.graphics.fonts; 36 import dlangui.graphics.ftfonts; 37 import dlangui.graphics.resources; 38 import dlangui.widgets.styles; 39 import dlangui.widgets.widget; 40 import dlangui.platforms.common.platform; 41 42 import derelict.sdl2.sdl; 43 import derelict.opengl3.gl3; 44 import derelict.opengl3.gl; 45 46 static if (ENABLE_OPENGL) { 47 import dlangui.graphics.gldrawbuf; 48 import dlangui.graphics.glsupport; 49 } 50 51 private derelict.util.exception.ShouldThrow missingSymFunc( string symName ) { 52 import std.algorithm : equal; 53 static import derelict.util.exception; 54 foreach(s; ["SDL_DestroyRenderer", "SDL_GL_DeleteContext", "SDL_DestroyWindow", "SDL_PushEvent", 55 "SDL_GL_SetAttribute", "SDL_GL_CreateContext", "SDL_GetError", 56 "SDL_CreateWindow", "SDL_CreateRenderer", "SDL_GetWindowSize", 57 "SDL_GL_GetDrawableSize", "SDL_GetWindowID", "SDL_SetWindowSize", 58 "SDL_ShowWindow", "SDL_SetWindowTitle", "SDL_CreateRGBSurfaceFrom", 59 "SDL_SetWindowIcon", "SDL_FreeSurface", "SDL_ShowCursor", 60 "SDL_SetCursor", "SDL_CreateSystemCursor", "SDL_DestroyTexture", 61 "SDL_CreateTexture", "SDL_UpdateTexture", "SDL_RenderCopy", 62 "SDL_GL_SwapWindow", "SDL_GL_MakeCurrent", "SDL_SetRenderDrawColor", 63 "SDL_RenderClear", "SDL_RenderPresent", "SDL_GetModState", 64 "SDL_RemoveTimer", "SDL_RemoveTimer", "SDL_PushEvent", 65 "SDL_RegisterEvents", "SDL_WaitEvent", "SDL_StartTextInput", 66 "SDL_Quit", "SDL_HasClipboardText", "SDL_GetClipboardText", 67 "SDL_free", "SDL_SetClipboardText", "SDL_Init"]) { 68 if (symName.equal(s)) // Symbol is used 69 return derelict.util.exception.ShouldThrow.Yes; 70 } 71 // Don't throw for unused symbol 72 return derelict.util.exception.ShouldThrow.No; 73 } 74 75 private __gshared uint USER_EVENT_ID; 76 private __gshared uint TIMER_EVENT_ID; 77 private __gshared uint WINDOW_CLOSE_EVENT_ID; 78 79 class SDLWindow : Window { 80 SDLPlatform _platform; 81 SDL_Window * _win; 82 SDL_Renderer* _renderer; 83 84 SDLWindow[] _children; 85 SDLWindow _parent; 86 87 this(SDLPlatform platform, dstring caption, Window parent, uint flags, uint width = 0, uint height = 0) { 88 _platform = platform; 89 _caption = caption; 90 _windowState = WindowState.hidden; 91 92 _parent = cast(SDLWindow) parent; 93 if (_parent) 94 _parent._children~=this; 95 96 debug Log.d("Creating SDL window"); 97 _dx = width; 98 _dy = height; 99 create(flags); 100 _children.reserve(20); 101 Log.i(_enableOpengl ? "OpenGL is enabled" : "OpenGL is disabled"); 102 } 103 104 ~this() { 105 debug Log.d("Destroying SDL window"); 106 107 if (_parent) { 108 ptrdiff_t index = countUntil(_parent._children,this); 109 if (index > -1 ) { 110 _parent._children=_parent._children.remove(index); 111 } 112 _parent = null; 113 } 114 115 if (_renderer) 116 SDL_DestroyRenderer(_renderer); 117 static if (ENABLE_OPENGL) { 118 if (_context) 119 SDL_GL_DeleteContext(_context); 120 } 121 if (_win) 122 SDL_DestroyWindow(_win); 123 if (_drawbuf) 124 destroy(_drawbuf); 125 } 126 127 128 private bool hasVisibleModalChild() { 129 foreach (SDLWindow w;_children) { 130 if (w.flags & WindowFlag.Modal && w._windowState != WindowState.hidden) 131 return true; 132 133 if (w.hasVisibleModalChild()) 134 return true; 135 } 136 return false; 137 } 138 139 140 private void restoreModalChilds() { 141 foreach (SDLWindow w;_children) { 142 if (w.flags & WindowFlag.Modal && w._windowState != WindowState.hidden) { 143 if (w._windowState == WindowState.maximized) 144 w.activateWindow(); 145 else 146 w.restoreWindow(true); 147 } 148 149 w.restoreModalChilds(); 150 } 151 } 152 153 private void minimizeModalChilds() { 154 foreach (SDLWindow w;_children) { 155 if (w.flags & WindowFlag.Modal && w._windowState != WindowState.hidden) 156 { 157 w.minimizeWindow(); 158 } 159 160 w.minimizeModalChilds(); 161 } 162 } 163 164 165 private void restoreParentWindows() { 166 SDLWindow[] tempWin; 167 if (!_platform) 168 return; 169 170 SDLWindow w = this; 171 172 while (true) { 173 if (w is null) 174 break; 175 176 tempWin~=w; 177 178 w = w._parent; 179 } 180 181 for (size_t i = tempWin.length ; i-- > 0 ; ) 182 tempWin[i].restoreWindow(true); 183 } 184 185 private void minimizeParentWindows() { 186 SDLWindow[] tempWin; 187 if (!_platform) 188 return; 189 190 SDLWindow w = this; 191 192 while (true) { 193 if (w is null) 194 break; 195 196 tempWin~=w; 197 198 w = w._parent; 199 } 200 201 for (size_t i = tempWin.length ; i-- > 0 ; ) 202 tempWin[i].minimizeWindow(); 203 } 204 205 /// post event to handle in UI thread (this method can be used from background thread) 206 override void postEvent(CustomEvent event) { 207 super.postEvent(event); 208 SDL_Event sdlevent; 209 sdlevent.user.type = USER_EVENT_ID; 210 sdlevent.user.code = cast(int)event.uniqueId; 211 sdlevent.user.windowID = windowId; 212 SDL_PushEvent(&sdlevent); 213 } 214 215 216 static if (ENABLE_OPENGL) 217 { 218 static private bool _gl3Reloaded = false; 219 private SDL_GLContext _context; 220 221 protected bool createContext(int versionMajor, int versionMinor) { 222 Log.i("Trying to create OpenGL ", versionMajor, ".", versionMinor, " context"); 223 SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, versionMajor); 224 SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, versionMinor); 225 SDL_GL_SetAttribute(SDL_GL_CONTEXT_PROFILE_MASK, SDL_GL_CONTEXT_PROFILE_CORE); 226 _context = SDL_GL_CreateContext(_win); // Create the actual context and make it current 227 if (!_context) 228 Log.e("SDL_GL_CreateContext failed: ", fromStringz(SDL_GetError())); 229 else { 230 Log.i("Created successfully"); 231 _platform.GLVersionMajor = versionMajor; 232 _platform.GLVersionMinor = versionMinor; 233 } 234 return _context !is null; 235 } 236 } 237 238 bool create(uint flags) { 239 if (!_dx) 240 _dx = 600; 241 if (!_dy) 242 _dy = 400; 243 _flags = flags; 244 uint windowFlags = SDL_WINDOW_HIDDEN; 245 if (flags & WindowFlag.Resizable) 246 windowFlags |= SDL_WINDOW_RESIZABLE; 247 if (flags & WindowFlag.Fullscreen) 248 windowFlags |= SDL_WINDOW_FULLSCREEN; 249 windowFlags |= SDL_WINDOW_ALLOW_HIGHDPI; 250 static if (ENABLE_OPENGL) { 251 if (_enableOpengl) 252 windowFlags |= SDL_WINDOW_OPENGL; 253 } 254 _win = SDL_CreateWindow(toUTF8(_caption).toStringz, SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED, 255 _dx, _dy, 256 windowFlags); 257 static if (ENABLE_OPENGL) { 258 if (!_win) { 259 if (_enableOpengl) { 260 Log.e("SDL_CreateWindow failed - cannot create OpenGL window: ", fromStringz(SDL_GetError())); 261 _enableOpengl = false; 262 // recreate w/o OpenGL 263 windowFlags &= ~SDL_WINDOW_OPENGL; 264 _win = SDL_CreateWindow(toUTF8(_caption).toStringz, SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED, 265 _dx, _dy, 266 windowFlags); 267 } 268 } 269 } 270 if (!_win) { 271 Log.e("SDL2: Failed to create window"); 272 return false; 273 } 274 275 static if (ENABLE_OPENGL) { 276 if (_enableOpengl) { 277 bool success = createContext(_platform.GLVersionMajor, _platform.GLVersionMinor); 278 if (!success) { 279 Log.w("trying other versions of OpenGL"); 280 // Lazy conditions. 281 if(_platform.GLVersionMajor >= 4) 282 success = success || createContext(4, 0); 283 success = success || createContext(3, 3); 284 success = success || createContext(3, 2); 285 success = success || createContext(3, 1); 286 success = success || createContext(2, 1); 287 if (!success) { 288 _enableOpengl = false; 289 _platform.GLVersionMajor = 0; 290 _platform.GLVersionMinor = 0; 291 Log.w("OpenGL support is disabled"); 292 } 293 } 294 if (success) { 295 _enableOpengl = initGLSupport(_platform.GLVersionMajor < 3); 296 fixSize(); 297 } 298 } 299 } 300 if (!_enableOpengl) { 301 _renderer = SDL_CreateRenderer(_win, -1, SDL_RENDERER_ACCELERATED | SDL_RENDERER_PRESENTVSYNC); 302 if (!_renderer) { 303 _renderer = SDL_CreateRenderer(_win, -1, SDL_RENDERER_SOFTWARE); 304 if (!_renderer) { 305 Log.e("SDL2: Failed to create renderer"); 306 return false; 307 } 308 } 309 fixSize(); 310 } 311 setOpenglEnabled(_enableOpengl); 312 windowCaption = _caption; 313 int x = 0; 314 int y = 0; 315 SDL_GetWindowPosition(_win, &x, &y); 316 handleWindowStateChange(WindowState.unspecified, Rect(x, y, _dx, _dy)); 317 return true; 318 } 319 320 void fixSize() { 321 int w = 0; 322 int h = 0; 323 SDL_GetWindowSize(_win, &w, &h); 324 doResize(w, h); 325 } 326 327 void doResize(int width, int height) { 328 int w = 0; 329 int h = 0; 330 SDL_GL_GetDrawableSize(_win, &w, &h); 331 version (Windows) { 332 // DPI already calculated 333 } else { 334 // scale DPI 335 if (w > width && h > height && width > 0 && height > 0) 336 SCREEN_DPI = 96 * w / width; 337 } 338 onResize(std.algorithm.max(width, w), std.algorithm.max(height, h)); 339 } 340 341 @property uint windowId() { 342 if (_win) 343 return SDL_GetWindowID(_win); 344 return 0; 345 } 346 347 override void show() { 348 Log.d("SDLWindow.show() - ", windowCaption); 349 if (!_mainWidget) { 350 Log.e("Window is shown without main widget"); 351 _mainWidget = new Widget(); 352 } 353 if (_mainWidget && !(_flags & WindowFlag.Resizable)) { 354 _mainWidget.measure(SIZE_UNSPECIFIED, SIZE_UNSPECIFIED); 355 SDL_SetWindowSize(_win, _mainWidget.measuredWidth, _mainWidget.measuredHeight); 356 } 357 SDL_ShowWindow(_win); 358 if (_mainWidget) 359 _mainWidget.setFocus(); 360 fixSize(); 361 //update(true); 362 //redraw(); 363 SDL_RaiseWindow(_win); 364 invalidate(); 365 } 366 367 /// close window 368 override void close() { 369 Log.d("SDLWindow.close()"); 370 _platform.closeWindow(this); 371 } 372 373 override protected void handleWindowStateChange(WindowState newState, Rect newWindowRect = RECT_VALUE_IS_NOT_SET) { 374 super.handleWindowStateChange(newState, newWindowRect); 375 } 376 377 override bool setWindowState(WindowState newState, bool activate = false, Rect newWindowRect = RECT_VALUE_IS_NOT_SET) { 378 // override for particular platforms 379 380 if (_win is null) 381 return false; 382 383 bool res = false; 384 385 // change state 386 switch(newState) { 387 case WindowState.maximized: 388 if (_windowState != WindowState.maximized) 389 SDL_MaximizeWindow(_win); 390 res = true; 391 break; 392 case WindowState.minimized: 393 if (_windowState != WindowState.minimized) 394 SDL_MinimizeWindow(_win); 395 res = true; 396 break; 397 case WindowState.hidden: 398 if (_windowState != WindowState.hidden) 399 SDL_HideWindow(_win); 400 res = true; 401 break; 402 case WindowState.normal: 403 if (_windowState != WindowState.normal) 404 SDL_RestoreWindow(_win); 405 res = true; 406 break; 407 default: 408 break; 409 } 410 411 // change size and/or position 412 if (newWindowRect != RECT_VALUE_IS_NOT_SET && (newState == WindowState.normal || newState == WindowState.unspecified)) { 413 414 // change position 415 if (newWindowRect.top != int.min && newWindowRect.left != int.min) { 416 SDL_SetWindowPosition(_win, newWindowRect.left, newWindowRect.top); 417 res = true; 418 } 419 420 // change size 421 if (newWindowRect.bottom != int.min && newWindowRect.right != int.min) { 422 SDL_SetWindowSize(_win, newWindowRect.right, newWindowRect.bottom); 423 res = true; 424 } 425 } 426 427 if (activate) { 428 SDL_RaiseWindow(_win); 429 res = true; 430 } 431 432 return res; 433 } 434 435 436 protected dstring _caption; 437 438 override @property dstring windowCaption() { 439 return _caption; 440 } 441 442 override @property void windowCaption(dstring caption) { 443 _caption = caption; 444 if (_win) 445 SDL_SetWindowTitle(_win, toUTF8(_caption).toStringz); 446 } 447 448 /// sets window icon 449 @property override void windowIcon(DrawBufRef buf) { 450 ColorDrawBuf icon = cast(ColorDrawBuf)buf.get; 451 if (!icon) { 452 Log.e("Trying to set null icon for window"); 453 return; 454 } 455 int iconw = 32; 456 int iconh = 32; 457 ColorDrawBuf iconDraw = new ColorDrawBuf(iconw, iconh); 458 iconDraw.fill(0xFF000000); 459 iconDraw.drawRescaled(Rect(0, 0, iconw, iconh), icon, Rect(0, 0, icon.width, icon.height)); 460 iconDraw.invertAndPreMultiplyAlpha(); 461 SDL_Surface *surface = SDL_CreateRGBSurfaceFrom(iconDraw.scanLine(0), iconDraw.width, iconDraw.height, 32, iconDraw.width * 4, 0x00ff0000,0x0000ff00,0x000000ff,0xff000000); 462 if (surface) { 463 // The icon is attached to the window pointer 464 SDL_SetWindowIcon(_win, surface); 465 // ...and the surface containing the icon pixel data is no longer required. 466 SDL_FreeSurface(surface); 467 } else { 468 Log.e("failed to set window icon"); 469 } 470 destroy(iconDraw); 471 } 472 473 /// after drawing, call to schedule redraw if animation is active 474 override void scheduleAnimation() { 475 invalidate(); 476 } 477 478 protected uint _lastCursorType = CursorType.None; 479 protected SDL_Cursor * [uint] _cursorMap; 480 /// sets cursor type for window 481 override protected void setCursorType(uint cursorType) { 482 // override to support different mouse cursors 483 if (_lastCursorType != cursorType) { 484 if (cursorType == CursorType.None) { 485 SDL_ShowCursor(SDL_DISABLE); 486 return; 487 } 488 if (_lastCursorType == CursorType.None) 489 SDL_ShowCursor(SDL_ENABLE); 490 _lastCursorType = cursorType; 491 SDL_Cursor * cursor; 492 // check for existing cursor in map 493 if (cursorType in _cursorMap) { 494 //Log.d("changing cursor to ", cursorType); 495 cursor = _cursorMap[cursorType]; 496 if (cursor) 497 SDL_SetCursor(cursor); 498 return; 499 } 500 // create new cursor 501 switch (cursorType) with(CursorType) 502 { 503 case Arrow: 504 cursor = SDL_CreateSystemCursor(SDL_SYSTEM_CURSOR_ARROW); 505 break; 506 case IBeam: 507 cursor = SDL_CreateSystemCursor(SDL_SYSTEM_CURSOR_IBEAM); 508 break; 509 case Wait: 510 cursor = SDL_CreateSystemCursor(SDL_SYSTEM_CURSOR_WAIT); 511 break; 512 case WaitArrow: 513 cursor = SDL_CreateSystemCursor(SDL_SYSTEM_CURSOR_WAITARROW); 514 break; 515 case Crosshair: 516 cursor = SDL_CreateSystemCursor(SDL_SYSTEM_CURSOR_CROSSHAIR); 517 break; 518 case No: 519 cursor = SDL_CreateSystemCursor(SDL_SYSTEM_CURSOR_NO); 520 break; 521 case Hand: 522 cursor = SDL_CreateSystemCursor(SDL_SYSTEM_CURSOR_HAND); 523 break; 524 case SizeNWSE: 525 cursor = SDL_CreateSystemCursor(SDL_SYSTEM_CURSOR_SIZENWSE); 526 break; 527 case SizeNESW: 528 cursor = SDL_CreateSystemCursor(SDL_SYSTEM_CURSOR_SIZENESW); 529 break; 530 case SizeWE: 531 cursor = SDL_CreateSystemCursor(SDL_SYSTEM_CURSOR_SIZEWE); 532 break; 533 case SizeNS: 534 cursor = SDL_CreateSystemCursor(SDL_SYSTEM_CURSOR_SIZENS); 535 break; 536 case SizeAll: 537 cursor = SDL_CreateSystemCursor(SDL_SYSTEM_CURSOR_SIZEALL); 538 break; 539 default: 540 // TODO: support custom cursors 541 cursor = SDL_CreateSystemCursor(SDL_SYSTEM_CURSOR_ARROW); 542 break; 543 } 544 _cursorMap[cursorType] = cursor; 545 if (cursor) { 546 debug(DebugSDL) Log.d("changing cursor to ", cursorType); 547 SDL_SetCursor(cursor); 548 } 549 } 550 } 551 552 SDL_Texture * _texture; 553 int _txw; 554 int _txh; 555 private void updateBufferSize() { 556 if (_texture && (_txw != _dx || _txh != _dy)) { 557 SDL_DestroyTexture(_texture); 558 _texture = null; 559 } 560 if (!_texture) { 561 _texture = SDL_CreateTexture(_renderer, 562 SDL_PIXELFORMAT_ARGB8888, 563 SDL_TEXTUREACCESS_STATIC, //SDL_TEXTUREACCESS_STREAMING, 564 _dx, 565 _dy); 566 _txw = _dx; 567 _txh = _dy; 568 } 569 } 570 571 private void draw(ColorDrawBuf buf) { 572 updateBufferSize(); 573 SDL_Rect rect; 574 rect.w = buf.width; 575 rect.h = buf.height; 576 SDL_UpdateTexture(_texture, 577 &rect, 578 cast(const void*)buf.scanLine(0), 579 buf.width * cast(int)uint.sizeof); 580 SDL_RenderCopy(_renderer, _texture, &rect, &rect); 581 } 582 583 void redraw() { 584 //Log.e("Widget instance count in SDLWindow.redraw: ", Widget.instanceCount()); 585 // check if size has been changed 586 fixSize(); 587 588 if (_enableOpengl) { 589 static if (ENABLE_OPENGL) { 590 SDL_GL_MakeCurrent(_win, _context); 591 glDisable(GL_DEPTH_TEST); 592 glViewport(0, 0, _dx, _dy); 593 float a = 1.0f; 594 float r = ((_backgroundColor >> 16) & 255) / 255.0f; 595 float g = ((_backgroundColor >> 8) & 255) / 255.0f; 596 float b = ((_backgroundColor >> 0) & 255) / 255.0f; 597 glClearColor(r, g, b, a); 598 glClear(GL_COLOR_BUFFER_BIT); 599 if (!_drawbuf) 600 _drawbuf = new GLDrawBuf(_dx, _dy); 601 _drawbuf.resize(_dx, _dy); 602 _drawbuf.beforeDrawing(); 603 onDraw(_drawbuf); 604 _drawbuf.afterDrawing(); 605 SDL_GL_SwapWindow(_win); 606 } 607 } else { 608 // Select the color for drawing. 609 ubyte r = cast(ubyte)((_backgroundColor >> 16) & 255); 610 ubyte g = cast(ubyte)((_backgroundColor >> 8) & 255); 611 ubyte b = cast(ubyte)((_backgroundColor >> 0) & 255); 612 SDL_SetRenderDrawColor(_renderer, r, g, b, 255); 613 // Clear the entire screen to our selected color. 614 SDL_RenderClear(_renderer); 615 616 if (!_drawbuf) 617 _drawbuf = new ColorDrawBuf(_dx, _dy); 618 _drawbuf.resize(_dx, _dy); 619 _drawbuf.fill(_backgroundColor); 620 onDraw(_drawbuf); 621 draw(cast(ColorDrawBuf)_drawbuf); 622 623 // Up until now everything was drawn behind the scenes. 624 // This will show the new, red contents of the window. 625 SDL_RenderPresent(_renderer); 626 } 627 } 628 629 DrawBuf _drawbuf; 630 631 //bool _exposeSent; 632 void processExpose() { 633 redraw(); 634 //_exposeSent = false; 635 } 636 637 protected ButtonDetails _lbutton; 638 protected ButtonDetails _mbutton; 639 protected ButtonDetails _rbutton; 640 ushort convertMouseFlags(uint flags) { 641 ushort res = 0; 642 if (flags & SDL_BUTTON_LMASK) 643 res |= MouseFlag.LButton; 644 if (flags & SDL_BUTTON_RMASK) 645 res |= MouseFlag.RButton; 646 if (flags & SDL_BUTTON_MMASK) 647 res |= MouseFlag.MButton; 648 return res; 649 } 650 651 MouseButton convertMouseButton(uint button) { 652 if (button == SDL_BUTTON_LEFT) 653 return MouseButton.Left; 654 if (button == SDL_BUTTON_RIGHT) 655 return MouseButton.Right; 656 if (button == SDL_BUTTON_MIDDLE) 657 return MouseButton.Middle; 658 return MouseButton.None; 659 } 660 661 ushort lastFlags; 662 short lastx; 663 short lasty; 664 void processMouseEvent(MouseAction action, uint button, uint state, int x, int y) { 665 666 // correct mouse coordinates for HIGHDPI on mac 667 int drawableW = 0; 668 int drawableH = 0; 669 int winW = 0; 670 int winH = 0; 671 SDL_GL_GetDrawableSize(_win, &drawableW, &drawableH); 672 SDL_GetWindowSize(_win, &winW, &winH); 673 if (drawableW != winW || drawableH != winH) { 674 if (drawableW > 0 && winW > 0 && drawableH > 0 && drawableW > 0) { 675 x = x * drawableW / winW; 676 y = y * drawableH / winH; 677 } 678 } 679 680 681 MouseEvent event = null; 682 if (action == MouseAction.Wheel) { 683 // handle wheel 684 short wheelDelta = cast(short)y; 685 if (_keyFlags & KeyFlag.Shift) 686 lastFlags |= MouseFlag.Shift; 687 else 688 lastFlags &= ~MouseFlag.Shift; 689 if (_keyFlags & KeyFlag.Control) 690 lastFlags |= MouseFlag.Control; 691 else 692 lastFlags &= ~MouseFlag.Control; 693 if (_keyFlags & KeyFlag.Alt) 694 lastFlags |= MouseFlag.Alt; 695 else 696 lastFlags &= ~MouseFlag.Alt; 697 if (wheelDelta) 698 event = new MouseEvent(action, MouseButton.None, lastFlags, lastx, lasty, wheelDelta); 699 } else { 700 lastFlags = convertMouseFlags(state); 701 if (_keyFlags & KeyFlag.Shift) 702 lastFlags |= MouseFlag.Shift; 703 if (_keyFlags & KeyFlag.Control) 704 lastFlags |= MouseFlag.Control; 705 if (_keyFlags & KeyFlag.Alt) 706 lastFlags |= MouseFlag.Alt; 707 lastx = cast(short)x; 708 lasty = cast(short)y; 709 MouseButton btn = convertMouseButton(button); 710 event = new MouseEvent(action, btn, lastFlags, lastx, lasty); 711 } 712 if (event) { 713 ButtonDetails * pbuttonDetails = null; 714 if (button == MouseButton.Left) 715 pbuttonDetails = &_lbutton; 716 else if (button == MouseButton.Right) 717 pbuttonDetails = &_rbutton; 718 else if (button == MouseButton.Middle) 719 pbuttonDetails = &_mbutton; 720 if (pbuttonDetails) { 721 if (action == MouseAction.ButtonDown) { 722 pbuttonDetails.down(cast(short)x, cast(short)y, lastFlags); 723 } else if (action == MouseAction.ButtonUp) { 724 pbuttonDetails.up(cast(short)x, cast(short)y, lastFlags); 725 } 726 } 727 event.lbutton = _lbutton; 728 event.rbutton = _rbutton; 729 event.mbutton = _mbutton; 730 bool res = dispatchMouseEvent(event); 731 if (res) { 732 debug(mouse) Log.d("Calling update() after mouse event"); 733 update(); 734 } 735 } 736 } 737 738 uint convertKeyCode(uint keyCode) { 739 switch(keyCode) { 740 case SDLK_0: 741 return KeyCode.KEY_0; 742 case SDLK_1: 743 return KeyCode.KEY_1; 744 case SDLK_2: 745 return KeyCode.KEY_2; 746 case SDLK_3: 747 return KeyCode.KEY_3; 748 case SDLK_4: 749 return KeyCode.KEY_4; 750 case SDLK_5: 751 return KeyCode.KEY_5; 752 case SDLK_6: 753 return KeyCode.KEY_6; 754 case SDLK_7: 755 return KeyCode.KEY_7; 756 case SDLK_8: 757 return KeyCode.KEY_8; 758 case SDLK_9: 759 return KeyCode.KEY_9; 760 case SDLK_a: 761 return KeyCode.KEY_A; 762 case SDLK_b: 763 return KeyCode.KEY_B; 764 case SDLK_c: 765 return KeyCode.KEY_C; 766 case SDLK_d: 767 return KeyCode.KEY_D; 768 case SDLK_e: 769 return KeyCode.KEY_E; 770 case SDLK_f: 771 return KeyCode.KEY_F; 772 case SDLK_g: 773 return KeyCode.KEY_G; 774 case SDLK_h: 775 return KeyCode.KEY_H; 776 case SDLK_i: 777 return KeyCode.KEY_I; 778 case SDLK_j: 779 return KeyCode.KEY_J; 780 case SDLK_k: 781 return KeyCode.KEY_K; 782 case SDLK_l: 783 return KeyCode.KEY_L; 784 case SDLK_m: 785 return KeyCode.KEY_M; 786 case SDLK_n: 787 return KeyCode.KEY_N; 788 case SDLK_o: 789 return KeyCode.KEY_O; 790 case SDLK_p: 791 return KeyCode.KEY_P; 792 case SDLK_q: 793 return KeyCode.KEY_Q; 794 case SDLK_r: 795 return KeyCode.KEY_R; 796 case SDLK_s: 797 return KeyCode.KEY_S; 798 case SDLK_t: 799 return KeyCode.KEY_T; 800 case SDLK_u: 801 return KeyCode.KEY_U; 802 case SDLK_v: 803 return KeyCode.KEY_V; 804 case SDLK_w: 805 return KeyCode.KEY_W; 806 case SDLK_x: 807 return KeyCode.KEY_X; 808 case SDLK_y: 809 return KeyCode.KEY_Y; 810 case SDLK_z: 811 return KeyCode.KEY_Z; 812 case SDLK_F1: 813 return KeyCode.F1; 814 case SDLK_F2: 815 return KeyCode.F2; 816 case SDLK_F3: 817 return KeyCode.F3; 818 case SDLK_F4: 819 return KeyCode.F4; 820 case SDLK_F5: 821 return KeyCode.F5; 822 case SDLK_F6: 823 return KeyCode.F6; 824 case SDLK_F7: 825 return KeyCode.F7; 826 case SDLK_F8: 827 return KeyCode.F8; 828 case SDLK_F9: 829 return KeyCode.F9; 830 case SDLK_F10: 831 return KeyCode.F10; 832 case SDLK_F11: 833 return KeyCode.F11; 834 case SDLK_F12: 835 return KeyCode.F12; 836 case SDLK_F13: 837 return KeyCode.F13; 838 case SDLK_F14: 839 return KeyCode.F14; 840 case SDLK_F15: 841 return KeyCode.F15; 842 case SDLK_F16: 843 return KeyCode.F16; 844 case SDLK_F17: 845 return KeyCode.F17; 846 case SDLK_F18: 847 return KeyCode.F18; 848 case SDLK_F19: 849 return KeyCode.F19; 850 case SDLK_F20: 851 return KeyCode.F20; 852 case SDLK_F21: 853 return KeyCode.F21; 854 case SDLK_F22: 855 return KeyCode.F22; 856 case SDLK_F23: 857 return KeyCode.F23; 858 case SDLK_F24: 859 return KeyCode.F24; 860 case SDLK_BACKSPACE: 861 return KeyCode.BACK; 862 case SDLK_SPACE: 863 return KeyCode.SPACE; 864 case SDLK_TAB: 865 return KeyCode.TAB; 866 case SDLK_RETURN: 867 return KeyCode.RETURN; 868 case SDLK_ESCAPE: 869 return KeyCode.ESCAPE; 870 case SDLK_DELETE: 871 case 0x40000063: // dirty hack for Linux - key on keypad 872 return KeyCode.DEL; 873 case SDLK_INSERT: 874 case 0x40000062: // dirty hack for Linux - key on keypad 875 return KeyCode.INS; 876 case SDLK_HOME: 877 case 0x4000005f: // dirty hack for Linux - key on keypad 878 return KeyCode.HOME; 879 case SDLK_PAGEUP: 880 case 0x40000061: // dirty hack for Linux - key on keypad 881 return KeyCode.PAGEUP; 882 case SDLK_END: 883 case 0x40000059: // dirty hack for Linux - key on keypad 884 return KeyCode.END; 885 case SDLK_PAGEDOWN: 886 case 0x4000005b: // dirty hack for Linux - key on keypad 887 return KeyCode.PAGEDOWN; 888 case SDLK_LEFT: 889 case 0x4000005c: // dirty hack for Linux - key on keypad 890 return KeyCode.LEFT; 891 case SDLK_RIGHT: 892 case 0x4000005e: // dirty hack for Linux - key on keypad 893 return KeyCode.RIGHT; 894 case SDLK_UP: 895 case 0x40000060: // dirty hack for Linux - key on keypad 896 return KeyCode.UP; 897 case SDLK_DOWN: 898 case 0x4000005a: // dirty hack for Linux - key on keypad 899 return KeyCode.DOWN; 900 case SDLK_KP_ENTER: 901 return KeyCode.RETURN; 902 case SDLK_LCTRL: 903 return KeyCode.LCONTROL; 904 case SDLK_LSHIFT: 905 return KeyCode.LSHIFT; 906 case SDLK_LALT: 907 return KeyCode.LALT; 908 case SDLK_RCTRL: 909 return KeyCode.RCONTROL; 910 case SDLK_RSHIFT: 911 return KeyCode.RSHIFT; 912 case SDLK_RALT: 913 return KeyCode.RALT; 914 case SDLK_LGUI: 915 return KeyCode.LWIN; 916 case SDLK_RGUI: 917 return KeyCode.RWIN; 918 case '/': 919 return KeyCode.KEY_DIVIDE; 920 default: 921 return 0x10000 | keyCode; 922 } 923 } 924 925 uint convertKeyFlags(uint flags) { 926 uint res; 927 if (flags & KMOD_CTRL) 928 res |= KeyFlag.Control; 929 if (flags & KMOD_SHIFT) 930 res |= KeyFlag.Shift; 931 if (flags & KMOD_ALT) 932 res |= KeyFlag.Alt; 933 if (flags & KMOD_GUI) 934 res |= KeyFlag.Menu; 935 if (flags & KMOD_RCTRL) 936 res |= KeyFlag.RControl | KeyFlag.Control; 937 if (flags & KMOD_RSHIFT) 938 res |= KeyFlag.RShift | KeyFlag.Shift; 939 if (flags & KMOD_RALT) 940 res |= KeyFlag.RAlt | KeyFlag.Alt; 941 if (flags & KMOD_LCTRL) 942 res |= KeyFlag.LControl | KeyFlag.Control; 943 if (flags & KMOD_LSHIFT) 944 res |= KeyFlag.LShift | KeyFlag.Shift; 945 if (flags & KMOD_LALT) 946 res |= KeyFlag.LAlt | KeyFlag.Alt; 947 return res; 948 } 949 950 bool processTextInput(const char * s) { 951 string str = fromStringz(s).dup; 952 dstring ds = toUTF32(str); 953 uint flags = convertKeyFlags(SDL_GetModState()); 954 //do not handle Ctrl+Space as text https://github.com/buggins/dlangui/issues/160 955 //but do hanlde RAlt https://github.com/buggins/dlangide/issues/129 956 if (flags & KeyFlag.Control || (flags & KeyFlag.LAlt) == KeyFlag.LAlt || flags & KeyFlag.Menu) 957 return true; 958 959 bool res = dispatchKeyEvent(new KeyEvent(KeyAction.Text, 0, flags, ds)); 960 if (res) { 961 debug(DebugSDL) Log.d("Calling update() after text event"); 962 update(); 963 } 964 return res; 965 } 966 967 static bool isNumLockEnabled() 968 { 969 version(Windows) { 970 return !!(GetKeyState( VK_NUMLOCK ) & 1); 971 } else { 972 return !!(SDL_GetModState() & KMOD_NUM); 973 } 974 } 975 976 uint _keyFlags; 977 bool processKeyEvent(KeyAction action, uint keyCodeIn, uint flags) { 978 debug(DebugSDL) Log.d("processKeyEvent ", action, " SDL key=0x", format("%08x", keyCodeIn), " SDL flags=0x", format("%08x", flags)); 979 uint keyCode = convertKeyCode(keyCodeIn); 980 flags = convertKeyFlags(flags); 981 if (action == KeyAction.KeyDown) { 982 switch(keyCode) { 983 case KeyCode.ALT: 984 flags |= KeyFlag.Alt; 985 break; 986 case KeyCode.RALT: 987 flags |= KeyFlag.Alt | KeyFlag.RAlt; 988 break; 989 case KeyCode.LALT: 990 flags |= KeyFlag.Alt | KeyFlag.LAlt; 991 break; 992 case KeyCode.CONTROL: 993 flags |= KeyFlag.Control; 994 break; 995 case KeyCode.LWIN: 996 case KeyCode.RWIN: 997 flags |= KeyFlag.Menu; 998 break; 999 case KeyCode.RCONTROL: 1000 flags |= KeyFlag.Control | KeyFlag.RControl; 1001 break; 1002 case KeyCode.LCONTROL: 1003 flags |= KeyFlag.Control | KeyFlag.LControl; 1004 break; 1005 case KeyCode.SHIFT: 1006 flags |= KeyFlag.Shift; 1007 break; 1008 case KeyCode.RSHIFT: 1009 flags |= KeyFlag.Shift | KeyFlag.RShift; 1010 break; 1011 case KeyCode.LSHIFT: 1012 flags |= KeyFlag.Shift | KeyFlag.LShift; 1013 break; 1014 1015 default: 1016 break; 1017 } 1018 } 1019 _keyFlags = flags; 1020 1021 debug(DebugSDL) Log.d("processKeyEvent ", action, " converted key=0x", format("%08x", keyCode), " converted flags=0x", format("%08x", flags)); 1022 if (action == KeyAction.KeyDown || action == KeyAction.KeyUp) { 1023 if ((keyCodeIn >= SDLK_KP_1 && keyCodeIn <= SDLK_KP_0 1024 || keyCodeIn == SDLK_KP_PERIOD 1025 //|| keyCodeIn >= 0x40000059 && keyCodeIn 1026 ) && isNumLockEnabled) 1027 return false; 1028 } 1029 bool res = dispatchKeyEvent(new KeyEvent(action, keyCode, flags)); 1030 // if ((keyCode & 0x10000) && (keyCode & 0xF000) != 0xF000) { 1031 // dchar[1] text; 1032 // text[0] = keyCode & 0xFFFF; 1033 // res = dispatchKeyEvent(new KeyEvent(KeyAction.Text, keyCode, flags, cast(dstring)text)) || res; 1034 // } 1035 if (res) { 1036 debug(DebugSDL) Log.d("Calling update() after key event"); 1037 update(); 1038 } 1039 return res; 1040 } 1041 1042 uint _lastRedrawEventCode; 1043 /// request window redraw 1044 override void invalidate() { 1045 _platform.sendRedrawEvent(windowId, ++_lastRedrawEventCode); 1046 } 1047 1048 void processRedrawEvent(uint code) { 1049 if (code == _lastRedrawEventCode) 1050 redraw(); 1051 } 1052 1053 1054 private long _nextExpectedTimerTs; 1055 private SDL_TimerID _timerId = 0; 1056 1057 /// schedule timer for interval in milliseconds - call window.onTimer when finished 1058 override protected void scheduleSystemTimer(long intervalMillis) { 1059 if (intervalMillis < 10) 1060 intervalMillis = 10; 1061 long nextts = currentTimeMillis + intervalMillis; 1062 if (_timerId && _nextExpectedTimerTs && _nextExpectedTimerTs < nextts + 10) 1063 return; // don't reschedule timer, timer event will be received soon 1064 if (_win) { 1065 if (_timerId) { 1066 SDL_RemoveTimer(_timerId); 1067 _timerId = 0; 1068 } 1069 _timerId = SDL_AddTimer(cast(uint)intervalMillis, &myTimerCallbackFunc, cast(void*)windowId); 1070 _nextExpectedTimerTs = nextts; 1071 } 1072 } 1073 1074 void handleTimer(SDL_TimerID timerId) { 1075 SDL_RemoveTimer(_timerId); 1076 _timerId = 0; 1077 _nextExpectedTimerTs = 0; 1078 onTimer(); 1079 } 1080 } 1081 1082 private extern(C) uint myTimerCallbackFunc(uint interval, void *param) nothrow { 1083 uint windowId = cast(uint)param; 1084 SDL_Event sdlevent; 1085 sdlevent.user.type = TIMER_EVENT_ID; 1086 sdlevent.user.code = 0; 1087 sdlevent.user.windowID = windowId; 1088 SDL_PushEvent(&sdlevent); 1089 return(interval); 1090 } 1091 1092 private __gshared bool _enableOpengl; 1093 1094 class SDLPlatform : Platform { 1095 this() { 1096 } 1097 1098 private SDLWindow[uint] _windowMap; 1099 1100 ~this() { 1101 foreach(ref SDLWindow wnd; _windowMap) { 1102 destroy(wnd); 1103 wnd = null; 1104 } 1105 destroy(_windowMap); 1106 } 1107 1108 SDLWindow getWindow(uint id) { 1109 if (id in _windowMap) 1110 return _windowMap[id]; 1111 return null; 1112 } 1113 1114 /// close window 1115 override void closeWindow(Window w) { 1116 SDLWindow window = cast(SDLWindow)w; 1117 SDL_Event sdlevent; 1118 sdlevent.user.type = WINDOW_CLOSE_EVENT_ID; 1119 sdlevent.user.code = 0; 1120 sdlevent.user.windowID = window.windowId; 1121 SDL_PushEvent(&sdlevent); 1122 } 1123 1124 /// calls request layout for all windows 1125 override void requestLayout() { 1126 foreach(w; _windowMap) { 1127 w.requestLayout(); 1128 w.invalidate(); 1129 } 1130 } 1131 1132 /// handle theme change: e.g. reload some themed resources 1133 override void onThemeChanged() { 1134 foreach(w; _windowMap) 1135 w.dispatchThemeChanged(); 1136 } 1137 1138 private uint _redrawEventId; 1139 1140 void sendRedrawEvent(uint windowId, uint code) { 1141 if (!_redrawEventId) 1142 _redrawEventId = SDL_RegisterEvents(1); 1143 SDL_Event event; 1144 event.type = _redrawEventId; 1145 event.user.windowID = windowId; 1146 event.user.code = code; 1147 SDL_PushEvent(&event); 1148 } 1149 1150 override Window createWindow(dstring windowCaption, Window parent, uint flags = WindowFlag.Resizable, uint width = 0, uint height = 0) { 1151 setDefaultLanguageAndThemeIfNecessary(); 1152 int oldDPI = SCREEN_DPI; 1153 int newwidth = width; 1154 int newheight = height; 1155 version(Windows) { 1156 newwidth = pointsToPixels(width); 1157 newheight = pointsToPixels(height); 1158 } 1159 SDLWindow res = new SDLWindow(this, windowCaption, parent, flags, newwidth, newheight); 1160 _windowMap[res.windowId] = res; 1161 if (oldDPI != SCREEN_DPI) { 1162 version(Windows) { 1163 newwidth = pointsToPixels(width); 1164 newheight = pointsToPixels(height); 1165 if (newwidth != width || newheight != height) 1166 SDL_SetWindowSize(res._win, newwidth, newheight); 1167 } 1168 onThemeChanged(); 1169 } 1170 return res; 1171 } 1172 1173 override bool hasModalWindowsAbove(Window w) { 1174 SDLWindow sdlWin = cast (SDLWindow) w; 1175 if (sdlWin) { 1176 return sdlWin.hasVisibleModalChild(); 1177 } 1178 return false; 1179 } 1180 1181 1182 //void redrawWindows() { 1183 // foreach(w; _windowMap) 1184 // w.redraw(); 1185 //} 1186 1187 private bool _windowsMinimized = false; 1188 1189 override int enterMessageLoop() { 1190 Log.i("entering message loop"); 1191 SDL_Event event; 1192 bool quit = false; 1193 bool skipNextQuit = false; 1194 while(!quit) { 1195 //redrawWindows(); 1196 if (SDL_WaitEvent(&event)) { 1197 1198 //Log.d("Event.type = ", event.type); 1199 1200 if (event.type == SDL_QUIT) { 1201 if (!skipNextQuit) { 1202 Log.i("event.type == SDL_QUIT"); 1203 quit = true; 1204 break; 1205 } 1206 skipNextQuit = false; 1207 } 1208 if (_redrawEventId && event.type == _redrawEventId) { 1209 // user defined redraw event 1210 uint windowID = event.user.windowID; 1211 SDLWindow w = getWindow(windowID); 1212 if (w) { 1213 w.processRedrawEvent(event.user.code); 1214 } 1215 continue; 1216 } 1217 switch (event.type) { 1218 case SDL_WINDOWEVENT: 1219 { 1220 // WINDOW EVENTS 1221 uint windowID = event.window.windowID; 1222 SDLWindow w = getWindow(windowID); 1223 if (!w) { 1224 Log.w("SDL_WINDOWEVENT ", event.window.event, " received with unknown id ", windowID); 1225 break; 1226 } 1227 // found window 1228 switch (event.window.event) { 1229 case SDL_WINDOWEVENT_RESIZED: 1230 debug(DebugSDL) Log.d("SDL_WINDOWEVENT_RESIZED win=", event.window.windowID, " pos=", event.window.data1, 1231 ",", event.window.data2); 1232 //redraw not needed here: SDL_WINDOWEVENT_RESIZED is following SDL_WINDOWEVENT_SIZE_CHANGED if the size was changed by an external event (window manager, user) 1233 break; 1234 case SDL_WINDOWEVENT_SIZE_CHANGED: 1235 debug(DebugSDL) Log.d("SDL_WINDOWEVENT_SIZE_CHANGED win=", event.window.windowID, " pos=", event.window.data1, 1236 ",", event.window.data2); 1237 w.handleWindowStateChange(WindowState.unspecified, Rect(w.windowRect().left, w.windowRect().top, event.window.data1, event.window.data2)); 1238 w.redraw(); 1239 break; 1240 case SDL_WINDOWEVENT_CLOSE: 1241 if (!w.hasVisibleModalChild()) { 1242 if (w.handleCanClose()) { 1243 debug(DebugSDL) Log.d("SDL_WINDOWEVENT_CLOSE win=", event.window.windowID); 1244 _windowMap.remove(windowID); 1245 destroy(w); 1246 } else { 1247 skipNextQuit = true; 1248 } 1249 } 1250 break; 1251 case SDL_WINDOWEVENT_SHOWN: 1252 debug(DebugSDL) Log.d("SDL_WINDOWEVENT_SHOWN - ", w.windowCaption); 1253 if (w.windowState()!=WindowState.normal) 1254 w.handleWindowStateChange(WindowState.normal); 1255 if (!_windowsMinimized && w.hasVisibleModalChild()) 1256 w.restoreModalChilds(); 1257 break; 1258 case SDL_WINDOWEVENT_HIDDEN: 1259 debug(DebugSDL) Log.d("SDL_WINDOWEVENT_HIDDEN - ", w.windowCaption); 1260 if (w.windowState()!=WindowState.hidden) 1261 w.handleWindowStateChange(WindowState.hidden); 1262 break; 1263 case SDL_WINDOWEVENT_EXPOSED: 1264 debug(DebugSDL) Log.d("SDL_WINDOWEVENT_EXPOSED - ", w.windowCaption); 1265 w.invalidate(); 1266 break; 1267 case SDL_WINDOWEVENT_MOVED: 1268 debug(DebugSDL) Log.d("SDL_WINDOWEVENT_MOVED - ", w.windowCaption); 1269 w.handleWindowStateChange(WindowState.unspecified, Rect(event.window.data1, event.window.data2, w.windowRect().right, w.windowRect().bottom)); 1270 if (!_windowsMinimized && w.hasVisibleModalChild()) 1271 w.restoreModalChilds(); 1272 break; 1273 case SDL_WINDOWEVENT_MINIMIZED: 1274 debug(DebugSDL) Log.d("SDL_WINDOWEVENT_MINIMIZED - ", w.windowCaption); 1275 if (w.windowState()!=WindowState.minimized) 1276 w.handleWindowStateChange(WindowState.minimized); 1277 if (!_windowsMinimized && w.hasVisibleModalChild()) 1278 w.minimizeModalChilds(); 1279 if (!_windowsMinimized && w.flags & WindowFlag.Modal) 1280 w.minimizeParentWindows(); 1281 _windowsMinimized = true; 1282 break; 1283 case SDL_WINDOWEVENT_MAXIMIZED: 1284 debug(DebugSDL) Log.d("SDL_WINDOWEVENT_MAXIMIZED - ", w.windowCaption); 1285 if (w.windowState()!=WindowState.maximized) 1286 w.handleWindowStateChange(WindowState.maximized); 1287 _windowsMinimized = false; 1288 break; 1289 case SDL_WINDOWEVENT_RESTORED: 1290 debug(DebugSDL) Log.d("SDL_WINDOWEVENT_RESTORED - ", w.windowCaption); 1291 _windowsMinimized = false; 1292 if (w.flags & WindowFlag.Modal) { 1293 w.restoreParentWindows(); 1294 w.restoreWindow(true); 1295 } 1296 1297 if (w.windowState()!=WindowState.normal) 1298 w.handleWindowStateChange(WindowState.normal); 1299 1300 if (w.hasVisibleModalChild()) 1301 w.restoreModalChilds(); 1302 version(linux) { //not sure if needed on Windows or OSX. Also need to check on FreeBSD 1303 w.invalidate(); 1304 } 1305 break; 1306 case SDL_WINDOWEVENT_ENTER: 1307 debug(DebugSDL) Log.d("SDL_WINDOWEVENT_ENTER - ", w.windowCaption); 1308 break; 1309 case SDL_WINDOWEVENT_LEAVE: 1310 debug(DebugSDL) Log.d("SDL_WINDOWEVENT_LEAVE - ", w.windowCaption); 1311 break; 1312 case SDL_WINDOWEVENT_FOCUS_GAINED: 1313 debug(DebugSDL) Log.d("SDL_WINDOWEVENT_FOCUS_GAINED - ", w.windowCaption); 1314 if (!_windowsMinimized) 1315 w.restoreModalChilds(); 1316 break; 1317 case SDL_WINDOWEVENT_FOCUS_LOST: 1318 debug(DebugSDL) Log.d("SDL_WINDOWEVENT_FOCUS_LOST - ", w.windowCaption); 1319 break; 1320 default: 1321 break; 1322 } 1323 break; 1324 } 1325 case SDL_KEYDOWN: 1326 SDLWindow w = getWindow(event.key.windowID); 1327 if (w && !w.hasVisibleModalChild()) { 1328 w.processKeyEvent(KeyAction.KeyDown, event.key.keysym.sym, event.key.keysym.mod); 1329 SDL_StartTextInput(); 1330 } 1331 break; 1332 case SDL_KEYUP: 1333 SDLWindow w = getWindow(event.key.windowID); 1334 if (w) { 1335 if (w.hasVisibleModalChild()) 1336 w.restoreModalChilds(); 1337 else 1338 w.processKeyEvent(KeyAction.KeyUp, event.key.keysym.sym, event.key.keysym.mod); 1339 } 1340 break; 1341 case SDL_TEXTEDITING: 1342 debug(DebugSDL) Log.d("SDL_TEXTEDITING"); 1343 break; 1344 case SDL_TEXTINPUT: 1345 debug(DebugSDL) Log.d("SDL_TEXTINPUT"); 1346 SDLWindow w = getWindow(event.text.windowID); 1347 if (w && !w.hasVisibleModalChild()) { 1348 w.processTextInput(event.text.text.ptr); 1349 } 1350 break; 1351 case SDL_MOUSEMOTION: 1352 SDLWindow w = getWindow(event.motion.windowID); 1353 if (w && !w.hasVisibleModalChild()) { 1354 w.processMouseEvent(MouseAction.Move, 0, event.motion.state, event.motion.x, event.motion.y); 1355 } 1356 break; 1357 case SDL_MOUSEBUTTONDOWN: 1358 SDLWindow w = getWindow(event.button.windowID); 1359 if (w && !w.hasVisibleModalChild()) { 1360 w.processMouseEvent(MouseAction.ButtonDown, event.button.button, event.button.state, event.button.x, event.button.y); 1361 } 1362 break; 1363 case SDL_MOUSEBUTTONUP: 1364 SDLWindow w = getWindow(event.button.windowID); 1365 if (w) { 1366 if (w.hasVisibleModalChild()) 1367 w.restoreModalChilds(); 1368 else 1369 w.processMouseEvent(MouseAction.ButtonUp, event.button.button, event.button.state, event.button.x, event.button.y); 1370 } 1371 break; 1372 case SDL_MOUSEWHEEL: 1373 SDLWindow w = getWindow(event.wheel.windowID); 1374 if (w && !w.hasVisibleModalChild()) { 1375 debug(DebugSDL) Log.d("SDL_MOUSEWHEEL x=", event.wheel.x, " y=", event.wheel.y); 1376 w.processMouseEvent(MouseAction.Wheel, 0, 0, event.wheel.x, event.wheel.y); 1377 } 1378 break; 1379 default: 1380 // not supported event 1381 if (event.type == USER_EVENT_ID) { 1382 SDLWindow w = getWindow(event.user.windowID); 1383 if (w) { 1384 w.handlePostedEvent(cast(uint)event.user.code); 1385 } 1386 } else if (event.type == TIMER_EVENT_ID) { 1387 SDLWindow w = getWindow(event.user.windowID); 1388 if (w) { 1389 w.handleTimer(cast(uint)event.user.code); 1390 } 1391 } else if (event.type == WINDOW_CLOSE_EVENT_ID) { 1392 SDLWindow windowToClose = getWindow(event.user.windowID); 1393 if(windowToClose) { 1394 if (windowToClose.windowId in _windowMap) { 1395 Log.i("Platform.closeWindow()"); 1396 _windowMap.remove(windowToClose.windowId); 1397 Log.i("windowMap.length=", _windowMap.length); 1398 destroy(windowToClose); 1399 } 1400 } 1401 } 1402 break; 1403 } 1404 if (_windowMap.length == 0) { 1405 SDL_Quit(); 1406 quit = true; 1407 } 1408 } 1409 } 1410 Log.i("exiting message loop"); 1411 return 0; 1412 } 1413 1414 /// retrieves text from clipboard (when mouseBuffer == true, use mouse selection clipboard - under linux) 1415 override dstring getClipboardText(bool mouseBuffer = false) { 1416 if (!SDL_HasClipboardText()) 1417 return ""d; 1418 char * txt = SDL_GetClipboardText(); 1419 if (!txt) 1420 return ""d; 1421 string s = fromStringz(txt).dup; 1422 SDL_free(txt); 1423 return toUTF32(s); 1424 } 1425 1426 /// sets text to clipboard (when mouseBuffer == true, use mouse selection clipboard - under linux) 1427 override void setClipboardText(dstring text, bool mouseBuffer = false) { 1428 string s = toUTF8(text); 1429 SDL_SetClipboardText(s.toStringz); 1430 } 1431 } 1432 1433 version (Windows) { 1434 import core.sys.windows.windows; 1435 import dlangui.platforms.windows.win32fonts; 1436 pragma(lib, "gdi32.lib"); 1437 pragma(lib, "user32.lib"); 1438 extern(Windows) 1439 int DLANGUIWinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, 1440 LPSTR lpCmdLine, int nCmdShow) 1441 { 1442 int result; 1443 1444 try 1445 { 1446 Runtime.initialize(); 1447 1448 // call SetProcessDPIAware to support HI DPI - fix by Kapps 1449 auto ulib = LoadLibraryA("user32.dll"); 1450 alias SetProcessDPIAwareFunc = int function(); 1451 auto setDpiFunc = cast(SetProcessDPIAwareFunc)GetProcAddress(ulib, "SetProcessDPIAware"); 1452 if(setDpiFunc) // Should never fail, but just in case... 1453 setDpiFunc(); 1454 1455 // Get screen DPI 1456 HDC dc = CreateCompatibleDC(NULL); 1457 SCREEN_DPI = GetDeviceCaps(dc, LOGPIXELSY); 1458 DeleteObject(dc); 1459 1460 //SCREEN_DPI = 96 * 3 / 2; 1461 1462 result = myWinMain(hInstance, hPrevInstance, lpCmdLine, nCmdShow); 1463 Log.i("calling Runtime.terminate()"); 1464 // commented out to fix hanging runtime.terminate when there are background threads 1465 Runtime.terminate(); 1466 } 1467 catch (Throwable e) // catch any uncaught exceptions 1468 { 1469 MessageBoxW(null, toUTF16z(e.toString ~ "\nStack trace:\n" ~ defaultTraceHandler.toString), "Error", 1470 MB_OK | MB_ICONEXCLAMATION); 1471 result = 0; // failed 1472 } 1473 1474 return result; 1475 } 1476 1477 string[] splitCmdLine(string line) { 1478 string[] res; 1479 int start = 0; 1480 bool insideQuotes = false; 1481 for (int i = 0; i <= line.length; i++) { 1482 char ch = i < line.length ? line[i] : 0; 1483 if (ch == '\"') { 1484 if (insideQuotes) { 1485 if (i > start) 1486 res ~= line[start .. i]; 1487 start = i + 1; 1488 insideQuotes = false; 1489 } else { 1490 insideQuotes = true; 1491 start = i + 1; 1492 } 1493 } else if (!insideQuotes && (ch == ' ' || ch == '\t' || ch == 0)) { 1494 if (i > start) { 1495 res ~= line[start .. i]; 1496 } 1497 start = i + 1; 1498 } 1499 } 1500 return res; 1501 } 1502 1503 int myWinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int iCmdShow) 1504 { 1505 //Log.d("myWinMain()"); 1506 string basePath = exePath(); 1507 //Log.i("Current executable: ", exePath()); 1508 string cmdline = fromStringz(lpCmdLine).dup; 1509 //Log.i("Command line: ", cmdline); 1510 string[] args = splitCmdLine(cmdline); 1511 //Log.i("Command line params: ", args); 1512 1513 return sdlmain(args); 1514 } 1515 } else { 1516 1517 extern(C) int DLANGUImain(string[] args) 1518 { 1519 return sdlmain(args); 1520 } 1521 } 1522 1523 int sdlmain(string[] args) { 1524 1525 initLogs(); 1526 1527 if (!initFontManager()) { 1528 Log.e("******************************************************************"); 1529 Log.e("No font files found!!!"); 1530 Log.e("Currently, only hardcoded font paths implemented."); 1531 Log.e("Probably you can modify sdlapp.d to add some fonts for your system."); 1532 Log.e("TODO: use fontconfig"); 1533 Log.e("******************************************************************"); 1534 assert(false); 1535 } 1536 initResourceManagers(); 1537 1538 version (Windows) { 1539 DOUBLE_CLICK_THRESHOLD_MS = GetDoubleClickTime(); 1540 } 1541 1542 currentTheme = createDefaultTheme(); 1543 1544 try { 1545 DerelictSDL2.missingSymbolCallback = &missingSymFunc; 1546 // Load the SDL 2 library. 1547 DerelictSDL2.load(); 1548 } catch (Exception e) { 1549 Log.e("Cannot load SDL2 library", e); 1550 return 1; 1551 } 1552 1553 static if (ENABLE_OPENGL) { 1554 try { 1555 DerelictGL3.missingSymbolCallback = &gl3MissingSymFunc; 1556 DerelictGL3.load(); 1557 DerelictGL.missingSymbolCallback = &gl3MissingSymFunc; 1558 DerelictGL.load(); 1559 _enableOpengl = true; 1560 } catch (Exception e) { 1561 Log.e("Cannot load opengl library", e); 1562 } 1563 } 1564 1565 SDL_DisplayMode displayMode; 1566 if (SDL_Init(SDL_INIT_VIDEO|SDL_INIT_TIMER|SDL_INIT_EVENTS|SDL_INIT_NOPARACHUTE) != 0) { 1567 Log.e("Cannot init SDL2: ", SDL_GetError().to!string()); 1568 return 2; 1569 } 1570 scope(exit)SDL_Quit(); 1571 1572 USER_EVENT_ID = SDL_RegisterEvents(1); 1573 TIMER_EVENT_ID = SDL_RegisterEvents(1); 1574 WINDOW_CLOSE_EVENT_ID = SDL_RegisterEvents(1); 1575 1576 int request = SDL_GetDesktopDisplayMode(0, &displayMode); 1577 1578 static if (ENABLE_OPENGL) { 1579 // Set OpenGL attributes 1580 SDL_GL_SetAttribute(SDL_GL_DOUBLEBUFFER, 1); 1581 SDL_GL_SetAttribute(SDL_GL_DEPTH_SIZE, 24); 1582 // Share textures between contexts 1583 SDL_GL_SetAttribute(SDL_GL_SHARE_WITH_CURRENT_CONTEXT, 1); 1584 } 1585 1586 auto sdl = new SDLPlatform; 1587 1588 Platform.setInstance(sdl); 1589 Platform.instance.uiTheme = "theme_default"; 1590 1591 int res = 0; 1592 1593 version (unittest) { 1594 } else { 1595 res = UIAppMain(args); 1596 } 1597 1598 //Log.e("Widget instance count after UIAppMain: ", Widget.instanceCount()); 1599 1600 Log.d("Destroying SDL platform"); 1601 Platform.setInstance(null); 1602 static if (ENABLE_OPENGL) 1603 glNoContext = true; 1604 1605 releaseResourcesOnAppExit(); 1606 1607 Log.d("Exiting main"); 1608 APP_IS_SHUTTING_DOWN = true; 1609 1610 return res; 1611 }