1 module graphics.adapters.win32;
2 
3 version( Windows ):
4 
5 import core.dgame, core.gameobject, core.properties;
6 import graphics.graphics, graphics.adapters.adapter;
7 import utility.input, utility.output;
8 
9 import win32.windef, win32.winuser, win32.winbase;
10 import win32.wingdi : PIXELFORMATDESCRIPTOR, SetPixelFormat, SwapBuffers;
11 import derelict.opengl3.gl3, derelict.opengl3.wgl, derelict.opengl3.wglext;
12 
13 enum DWS_FULLSCREEN = WS_POPUP | WS_SYSMENU;
14 enum DWS_WINDOWED = WS_OVERLAPPED | WS_CAPTION | WS_SYSMENU;
15 
16 extern( Windows )
17 LRESULT WndProc( HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam )
18 {
19 	switch( message )
20 	{
21         case WM_CLOSE:
22         case WM_DESTROY:
23         case WM_QUIT:
24 			PostQuitMessage( 0 );
25 			break;
26 			// If key down, send it to input
27         case WM_KEYDOWN:
28 			Input.setKeyState( cast(uint)wParam, true );
29 			break;
30 			// If key up, send it to input
31         case WM_KEYUP:
32 			Input.setKeyState( cast(uint)wParam, false );
33 			break;
34 			// On Mouse Event
35         case WM_RBUTTONDOWN:
36 			Input.setKeyState( VK_RBUTTON, true );
37 			break;
38 			// On Mouse Event
39         case WM_RBUTTONUP:
40 			Input.setKeyState( VK_RBUTTON, false );
41 			break;
42 			// On Mouse Event
43         case WM_LBUTTONDOWN:
44 			Input.setKeyState( VK_LBUTTON, true );
45 			break;
46 			// On Mouse Event
47         case WM_LBUTTONUP:
48 			Input.setKeyState( VK_LBUTTON, false );
49 			break;
50 			// If no change, send to default windows handler
51         default:
52 			return DefWindowProc( hWnd, message, wParam, lParam );
53 	}
54 	return 0;
55 }
56 
57 final class Win32 : Adapter
58 {
59 public:
60 	static @property Win32 get() { return cast(Win32)Graphics.adapter; }
61 
62 	mixin Property!( "HWND", "hWnd" );
63 	mixin Property!( "HINSTANCE", "hInstance" );
64 
65 	override void initialize()
66 	{
67 		// Load opengl functions
68 		DerelictGL3.load();
69 
70 		// Setup the window
71 		screenWidth = GetSystemMetrics( SM_CXSCREEN );
72 		screenHeight = GetSystemMetrics( SM_CYSCREEN );
73 		
74 		hInstance = GetModuleHandle( null );
75 		
76 		WNDCLASSEX wcex = {
77 			WNDCLASSEX.sizeof,
78 				CS_HREDRAW | CS_VREDRAW,// | CS_OWNDC,
79 				&WndProc,
80 				0,
81 				0,
82 				hInstance,
83 				null,
84 				LoadCursor( null, IDC_ARROW ),
85 				cast(HBRUSH)( COLOR_WINDOW + 1 ),
86 				null,
87 				DGame.instance.title.ptr,
88 				null
89 		};
90 		
91 		RegisterClassEx( &wcex );
92 		openWindow( false );
93 
94 		// Setup opengl		
95 		uint formatCount;
96 		int pixelFormat;
97 		PIXELFORMATDESCRIPTOR pfd;
98 		
99 		HGLRC handle;
100 		
101 		deviceContext = GetDC( hWnd );		
102 		SetPixelFormat( deviceContext, 1, &pfd );
103 		renderContext = wglCreateContext( deviceContext );
104 		wglMakeCurrent( deviceContext, renderContext );
105 		
106 		DerelictGL3.reload();
107 		
108 		if( DerelictGL3.loadedVersion < GLVersion.GL40 )
109 		{
110 			log( OutputType.Error, "Your version of OpenGL is unsupported. Required: GL40 Yours: ", DerelictGL3.loadedVersion );
111 			//throw new Exception( "Unsupported version of OpenGL." );
112 			return;
113 		}
114 		
115 		closeWindow();
116 		openWindow();
117 		
118 		// Set attributes list
119 		const(int)[ 19 ] attributeList = [
120 			WGL_SUPPORT_OPENGL_ARB,	TRUE,						// Support for OpenGL rendering
121 			WGL_DRAW_TO_WINDOW_ARB,	TRUE,						// Support for rendering window
122 			WGL_ACCELERATION_ARB,	WGL_FULL_ACCELERATION_ARB,	// Support for hardware acceleration
123 			WGL_COLOR_BITS_ARB,		24,							// Support for 24bit color
124 			WGL_DEPTH_BITS_ARB,		24,							// Support for 24bit depth buffer
125 			WGL_DOUBLE_BUFFER_ARB,	TRUE,						// Support for double buffer
126 			WGL_SWAP_METHOD_ARB,	WGL_SWAP_EXCHANGE_ARB,		// Support for swapping buffers
127 			WGL_PIXEL_TYPE_ARB,		WGL_TYPE_RGBA_ARB,			// Support for RGBA pixel type
128 			WGL_STENCIL_BITS_ARB,	8,							// Support for 8 bit stencil buffer
129 			0													// Null terminate
130 		];
131 		
132 		// Set version to 4.0
133 		const(int)[ 5 ] versionInfo = [
134 			WGL_CONTEXT_MAJOR_VERSION_ARB, 4,
135 			WGL_CONTEXT_MINOR_VERSION_ARB, 0,
136 			0
137 		];
138 		
139 		// Get new Device Context
140 		deviceContext = GetDC( hWnd );
141 		
142 		// Query pixel format
143 		wglChoosePixelFormatARB( deviceContext, attributeList.ptr, null, 1, &pixelFormat, &formatCount );
144 		
145 		// Set the pixel format
146 		SetPixelFormat( deviceContext, pixelFormat, &pfd );
147 		
148 		// Create OpenGL rendering context
149 		renderContext = wglCreateContextAttribsARB( deviceContext, null, versionInfo.ptr );
150 		
151 		// Set current context
152 		wglMakeCurrent( deviceContext, renderContext );
153 		
154 		// Set depth buffer
155 		glClearDepth( 1.0f );
156 		
157 		// Enable depth testing
158 		glEnable( GL_DEPTH_TEST );
159 		
160 		// Enable transparency
161 		glEnable( GL_BLEND );
162 		glBlendFunc( GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA );
163 		
164 		// Set front face
165 		//glFrontFace( GL_CW );
166 		
167 		reload();
168 		
169 		glClearColor( 0.0f, 0.0f, 0.0f, 1.0f );
170 	}
171 
172 	override void shutdown()
173 	{
174 		wglMakeCurrent( null, null );
175 		wglDeleteContext( renderContext );
176 		renderContext = null;
177 		ReleaseDC( hWnd, deviceContext );
178 		deviceContext = null;
179 		closeWindow();
180 	}
181 
182 	override void resize()
183 	{
184 		LONG style = GetWindowLong( hWnd, GWL_STYLE ) & ~( DWS_FULLSCREEN | DWS_WINDOWED );
185 
186 		loadProperties();
187 
188 		if( fullscreen )
189 		{
190 			width = screenWidth;
191 			height = screenHeight;
192 			style |= DWS_FULLSCREEN;
193 		}
194 		else
195 		{
196 			style |= DWS_WINDOWED;
197 		}
198 
199 		SetWindowLong( hWnd, GWL_STYLE, style );
200 		SetWindowPos( hWnd, null, ( screenWidth - width ) / 2, ( screenHeight - height ) / 2,
201 					  width + ( 2 * GetSystemMetrics( SM_CYBORDER ) ),
202 					  height + GetSystemMetrics( SM_CYCAPTION ) + GetSystemMetrics( SM_CYBORDER ),
203 					  SWP_NOZORDER | SWP_NOACTIVATE | SWP_FRAMECHANGED );
204 
205 		glViewport( 0, 0, width, height );
206 		// update matricies
207 	}
208 
209 	override void reload()
210 	{
211 		resize();
212 		
213 		// Enable back face culling
214 		if( backfaceCulling )
215 		{
216 			glEnable( GL_CULL_FACE );
217 			glCullFace( GL_BACK );
218 		}
219 		
220 		// Turn on of off the v sync
221 		wglSwapIntervalEXT( vsync );
222 	}
223 
224 	override void swapBuffers()
225 	{
226 		SwapBuffers( deviceContext );
227 	}
228 
229 	override void openWindow()
230 	{
231 		openWindow( true );
232 	}
233 
234 	final void openWindow( bool showWindow )
235 	{
236 		hWnd = CreateWindowEx( 0, DGame.instance.title.ptr, DGame.instance.title.ptr, fullscreen ? DWS_FULLSCREEN : DWS_WINDOWED,
237 							   ( screenWidth - width ) / 2, ( screenHeight - height ) / 2, width, height,
238 							  null, null, hInstance, null );
239 
240 		assert( hWnd );
241 
242 		resize();
243 
244 		ShowWindow( hWnd, showWindow ? SW_NORMAL : SW_HIDE );
245 	}
246 
247 	override void closeWindow()
248 	{
249 		DestroyWindow( hWnd );
250 		hWnd = null;
251 	}
252 
253 	override void messageLoop()
254 	{
255 		MSG msg;
256 
257 		// Initialize the message structure.
258 		( cast(byte*)&msg )[ 0 .. msg.sizeof ] = 0;
259 
260 		// Handle the windows messages.
261 		while( PeekMessage( &msg, NULL, 0, 0, PM_REMOVE ) )
262 		{
263 			TranslateMessage( &msg );
264 			DispatchMessage( &msg );
265 		}
266 	}
267 }