1 /** 2 * Copyright © Novelate 2020 3 * License: MIT (https://github.com/Novelate/NovelateEngine/blob/master/LICENSE) 4 * Author: Jacob Jensen (bausshf) 5 * Website: https://novelate.com/ 6 * ------ 7 * Novelate is a free and open-source visual novel engine and framework written in the D programming language. 8 * It can be used freely for both personal and commercial projects. 9 * ------ 10 * Module Description: 11 * This module is used for external bindings to SDL. Types and functions should only be exposed through internal types ex. SDL_Texture is only referenced through ex. ExternalImage. 12 * This module must always have public members exposed equally to the module: novelate.external.dsfmlbinding 13 */ 14 module novelate.sdlbinding; 15 16 import novelate.buildstate; 17 18 static if (useSDL && useDerelict) 19 { 20 public import std.string : toStringz; 21 import std.conv : to; 22 import std.array : replace, split; 23 import std.traits : isSomeString; 24 import std.process : thisThreadID; 25 26 import novelate.external.core; 27 import novelate.config; 28 import novelate.ui.textwrap; 29 30 private 31 { 32 import derelict.sdl2.sdl; 33 import derelict.sdl2.image; 34 import derelict.sdl2.mixer; 35 import derelict.sdl2.ttf; 36 import derelict.sdl2.net; 37 } 38 39 /// Initialization of the external binding. 40 void initExternalBinding() 41 { 42 DerelictSDL2.load(); 43 DerelictSDL2Image.load(); 44 DerelictSDL2Mixer.load(); 45 DerelictSDL2ttf.load(); 46 DerelictSDL2Net.load(); 47 48 TTF_Init(); 49 } 50 51 /// Paint -> SDL_Color 52 SDL_Color toNative(Paint paint) 53 { 54 return SDL_Color(paint.r, paint.g, paint.b, paint.a); 55 } 56 57 /// SDL_Color -> Paint 58 Paint fromNative(SDL_Color color) 59 { 60 return Paint(color.r, color.g, color.b, color.a); 61 } 62 63 /// KeyboardKey -> SDL_Keycode 64 SDL_Keycode toNative(KeyboardKey key) 65 { 66 switch (key) 67 { 68 case KeyboardKey.num0: 69 return SDL_Keycode.SDLK_0; 70 case KeyboardKey.num1: 71 return SDL_Keycode.SDLK_1; 72 case KeyboardKey.num2: 73 return SDL_Keycode.SDLK_2; 74 case KeyboardKey.num3: 75 return SDL_Keycode.SDLK_3; 76 case KeyboardKey.num4: 77 return SDL_Keycode.SDLK_4; 78 case KeyboardKey.num5: 79 return SDL_Keycode.SDLK_5; 80 case KeyboardKey.num6: 81 return SDL_Keycode.SDLK_6; 82 case KeyboardKey.num7: 83 return SDL_Keycode.SDLK_7; 84 case KeyboardKey.num8: 85 return SDL_Keycode.SDLK_8; 86 case KeyboardKey.num9: 87 return SDL_Keycode.SDLK_9; 88 case KeyboardKey.LControl: 89 return SDL_Keycode.SDLK_LCTRL; 90 case KeyboardKey.LShift: 91 return SDL_Keycode.SDLK_LSHIFT; 92 case KeyboardKey.LAlt: 93 return SDL_Keycode.SDLK_LALT; 94 case KeyboardKey.RControl: 95 return SDL_Keycode.SDLK_RCTRL; 96 case KeyboardKey.RShift: 97 return SDL_Keycode.SDLK_RSHIFT; 98 case KeyboardKey.RAlt: 99 return SDL_Keycode.SDLK_RALT; 100 case KeyboardKey.escape: 101 return SDL_Keycode.SDLK_ESCAPE; 102 case KeyboardKey.returnKey: 103 return SDL_Keycode.SDLK_RETURN; 104 case KeyboardKey.tab: 105 return SDL_Keycode.SDLK_TAB; 106 107 default: return cast(SDL_Keycode)'\0'; 108 } 109 } 110 111 /// SDL_Keycode -> KeyboardKey 112 KeyboardKey fromNative(SDL_Keycode keyCode) 113 { 114 switch (keyCode) 115 { 116 case SDLK_0: 117 return KeyboardKey.num0; 118 case SDLK_1: 119 return KeyboardKey.num1; 120 case SDLK_2: 121 return KeyboardKey.num2; 122 case SDLK_3: 123 return KeyboardKey.num3; 124 case SDLK_4: 125 return KeyboardKey.num4; 126 case SDLK_5: 127 return KeyboardKey.num5; 128 case SDLK_6: 129 return KeyboardKey.num6; 130 case SDLK_7: 131 return KeyboardKey.num7; 132 case SDLK_8: 133 return KeyboardKey.num8; 134 case SDLK_9: 135 return KeyboardKey.num9; 136 case SDLK_LCTRL: 137 return KeyboardKey.LControl; 138 case SDLK_LSHIFT: 139 return KeyboardKey.LShift; 140 case SDLK_LALT: 141 return KeyboardKey.LAlt; 142 case SDLK_RCTRL: 143 return KeyboardKey.RControl; 144 case SDLK_RSHIFT: 145 return KeyboardKey.RShift; 146 case SDLK_RALT: 147 return KeyboardKey.RAlt; 148 case SDLK_ESCAPE: 149 return KeyboardKey.escape; 150 case SDLK_RETURN: 151 return KeyboardKey.returnKey; 152 case SDLK_TAB: 153 return KeyboardKey.tab; 154 155 default: return KeyboardKey.unknown; 156 } 157 } 158 159 /// button -> SDL_D_MouseButton 160 SDL_D_MouseButton toNative(MouseButton button) 161 { 162 switch (button) 163 { 164 case MouseButton.left: 165 return SDL_D_MouseButton.SDL_BUTTON_LEFT; 166 167 case MouseButton.middle: 168 return SDL_D_MouseButton.SDL_BUTTON_MIDDLE; 169 170 case MouseButton.right: 171 return SDL_D_MouseButton.SDL_BUTTON_RIGHT; 172 173 case MouseButton.extraButton1: 174 return SDL_D_MouseButton.SDL_BUTTON_X1; 175 176 case MouseButton.extraButton2: 177 return SDL_D_MouseButton.SDL_BUTTON_X2; 178 179 default: return cast(SDL_D_MouseButton)0; 180 } 181 } 182 183 /// SDL_D_MouseButton -> MouseButton 184 MouseButton fromNative(SDL_D_MouseButton button) 185 { 186 switch (button) 187 { 188 case SDL_D_MouseButton.SDL_BUTTON_LEFT: 189 return MouseButton.left; 190 191 case SDL_D_MouseButton.SDL_BUTTON_MIDDLE: 192 return MouseButton.middle; 193 194 case SDL_D_MouseButton.SDL_BUTTON_RIGHT: 195 return MouseButton.right; 196 197 case SDL_D_MouseButton.SDL_BUTTON_X1: 198 return MouseButton.extraButton1; 199 200 case SDL_D_MouseButton.SDL_BUTTON_X2: 201 return MouseButton.extraButton2; 202 203 default: return cast(MouseButton)-1; 204 } 205 } 206 207 /// ExternalEventType -> SDL_EventType 208 SDL_EventType toNative(ExternalEventType eventType) 209 { 210 switch (eventType) 211 { 212 case ExternalEventType.closed: 213 return SDL_EventType.SDL_QUIT; 214 215 case ExternalEventType.keyPressed: 216 return SDL_EventType.SDL_KEYDOWN; 217 218 case ExternalEventType.keyReleased: 219 return SDL_EventType.SDL_KEYUP; 220 221 case ExternalEventType.mouseButtonPressed: 222 return SDL_EventType.SDL_MOUSEBUTTONDOWN; 223 224 case ExternalEventType.mouseButtonReleased: 225 return SDL_EventType.SDL_MOUSEBUTTONUP; 226 227 case ExternalEventType.mouseMoved: 228 return SDL_EventType.SDL_MOUSEMOTION; 229 230 default: return cast(SDL_EventType)-1; 231 } 232 } 233 234 /// SDL_EventType -> ExternalEventType 235 ExternalEventType fromNative(SDL_EventType eventType) 236 { 237 switch (eventType) 238 { 239 case SDL_EventType.SDL_QUIT: 240 return ExternalEventType.closed; 241 242 case SDL_EventType.SDL_KEYDOWN: 243 return ExternalEventType.keyPressed; 244 245 case SDL_EventType.SDL_KEYUP: 246 return ExternalEventType.keyReleased; 247 248 case SDL_EventType.SDL_MOUSEBUTTONDOWN: 249 return ExternalEventType.mouseButtonPressed; 250 251 case SDL_EventType.SDL_MOUSEBUTTONUP: 252 return ExternalEventType.mouseButtonReleased; 253 254 case SDL_EventType.SDL_MOUSEMOTION: 255 return ExternalEventType.mouseMoved; 256 257 default: return cast(ExternalEventType)-1; 258 } 259 } 260 261 /// IExternalDrawableComponent 262 interface IExternalDrawableComponent 263 { 264 /// draw() 265 void draw(ExternalWindow window); 266 } 267 268 /// The window. 269 private SDL_Window* window; 270 /// The renderer. 271 private SDL_Renderer* renderer; 272 /// The external window. 273 private ExternalWindow externalWindow; 274 275 /// A SDL Exception. 276 private final class SDLException : Exception 277 { 278 public: 279 /** 280 * Creates a new SDL exception. 281 * Params: 282 * message = The message. 283 * fn = The file. 284 * ln = The line. 285 */ 286 this(string message, string fn = __FILE__, size_t ln = __LINE__) @safe 287 { 288 super(message, fn, ln); 289 } 290 } 291 292 /// reportError() 293 private void reportError(string errorMessageFun = "SDL_GetError", T = string)(T extraData = null) 294 if (isSomeString!T) 295 { 296 mixin("T message = to!T(" ~ errorMessageFun ~ "());"); 297 298 if (extraData && extraData.length) 299 { 300 message = "Data: " ~ extraData ~ "\r\n---\r\n" ~ message; 301 } 302 303 throw new SDLException(to!string(message)); 304 } 305 306 // ExternalImage 307 final class ExternalImage : IExternalDrawableComponent 308 { 309 private: 310 /// _image 311 SDL_Texture* _image; 312 /// _width 313 int _width; 314 /// _height 315 int _height; 316 /// _rect 317 SDL_Rect _rect; 318 /// _color 319 Paint _color; 320 /// _path 321 string _path; 322 /// _scale 323 FloatVector _scale; 324 325 /// loadTexture 326 SDL_Texture* loadTexture(string path) 327 { 328 SDL_Texture* texture = null; 329 330 SDL_Surface* surface = IMG_Load(path.toStringz); 331 332 if (surface) 333 { 334 texture = SDL_CreateTextureFromSurface(renderer, surface); 335 336 if (texture) 337 { 338 SDL_FreeSurface(surface); 339 } 340 else 341 { 342 reportError(path); 343 } 344 } 345 else 346 { 347 reportError(path); 348 } 349 350 return texture; 351 } 352 353 public: 354 /// this() 355 this() 356 { 357 } 358 359 /// loadFromFile() 360 void loadFromFile(string path) 361 { 362 _path = path; 363 364 _image = loadTexture(path); 365 366 if (SDL_QueryTexture(_image, null, null, &_width, &_height) != 0) 367 { 368 reportError(_path); 369 } 370 371 _rect.x = 0; 372 _rect.y = 0; 373 _rect.w = _width; 374 _rect.h = _height; 375 } 376 377 @property 378 { 379 /// Gets bounds. 380 FloatVector bounds() 381 { 382 return FloatVector(_width, _height); 383 } 384 385 /// Gets size. 386 UintVector size() 387 { 388 return UintVector(cast(uint)_width, cast(uint)_height); 389 } 390 391 /// Gets position. 392 FloatVector position() 393 { 394 return FloatVector(_rect.x, _rect.y); 395 } 396 397 /// Sets position. 398 void position(FloatVector newPosition) 399 { 400 _rect.x = cast(int)newPosition.x; 401 _rect.y = cast(int)newPosition.y; 402 } 403 404 /// Gets color. 405 Paint color() 406 { 407 return _color; 408 } 409 410 /// Sets color. 411 void color(Paint newColor) 412 { 413 _color = newColor; 414 415 if (!_image) 416 { 417 return; 418 } 419 420 if (SDL_SetTextureBlendMode(_image, SDL_BLENDMODE_BLEND) != 0) 421 { 422 reportError(_path); 423 } 424 425 if (SDL_SetTextureAlphaMod(_image, _color.a) != 0) 426 { 427 reportError(_path); 428 } 429 } 430 431 /// Gets scale. 432 FloatVector scale() 433 { 434 return _scale; 435 } 436 437 /// Sets scale. 438 void scale(FloatVector newScale) 439 { 440 _scale = newScale; 441 442 _rect.w = cast(int)(((_scale.x * 100) / 100) * cast(float)_width); 443 _rect.h = cast(int)(((_scale.y * 100) / 100) * cast(float)_height); 444 } 445 } 446 447 /// draw() 448 void draw(ExternalWindow window) 449 { 450 if (_image) 451 { 452 if (SDL_RenderCopy(renderer, _image, null, &_rect) != 0) 453 { 454 reportError(_path); 455 } 456 } 457 } 458 459 static if (isManualMemory) 460 { 461 /// clean() 462 void clean() 463 { 464 if (_image) 465 { 466 SDL_DestroyTexture(_image); 467 468 _image = null; 469 } 470 } 471 } 472 } 473 474 /// ExternalWindow. 475 final class ExternalWindow 476 { 477 private: 478 /// _fps. 479 uint _fps; 480 /// _lastTicks 481 uint _lastTicks; 482 483 public: 484 /// this() 485 this() 486 { 487 _lastTicks = SDL_GetTicks(); 488 } 489 490 @property 491 { 492 /// Gets isOpen. 493 bool isOpen() { return window && renderer; } 494 495 /// Gets fps. 496 uint fps() { return _fps; } 497 498 /// Sets fps. 499 void fps(uint newFps) 500 { 501 _fps = newFps; 502 } 503 504 /// Gets canUpdate. 505 bool canUpdate() 506 { 507 auto ticks = SDL_GetTicks(); 508 size_t delta = ticks - _lastTicks; 509 510 auto deltaFps = cast(size_t)(1000 / cast(double)_fps); 511 512 if (delta > deltaFps) 513 { 514 _lastTicks = ticks; 515 516 return true; 517 } 518 519 return false; 520 } 521 } 522 523 /// close() 524 void close() 525 { 526 clean(); 527 } 528 529 /// render() 530 void render() 531 { 532 if (renderer) 533 { 534 SDL_RenderPresent(renderer); 535 } 536 } 537 538 /// clear() 539 void clear(Paint paint) 540 { 541 if (renderer) 542 { 543 if (SDL_RenderClear(renderer) != 0) 544 { 545 reportError(); 546 } 547 } 548 } 549 550 /// processEvents() 551 bool processEvents(ExternalEventManager eventManager) 552 { 553 SDL_Event e; 554 while (SDL_PollEvent(&e)) 555 { 556 switch (e.type) 557 { 558 case SDL_EventType.SDL_QUIT: 559 eventManager.fireEvent(e.type.fromNative()); 560 return false; 561 562 case SDL_EventType.SDL_KEYDOWN: 563 case SDL_EventType.SDL_KEYUP: 564 auto keysym = e.key.keysym.sym.fromNative(); 565 ExternalEventState._keyEvent.code = keysym; 566 break; 567 568 case SDL_EventType.SDL_MOUSEBUTTONDOWN: 569 case SDL_EventType.SDL_MOUSEBUTTONUP: 570 ExternalEventState._mouseButtonEvent.button = fromNative(cast(SDL_D_MouseButton)e.button.button); 571 break; 572 573 case SDL_EventType.SDL_MOUSEMOTION: 574 ExternalEventState._mouseMoveEvent.x = cast(int)e.motion.x; 575 ExternalEventState._mouseMoveEvent.y = cast(int)e.motion.y; 576 break; 577 578 default: break; 579 } 580 581 eventManager.fireEvent(e.type.fromNative()); 582 } 583 584 return true; 585 } 586 587 static if (isManualMemory) 588 { 589 /// clean() 590 void clean() 591 { 592 if (renderer) 593 { 594 SDL_DestroyRenderer(renderer); 595 596 renderer = null; 597 } 598 599 if (window) 600 { 601 SDL_DestroyWindow(window); 602 603 window = null; 604 } 605 } 606 } 607 608 static: 609 /// create() 610 ExternalWindow create(string title, size_t width, size_t height, bool fullScreen) 611 { 612 if (externalWindow) 613 { 614 return externalWindow; 615 } 616 617 if (fullScreen) 618 { 619 window = SDL_CreateWindow( 620 title.toStringz, 621 SDL_WINDOWPOS_UNDEFINED, 622 SDL_WINDOWPOS_UNDEFINED, 623 cast(int)width, 624 cast(int)height, 625 SDL_WINDOW_OPENGL | SDL_WINDOW_FULLSCREEN 626 ); 627 } 628 else 629 { 630 window = SDL_CreateWindow( 631 title.toStringz, 632 SDL_WINDOWPOS_UNDEFINED, 633 SDL_WINDOWPOS_UNDEFINED, 634 cast(int)width, 635 cast(int)height, 636 SDL_WINDOW_OPENGL 637 ); 638 } 639 640 renderer = SDL_CreateRenderer(window, -1, SDL_RENDERER_ACCELERATED); 641 642 SDL_SetRenderDrawBlendMode(renderer, SDL_BLENDMODE_BLEND); 643 644 auto window = new ExternalWindow; 645 return window; 646 } 647 } 648 649 /// ExternalRectangleShape 650 final class ExternalRectangleShape : IExternalDrawableComponent 651 { 652 private: 653 /// _rect 654 SDL_Rect _rect; 655 /// _fillColor 656 Paint _fillColor; 657 658 public: 659 final: 660 /// this() 661 this(FloatVector size) 662 { 663 _rect.w = cast(int)size.x; 664 _rect.h = cast(int)size.y; 665 } 666 667 @property 668 { 669 /// Gets fillColor 670 Paint fillColor() 671 { 672 return _fillColor; 673 } 674 675 /// Sets fillColor 676 void fillColor(Paint newColor) 677 { 678 _fillColor = newColor; 679 } 680 681 /// Gets position 682 FloatVector position() 683 { 684 return FloatVector(_rect.x, _rect.y); 685 } 686 687 /// Sets position 688 void position(FloatVector newPosition) 689 { 690 _rect.x = cast(int)newPosition.x; 691 _rect.y = cast(int)newPosition.y; 692 } 693 } 694 695 /// draw() 696 void draw(ExternalWindow window) 697 { 698 if (SDL_SetRenderDrawColor(renderer, _fillColor.r, _fillColor.g, _fillColor.b, _fillColor.a) != 0) 699 { 700 reportError(); 701 } 702 703 if (SDL_RenderFillRect(renderer, &_rect) != 0) 704 { 705 reportError(); 706 } 707 } 708 709 static if (isManualMemory) 710 { 711 /// clean() 712 void clean() 713 { 714 } 715 } 716 } 717 718 /// ExternalText 719 final class ExternalText : IExternalDrawableComponent 720 { 721 /// SDL_TextEntry 722 private class SDL_TextEntry 723 { 724 /// _texture 725 SDL_Texture* _texture; 726 /// _rect 727 SDL_Rect _rect; 728 } 729 730 private: 731 /// _text 732 dstring _text; 733 /// _color 734 Paint _color; 735 /// _font 736 ExternalFont _font; 737 /// _fontSize 738 uint _fontSize; 739 /// _position 740 FloatVector _position; 741 /// _textWidth 742 int _textWidth; 743 /// _textHeight 744 int _textHeight; 745 /// _entries 746 SDL_TextEntry[] _entries; 747 /// hasGottenPosition 748 bool hasGottenPosition; 749 750 /// updateText() 751 void updateText() 752 { 753 clean(); 754 755 if (!_font || !_text || !renderer || !_font._fontPath || !_fontSize) 756 { 757 return; 758 } 759 760 dstring[] lines = _text.replace("\r", "").split("\n"); 761 762 _textHeight = 0; 763 _textWidth = 0; 764 765 _entries = []; 766 767 foreach (l; lines) 768 { 769 auto line = l; 770 771 if (!line || !line.length) 772 { 773 line = " "; // Weird workaround to TTF not handling empty strings properly. 774 } 775 776 auto textSurface = TTF_RenderUTF8_Blended(getFont(_font._fontPath, _fontSize), to!string(line).toStringz, _color.toNative()); 777 778 if (!textSurface) 779 { 780 reportError(_text); 781 } 782 783 auto text = new SDL_TextEntry; 784 text._texture = SDL_CreateTextureFromSurface(renderer, textSurface); 785 786 if (!text._texture) 787 { 788 reportError(_text); 789 } 790 791 _textWidth = textSurface.w > _textWidth ? textSurface.w : _textWidth; 792 793 _textHeight += textSurface.h; 794 795 text._rect.x = cast(int)_position.x; 796 text._rect.y = cast(int)(_position.y + (_textHeight - textSurface.h)); 797 text._rect.w = textSurface.w; 798 text._rect.h = textSurface.h; 799 800 if (textSurface) 801 { 802 SDL_FreeSurface(textSurface); 803 } 804 805 _entries ~= text; 806 } 807 } 808 809 public: 810 final: 811 /// this() 812 this() 813 { 814 } 815 816 @property 817 { 818 /// Gets bounds. 819 FloatVector bounds() 820 { 821 return FloatVector(_textWidth, _textHeight); 822 } 823 824 /// Gets position. 825 FloatVector position() 826 { 827 return _position; 828 } 829 830 /// Sets position. 831 void position(FloatVector newPosition) 832 { 833 _position = newPosition; 834 835 if (_entries) 836 { 837 foreach (texture; _entries) 838 { 839 texture._rect.x = cast(int)_position.x; 840 texture._rect.y = cast(int)_position.y; 841 } 842 } 843 } 844 } 845 846 /// setFont() 847 void setFont(ExternalFont font) 848 { 849 _font = font; 850 851 updateText(); 852 } 853 854 /// setString() 855 void setString(dstring text) 856 { 857 _text = text; 858 859 updateText(); 860 } 861 862 /// setCharacterSize() 863 void setCharacterSize(size_t characterSize) 864 { 865 _fontSize = cast(uint)characterSize; 866 867 updateText(); 868 } 869 870 /// setColor() 871 void setColor(Paint newColor) 872 { 873 _color = newColor; 874 875 updateText(); 876 } 877 878 /// draw() 879 void draw(ExternalWindow window) 880 { 881 if (_entries) 882 { 883 if (!hasGottenPosition) 884 { 885 position = _position; 886 887 hasGottenPosition = true; 888 } 889 890 foreach (texture; _entries) 891 { 892 if (SDL_RenderCopy(renderer, texture._texture, null, &texture._rect) != 0) 893 { 894 reportError(_text); 895 } 896 } 897 } 898 } 899 900 static if (isManualMemory) 901 { 902 /// clean() 903 void clean() 904 { 905 if (_entries) 906 { 907 foreach (texture; _entries) 908 { 909 SDL_DestroyTexture(texture._texture); 910 } 911 } 912 } 913 } 914 } 915 916 /// TTF_FontPtr 917 private alias TTF_FontPtr = TTF_Font*; 918 919 /// _fonts 920 private TTF_FontPtr[string] _fonts; 921 922 /// getFont() 923 private TTF_FontPtr getFont(string path, size_t size) 924 { 925 auto key = path ~ "_" ~ to!string(size); 926 927 auto font = _fonts.get(key, cast(TTF_FontPtr)null); 928 929 if (!font) 930 { 931 font = TTF_OpenFont(path.toStringz, cast(uint)size); 932 933 if (!font) 934 { 935 return null; 936 } 937 938 _fonts[key] = font; 939 } 940 941 return font; 942 } 943 944 /// ExternalFont 945 final class ExternalFont 946 { 947 private: 948 /// _fontPath 949 string _fontPath; 950 951 public: 952 final: 953 /// this() 954 this() 955 { 956 } 957 958 /// loadFromFile() 959 void loadFromFile(string path) 960 { 961 if (_fontPath) 962 { 963 return; 964 } 965 966 _fontPath = path; 967 } 968 969 static if (isManualMemory) 970 { 971 /// clean() 972 void clean() 973 { 974 } 975 } 976 } 977 978 /// ExternalMusic 979 final class ExternalMusic 980 { 981 private: 982 /// _music 983 Mix_Music* _music; 984 985 public: 986 final: 987 /// this() 988 this() 989 { 990 } 991 992 @property 993 { 994 /// Gets looping 995 bool looping() 996 { 997 return true; 998 } 999 1000 /// Sets looping 1001 void looping(bool isLooping) 1002 { 1003 // Do nothing ... 1004 } 1005 } 1006 1007 /// openFromFile() 1008 bool openFromFile(string music) 1009 { 1010 clean(); 1011 1012 if (!music || !music.length) 1013 { 1014 return false; 1015 } 1016 1017 int flags = MIX_INIT_MP3 | MIX_INIT_OGG | MIX_INIT_FLAC | MIX_INIT_MOD; 1018 1019 int initted = Mix_Init(flags); 1020 1021 if ((initted & flags) != flags) 1022 { 1023 throw new Exception("Mix_GetError()"); 1024 } 1025 1026 if (Mix_OpenAudio(22050, AUDIO_S16SYS, 2, 640) != 0) 1027 { 1028 throw new Exception("Mix_GetError()"); 1029 } 1030 1031 _music = Mix_LoadMUS(music.toStringz); 1032 1033 if (!_music) 1034 { 1035 throw new Exception("Mix_GetError()"); 1036 } 1037 1038 return true; 1039 } 1040 1041 /// play() 1042 void play() 1043 { 1044 if (Mix_PlayMusic(_music, 1) != 0) 1045 { 1046 throw new Exception("Mix_GetError()"); 1047 } 1048 } 1049 1050 /// pause() 1051 void pause() 1052 { 1053 Mix_PauseMusic(); 1054 } 1055 1056 /// stop() 1057 void stop() 1058 { 1059 Mix_HaltMusic(); 1060 } 1061 1062 static if (isManualMemory) 1063 { 1064 /// clean 1065 void clean() 1066 { 1067 if (_music) 1068 { 1069 Mix_FreeMusic(_music); 1070 1071 _music = null; 1072 } 1073 } 1074 } 1075 } 1076 1077 /// quit() 1078 void quit() 1079 { 1080 if (externalWindow) 1081 { 1082 externalWindow.clean(); 1083 } 1084 1085 SDL_Quit(); 1086 } 1087 }