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