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 X11 utilities.
22 *//*--------------------------------------------------------------------*/
23
24#include "tcuX11.hpp"
25#include "gluRenderConfig.hpp"
26#include "deMemory.h"
27
28#include <X11/Xutil.h>
29
30namespace tcu
31{
32namespace x11
33{
34
35enum
36{
37	DEFAULT_WINDOW_WIDTH	= 400,
38	DEFAULT_WINDOW_HEIGHT	= 300
39};
40
41EventState::EventState (void)
42	: m_quit(false)
43{
44}
45
46EventState::~EventState (void)
47{
48}
49
50void EventState::setQuitFlag (bool quit)
51{
52	de::ScopedLock lock(m_mutex);
53	m_quit = quit;
54}
55
56bool EventState::getQuitFlag (void)
57{
58	de::ScopedLock lock(m_mutex);
59	return m_quit;
60}
61
62Display::Display (EventState& eventState, const char* name)
63	: m_eventState	(eventState)
64	, m_display		(DE_NULL)
65	, m_deleteAtom	(DE_NULL)
66{
67	m_display = XOpenDisplay((char*)name); // Won't modify argument string.
68	if (!m_display)
69		throw ResourceError("Failed to open display", name, __FILE__, __LINE__);
70
71	m_deleteAtom	= XInternAtom(m_display, "WM_DELETE_WINDOW", False);
72}
73
74Display::~Display (void)
75{
76	XCloseDisplay(m_display);
77}
78
79void Display::processEvents (void)
80{
81	XEvent	event;
82
83	while (XPending(m_display))
84	{
85		XNextEvent(m_display, &event);
86
87		// \todo [2010-10-27 pyry] Handle ConfigureNotify?
88		if (event.type == ClientMessage && (unsigned)event.xclient.data.l[0] == m_deleteAtom)
89			m_eventState.setQuitFlag(true);
90	}
91}
92
93bool Display::getVisualInfo (VisualID visualID, XVisualInfo& dst)
94{
95	XVisualInfo		query;
96	query.visualid = visualID;
97	int				numVisuals	= 0;
98	XVisualInfo*	response	= XGetVisualInfo(m_display, VisualIDMask, &query, &numVisuals);
99	bool			succ		= false;
100
101	if (response != DE_NULL)
102	{
103		if (numVisuals > 0) // should be 1, but you never know...
104		{
105			dst = response[0];
106			succ = true;
107		}
108		XFree(response);
109	}
110
111	return succ;
112}
113
114::Visual* Display::getVisual (VisualID visualID)
115{
116	XVisualInfo		info;
117
118	if (getVisualInfo(visualID, info))
119		return info.visual;
120
121	return DE_NULL;
122}
123
124Window::Window (Display& display, int width, int height, ::Visual* visual)
125	: m_display		(display)
126	, m_colormap	(None)
127	, m_window		(None)
128	, m_visible		(false)
129{
130	XSetWindowAttributes	swa;
131	::Display* const		dpy					= m_display.getXDisplay();
132	::Window				root				= DefaultRootWindow(dpy);
133	unsigned long			mask				= CWBorderPixel | CWEventMask;
134
135	// If redirect is enabled, window size can't be guaranteed and it is up to
136	// the window manager to decide whether to honor sizing requests. However,
137	// overriding that causes window to appear as an overlay, which causes
138	// other issues, so this is disabled by default.
139	const bool				overrideRedirect	= false;
140
141	if (overrideRedirect)
142	{
143		mask |= CWOverrideRedirect;
144		swa.override_redirect = true;
145	}
146
147	if (visual == DE_NULL)
148		visual = CopyFromParent;
149	else
150	{
151		XVisualInfo	info	= XVisualInfo();
152		bool		succ	= display.getVisualInfo(XVisualIDFromVisual(visual), info);
153
154		TCU_CHECK_INTERNAL(succ);
155
156		root				= RootWindow(dpy, info.screen);
157		m_colormap			= XCreateColormap(dpy, root, visual, AllocNone);
158		swa.colormap		= m_colormap;
159		mask |= CWColormap;
160	}
161
162	swa.border_pixel	= 0;
163	swa.event_mask		= ExposureMask|KeyPressMask|KeyReleaseMask|StructureNotifyMask;
164
165	if (width == glu::RenderConfig::DONT_CARE)
166		width = DEFAULT_WINDOW_WIDTH;
167	if (height == glu::RenderConfig::DONT_CARE)
168		height = DEFAULT_WINDOW_HEIGHT;
169
170	m_window = XCreateWindow(dpy, root, 0, 0, width, height, 0,
171							 CopyFromParent, InputOutput, visual, mask, &swa);
172	TCU_CHECK(m_window);
173
174	Atom deleteAtom = m_display.getDeleteAtom();
175	XSetWMProtocols(dpy, m_window, &deleteAtom, 1);
176}
177
178void Window::setVisibility (bool visible)
179{
180	::Display*	dpy			= m_display.getXDisplay();
181	int			eventType	= None;
182	XEvent		event;
183
184	if (visible == m_visible)
185		return;
186
187	if (visible)
188	{
189		XMapWindow(dpy, m_window);
190		eventType = MapNotify;
191	}
192	else
193	{
194		XUnmapWindow(dpy, m_window);
195		eventType = UnmapNotify;
196	}
197
198	// We are only interested about exposure/structure notify events, not user input
199	XSelectInput(dpy, m_window, ExposureMask | StructureNotifyMask);
200
201	do
202	{
203		XNextEvent(dpy, &event);
204	} while (event.type != eventType);
205
206	m_visible = visible;
207}
208
209void Window::getDimensions (int* width, int* height) const
210{
211	int x, y;
212	::Window root;
213	unsigned width_, height_, borderWidth, depth;
214
215	XGetGeometry(m_display.getXDisplay(), m_window, &root, &x, &y, &width_, &height_, &borderWidth, &depth);
216	if (width != DE_NULL)
217		*width = static_cast<int>(width_);
218	if (height != DE_NULL)
219		*height = static_cast<int>(height_);
220}
221
222void Window::setDimensions (int width, int height)
223{
224	const unsigned int	mask = CWWidth | CWHeight;
225	XWindowChanges		changes;
226	changes.width		= width;
227	changes.height		= height;
228
229	XConfigureWindow(m_display.getXDisplay(), m_window, mask, &changes);
230}
231
232void Window::processEvents (void)
233{
234	// A bit of a hack, since we don't really handle all the events.
235	m_display.processEvents();
236}
237
238Window::~Window (void)
239{
240	XDestroyWindow(m_display.getXDisplay(), m_window);
241	if (m_colormap != None)
242		XFreeColormap(m_display.getXDisplay(), m_colormap);
243}
244
245} // x11
246} // tcu
247