1/*-------------------------------------------------------------------------
2 * drawElements Quality Program Tester Core
3 * ----------------------------------------
4 *
5 * Copyright 2014 The Android Open Source Project
6 *
7 * Licensed under the Apache License, Version 2.0 (the "License");
8 * you may not use this file except in compliance with the License.
9 * You may obtain a copy of the License at
10 *
11 *      http://www.apache.org/licenses/LICENSE-2.0
12 *
13 * Unless required by applicable law or agreed to in writing, software
14 * distributed under the License is distributed on an "AS IS" BASIS,
15 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16 * See the License for the specific language governing permissions and
17 * limitations under the License.
18 *
19 *//*!
20 * \file
21 * \brief Win32 EGL native display factory
22 *//*--------------------------------------------------------------------*/
23
24#include "tcuWin32EGLNativeDisplayFactory.hpp"
25
26#include "egluDefs.hpp"
27#include "tcuWin32Window.hpp"
28#include "tcuWin32API.h"
29#include "tcuTexture.hpp"
30#include "deMemory.h"
31#include "deThread.h"
32#include "deClock.h"
33#include "eglwLibrary.hpp"
34#include "eglwEnums.hpp"
35
36// Assume no call translation is needed
37DE_STATIC_ASSERT(sizeof(eglw::EGLNativeDisplayType) == sizeof(HDC));
38DE_STATIC_ASSERT(sizeof(eglw::EGLNativePixmapType) == sizeof(HBITMAP));
39DE_STATIC_ASSERT(sizeof(eglw::EGLNativeWindowType) == sizeof(HWND));
40
41namespace tcu
42{
43namespace win32
44{
45namespace
46{
47
48using namespace eglw;
49
50enum
51{
52	DEFAULT_SURFACE_WIDTH		= 400,
53	DEFAULT_SURFACE_HEIGHT		= 300,
54	WAIT_WINDOW_VISIBLE_MS		= 500	//!< Time to wait before issuing screenshot after changing window visibility (hack for DWM)
55};
56
57static const eglu::NativeDisplay::Capability	DISPLAY_CAPABILITIES	= eglu::NativeDisplay::CAPABILITY_GET_DISPLAY_LEGACY;
58static const eglu::NativePixmap::Capability		BITMAP_CAPABILITIES		= eglu::NativePixmap::CAPABILITY_CREATE_SURFACE_LEGACY;
59static const eglu::NativeWindow::Capability		WINDOW_CAPABILITIES		= (eglu::NativeWindow::Capability)
60																		   (eglu::NativeWindow::CAPABILITY_CREATE_SURFACE_LEGACY	|
61																		    eglu::NativeWindow::CAPABILITY_GET_SURFACE_SIZE			|
62																		    eglu::NativeWindow::CAPABILITY_GET_SCREEN_SIZE			|
63																			eglu::NativeWindow::CAPABILITY_READ_SCREEN_PIXELS		|
64																		    eglu::NativeWindow::CAPABILITY_SET_SURFACE_SIZE			|
65																			eglu::NativeWindow::CAPABILITY_CHANGE_VISIBILITY);
66
67class NativeDisplay : public eglu::NativeDisplay
68{
69public:
70									NativeDisplay			(void);
71	virtual							~NativeDisplay			(void) {}
72
73	virtual EGLNativeDisplayType	getLegacyNative			(void)			{ return m_deviceContext;	}
74	const eglw::Library&			getLibrary				(void) const	{ return m_library;			}
75
76	HDC								getDeviceContext		(void)			{ return m_deviceContext;	}
77
78private:
79	HDC								m_deviceContext;
80	eglw::DefaultLibrary			m_library;
81};
82
83class NativePixmapFactory : public eglu::NativePixmapFactory
84{
85public:
86								NativePixmapFactory		(void);
87								~NativePixmapFactory	(void) {}
88
89	virtual eglu::NativePixmap*	createPixmap			(eglu::NativeDisplay* nativeDisplay, int width, int height) const;
90	virtual eglu::NativePixmap*	createPixmap			(eglu::NativeDisplay* nativeDisplay, EGLDisplay display, EGLConfig config, const EGLAttrib* attribList, int width, int height) const;
91};
92
93class NativePixmap : public eglu::NativePixmap
94{
95public:
96								NativePixmap			(NativeDisplay* nativeDisplay, int width, int height, int bitDepth);
97	virtual						~NativePixmap			(void);
98
99	EGLNativePixmapType			getLegacyNative			(void) { return m_bitmap; }
100
101private:
102	HBITMAP						m_bitmap;
103};
104
105class NativeWindowFactory : public eglu::NativeWindowFactory
106{
107public:
108								NativeWindowFactory		(HINSTANCE instance);
109	virtual						~NativeWindowFactory	(void) {}
110
111	virtual eglu::NativeWindow*	createWindow			(eglu::NativeDisplay* nativeDisplay, const eglu::WindowParams& params) const;
112
113private:
114	const HINSTANCE				m_instance;
115};
116
117class NativeWindow : public eglu::NativeWindow
118{
119public:
120									NativeWindow			(NativeDisplay* nativeDisplay, HINSTANCE instance, const eglu::WindowParams& params);
121	virtual							~NativeWindow			(void);
122
123	EGLNativeWindowType				getLegacyNative			(void) { return m_window.getHandle(); }
124	virtual IVec2					getSurfaceSize			(void) const;
125	virtual IVec2					getScreenSize			(void) const { return getSurfaceSize(); }
126	virtual void					processEvents			(void);
127	virtual void					setSurfaceSize			(IVec2 size);
128	virtual void					setVisibility			(eglu::WindowParams::Visibility visibility);
129	virtual void					readScreenPixels		(tcu::TextureLevel* dst) const;
130
131private:
132	win32::Window					m_window;
133	eglu::WindowParams::Visibility	m_curVisibility;
134	deUint64						m_setVisibleTime;		//!< Time window was set visible.
135};
136
137// NativeDisplay
138
139NativeDisplay::NativeDisplay (void)
140	: eglu::NativeDisplay	(DISPLAY_CAPABILITIES)
141	, m_deviceContext		((HDC)EGL_DEFAULT_DISPLAY)
142	, m_library				("libEGL.dll")
143{
144}
145
146// NativePixmap
147
148NativePixmap::NativePixmap (NativeDisplay* nativeDisplay, int width, int height, int bitDepth)
149	: eglu::NativePixmap	(BITMAP_CAPABILITIES)
150	, m_bitmap				(DE_NULL)
151{
152	const HDC		deviceCtx	= nativeDisplay->getDeviceContext();
153	BITMAPINFO		bitmapInfo;
154
155	memset(&bitmapInfo, 0, sizeof(bitmapInfo));
156
157	if (bitDepth != 24 && bitDepth != 32)
158		throw NotSupportedError("Unsupported pixmap bit depth", DE_NULL, __FILE__, __LINE__);
159
160	bitmapInfo.bmiHeader.biSize				= sizeof(bitmapInfo);
161	bitmapInfo.bmiHeader.biWidth			= width;
162	bitmapInfo.bmiHeader.biHeight			= height;
163	bitmapInfo.bmiHeader.biPlanes			= 1;
164	bitmapInfo.bmiHeader.biBitCount			= bitDepth;
165	bitmapInfo.bmiHeader.biCompression		= BI_RGB;
166	bitmapInfo.bmiHeader.biSizeImage		= 0;
167	bitmapInfo.bmiHeader.biXPelsPerMeter	= 1;
168	bitmapInfo.bmiHeader.biYPelsPerMeter	= 1;
169	bitmapInfo.bmiHeader.biClrUsed			= 0;
170	bitmapInfo.bmiHeader.biClrImportant		= 0;
171
172	void* bitmapPtr = DE_NULL;
173	m_bitmap = CreateDIBSection(deviceCtx, &bitmapInfo, DIB_RGB_COLORS, &bitmapPtr, NULL, 0);
174
175	if (!m_bitmap)
176		throw ResourceError("Failed to create bitmap", DE_NULL, __FILE__, __LINE__);
177}
178
179NativePixmap::~NativePixmap (void)
180{
181	DeleteObject(m_bitmap);
182}
183
184// NativePixmapFactory
185
186NativePixmapFactory::NativePixmapFactory (void)
187	: eglu::NativePixmapFactory	("bitmap", "Win32 Bitmap", BITMAP_CAPABILITIES)
188{
189}
190
191eglu::NativePixmap* NativePixmapFactory::createPixmap (eglu::NativeDisplay* nativeDisplay, EGLDisplay display, EGLConfig config, const EGLAttrib* attribList, int width, int height) const
192{
193	const Library&	egl			= nativeDisplay->getLibrary();
194	int				redBits		= 0;
195	int				greenBits	= 0;
196	int				blueBits	= 0;
197	int				alphaBits	= 0;
198	int				bitSum		= 0;
199
200	DE_ASSERT(display != EGL_NO_DISPLAY);
201
202	egl.getConfigAttrib(display, config, EGL_RED_SIZE,		&redBits);
203	egl.getConfigAttrib(display, config, EGL_GREEN_SIZE,	&greenBits);
204	egl.getConfigAttrib(display, config, EGL_BLUE_SIZE,		&blueBits);
205	egl.getConfigAttrib(display, config, EGL_ALPHA_SIZE,	&alphaBits);
206	EGLU_CHECK_MSG(egl, "eglGetConfigAttrib()");
207
208	bitSum = redBits+greenBits+blueBits+alphaBits;
209
210	return new NativePixmap(dynamic_cast<NativeDisplay*>(nativeDisplay), width, height, bitSum);
211}
212
213eglu::NativePixmap* NativePixmapFactory::createPixmap (eglu::NativeDisplay* nativeDisplay, int width, int height) const
214{
215	const int defaultDepth = 32;
216	return new NativePixmap(dynamic_cast<NativeDisplay*>(nativeDisplay), width, height, defaultDepth);
217}
218
219// NativeWindowFactory
220
221NativeWindowFactory::NativeWindowFactory (HINSTANCE instance)
222	: eglu::NativeWindowFactory	("window", "Win32 Window", WINDOW_CAPABILITIES)
223	, m_instance				(instance)
224{
225}
226
227eglu::NativeWindow* NativeWindowFactory::createWindow (eglu::NativeDisplay* nativeDisplay, const eglu::WindowParams& params) const
228{
229	return new NativeWindow(dynamic_cast<NativeDisplay*>(nativeDisplay), m_instance, params);
230}
231
232// NativeWindow
233
234NativeWindow::NativeWindow (NativeDisplay* nativeDisplay, HINSTANCE instance, const eglu::WindowParams& params)
235	: eglu::NativeWindow	(WINDOW_CAPABILITIES)
236	, m_window				(instance,
237							 params.width	== eglu::WindowParams::SIZE_DONT_CARE ? DEFAULT_SURFACE_WIDTH	: params.width,
238							 params.height	== eglu::WindowParams::SIZE_DONT_CARE ? DEFAULT_SURFACE_HEIGHT	: params.height)
239	, m_curVisibility		(eglu::WindowParams::VISIBILITY_HIDDEN)
240	, m_setVisibleTime		(0)
241{
242	if (params.visibility != eglu::WindowParams::VISIBILITY_DONT_CARE)
243		setVisibility(params.visibility);
244}
245
246void NativeWindow::setVisibility (eglu::WindowParams::Visibility visibility)
247{
248	switch (visibility)
249	{
250		case eglu::WindowParams::VISIBILITY_HIDDEN:
251			m_window.setVisible(false);
252			m_curVisibility		= visibility;
253			break;
254
255		case eglu::WindowParams::VISIBILITY_VISIBLE:
256		case eglu::WindowParams::VISIBILITY_FULLSCREEN:
257			// \todo [2014-03-12 pyry] Implement FULLSCREEN, or at least SW_MAXIMIZE.
258			m_window.setVisible(true);
259			m_curVisibility		= eglu::WindowParams::VISIBILITY_VISIBLE;
260			m_setVisibleTime	= deGetMicroseconds();
261			break;
262
263		default:
264			DE_ASSERT(DE_FALSE);
265	}
266}
267
268NativeWindow::~NativeWindow (void)
269{
270}
271
272IVec2 NativeWindow::getSurfaceSize (void) const
273{
274	return m_window.getSize();
275}
276
277void NativeWindow::processEvents (void)
278{
279	m_window.processEvents();
280}
281
282void NativeWindow::setSurfaceSize (IVec2 size)
283{
284	m_window.setSize(size.x(), size.y());
285}
286
287void NativeWindow::readScreenPixels (tcu::TextureLevel* dst) const
288{
289	HDC			windowDC	= DE_NULL;
290	HDC			screenDC	= DE_NULL;
291	HDC			tmpDC		= DE_NULL;
292	HBITMAP		tmpBitmap	= DE_NULL;
293	RECT		rect;
294
295	TCU_CHECK_INTERNAL(m_curVisibility != eglu::WindowParams::VISIBILITY_HIDDEN);
296
297	// Hack for DWM: There is no way to wait for DWM animations to finish, so we just have to wait
298	// for a while before issuing screenshot if window was just made visible.
299	{
300		const deInt64 timeSinceVisibleUs = (deInt64)(deGetMicroseconds()-m_setVisibleTime);
301
302		if (timeSinceVisibleUs < (deInt64)WAIT_WINDOW_VISIBLE_MS*1000)
303			deSleep(WAIT_WINDOW_VISIBLE_MS - (deUint32)(timeSinceVisibleUs/1000));
304	}
305
306	TCU_CHECK(GetClientRect(m_window.getHandle(), &rect));
307
308	try
309	{
310		const int			width		= rect.right - rect.left;
311		const int			height		= rect.bottom - rect.top;
312		BITMAPINFOHEADER	bitmapInfo;
313
314		deMemset(&bitmapInfo, 0, sizeof(bitmapInfo));
315
316		screenDC = GetDC(DE_NULL);
317		TCU_CHECK(screenDC);
318
319		windowDC = GetDC(m_window.getHandle());
320		TCU_CHECK(windowDC);
321
322		tmpDC = CreateCompatibleDC(screenDC);
323		TCU_CHECK(tmpDC != DE_NULL);
324
325		MapWindowPoints(m_window.getHandle(), DE_NULL, (LPPOINT)&rect, 2);
326
327		tmpBitmap = CreateCompatibleBitmap(screenDC, width, height);
328		TCU_CHECK(tmpBitmap != DE_NULL);
329
330		TCU_CHECK(SelectObject(tmpDC, tmpBitmap) != DE_NULL);
331
332		TCU_CHECK(BitBlt(tmpDC, 0, 0, width, height, screenDC, rect.left, rect.top, SRCCOPY));
333
334
335		bitmapInfo.biSize			= sizeof(BITMAPINFOHEADER);
336		bitmapInfo.biWidth			= width;
337		bitmapInfo.biHeight			= -height;
338		bitmapInfo.biPlanes			= 1;
339		bitmapInfo.biBitCount		= 32;
340		bitmapInfo.biCompression	= BI_RGB;
341		bitmapInfo.biSizeImage		= 0;
342		bitmapInfo.biXPelsPerMeter	= 0;
343		bitmapInfo.biYPelsPerMeter	= 0;
344		bitmapInfo.biClrUsed		= 0;
345		bitmapInfo.biClrImportant	= 0;
346
347		dst->setStorage(TextureFormat(TextureFormat::BGRA, TextureFormat::UNORM_INT8), width, height);
348
349		TCU_CHECK(GetDIBits(screenDC, tmpBitmap, 0, height, dst->getAccess().getDataPtr(), (BITMAPINFO*)&bitmapInfo, DIB_RGB_COLORS));
350
351		DeleteObject(tmpBitmap);
352		tmpBitmap = DE_NULL;
353
354		ReleaseDC(DE_NULL, screenDC);
355		screenDC = DE_NULL;
356
357		ReleaseDC(m_window.getHandle(), windowDC);
358		windowDC = DE_NULL;
359
360		DeleteDC(tmpDC);
361		tmpDC = DE_NULL;
362	}
363	catch (...)
364	{
365		if (screenDC)
366			ReleaseDC(DE_NULL, screenDC);
367
368		if (windowDC)
369			ReleaseDC(m_window.getHandle(), windowDC);
370
371		if (tmpBitmap)
372			DeleteObject(tmpBitmap);
373
374		if (tmpDC)
375			DeleteDC(tmpDC);
376
377		throw;
378	}
379}
380
381} // anonymous
382
383EGLNativeDisplayFactory::EGLNativeDisplayFactory (HINSTANCE instance)
384	: eglu::NativeDisplayFactory	("win32", "Native Win32 Display", DISPLAY_CAPABILITIES)
385	, m_instance					(instance)
386{
387	m_nativeWindowRegistry.registerFactory(new NativeWindowFactory(m_instance));
388	m_nativePixmapRegistry.registerFactory(new NativePixmapFactory());
389}
390
391EGLNativeDisplayFactory::~EGLNativeDisplayFactory (void)
392{
393}
394
395eglu::NativeDisplay* EGLNativeDisplayFactory::createDisplay (const EGLAttrib* attribList) const
396{
397	DE_UNREF(attribList);
398	return new NativeDisplay();
399}
400
401} // win32
402} // tcu
403