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 }