1 /++Authors: meatRay+/ 2 module meatbox.window; 3 4 public import derelict.opengl3.gl; 5 public import derelict.opengl3.gl3; 6 import derelict.sdl2.sdl; 7 import core.thread; 8 9 import meatbox.keyboard; 10 import meatbox.mouse; 11 12 static this() 13 { 14 DerelictGL3.load(); 15 DerelictGL.load(); 16 DerelictSDL2.load(); 17 } 18 19 /++ Encapsulates state-based OpenGL and SDL2 into something much more friendly.+/ 20 public class Window 21 { 22 SDL_Window* _window; 23 SDL_GLContext _context; 24 SDL_Event _wndwEvnt; 25 Thread _updateThread; 26 Keyboard _keyboard; 27 bool _running; 28 int _width, _height; 29 uint _mspf =16, _mspu =16; 30 31 void setSize( int width, int height ) 32 { 33 this._width =width; 34 this._height =height; 35 } 36 void runUpdate() 37 { 38 while( this._running ) 39 { 40 this.update(); 41 Thread.sleep( dur!("msecs")( _mspu ) ); 42 } 43 } 44 45 protected: 46 /++ Returns: Renders made each second.+/ 47 uint framesPerSecond() @property const 48 { return 1000 /_mspf; } 49 /++ Returns: Updates made each second.+/ 50 uint updatesPerSecond() @property const 51 { return 1000 /_mspu; } 52 /++ Override to specify update behaviour. 53 + Called by the Worker-thread to run non-visual updates.+/ 54 void update() 55 { this.onUpdate(); } 56 /++ 57 + Override to specify Render behaviour. 58 + Called once for each frame per second. 59 +/ 60 void render() 61 { this.onRender(); } 62 /++ Override to specify loading behaviour.+/ 63 void load() 64 { this.onLoad(); } 65 /++ Override to specify resizing behaviour.+/ 66 void resize() 67 { this.onResize(); } 68 /++ Override to specify unique actions on SDL events.+/ 69 void processEvent( SDL_Event event ) 70 {} 71 72 public: 73 /++ Returns: Reference to keyboard state.+/ 74 Keyboard keyboard() @property 75 { return this._keyboard; } 76 /++ Returns: Window width in pixels+/ 77 int width() @property const 78 { return this._width; } 79 /++ Returns: Window height in pixels+/ 80 int height() @property const 81 { return this._height; } 82 /// 83 float aspectRatio() @property const 84 { return cast(float)this._width /_height; } 85 /++ Halt running.+/ 86 void quit() 87 { this._running = false; } 88 /// 89 void run( ubyte fps, ubyte ups ) 90 { 91 this._mspu =1000 /ups; 92 run( fps ); 93 } 94 /// 95 void run( ubyte fps ) 96 { 97 this._mspf =1000 /fps; 98 run(); 99 } 100 /// 101 void run() 102 { 103 this.load(); 104 _running = true; 105 106 _updateThread = new Thread( &runUpdate ); 107 _updateThread.start(); 108 109 while( _running) 110 { 111 while( SDL_PollEvent( &_wndwEvnt ) == 1) 112 { 113 switch ( _wndwEvnt.type ) 114 { 115 case( SDL_WINDOWEVENT ): 116 if( _wndwEvnt.window.event == SDL_WINDOWEVENT_RESIZED ) 117 { 118 this.setSize( _wndwEvnt.window.data1, _wndwEvnt.window.data2 ); 119 this.resize(); 120 } 121 break; 122 case( SDL_QUIT ): 123 this._running = false; 124 break; 125 default: 126 processEvent( _wndwEvnt ); 127 break; 128 } 129 } 130 render(); 131 SDL_GL_SwapWindow( _window ); 132 Thread.sleep( dur!("msecs")( _mspf ) ); 133 } 134 _updateThread.join(); 135 } 136 /// 137 void delegate() onUpdate, onRender, onLoad, onResize; 138 /// 139 this( string title, int x, int y, int width, int height ) 140 { 141 SDL_Init( SDL_INIT_VIDEO ); 142 143 SDL_GL_SetAttribute( SDL_GL_DOUBLEBUFFER, 1 ); 144 SDL_GL_SetAttribute( SDL_GL_DEPTH_SIZE, 24 ); 145 146 import std.string: toStringz; 147 this._window =SDL_CreateWindow( toStringz(title), x, y, width, height, SDL_WINDOW_OPENGL | SDL_WINDOW_SHOWN | SDL_WINDOW_RESIZABLE ); 148 this._context =SDL_GL_CreateContext( _window ); 149 this._keyboard =new Keyboard(); 150 151 setSize( width, height ); 152 onUpdate =onRender =onLoad =(){}; 153 154 scope( failure ) 155 { destroy( this ); } 156 scope( success ) 157 { 158 DerelictGL3.reload(); 159 DerelictGL.reload(); 160 } 161 } 162 ~this() 163 { 164 SDL_GL_DeleteContext( _context ); 165 SDL_DestroyWindow( _window ); 166 SDL_Quit(); 167 } 168 }