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*				dpy	= m_display.getXDisplay();
132	::Window				root = DefaultRootWindow(dpy);
133	unsigned long			mask = CWBorderPixel | CWEventMask;
134
135	if (visual == DE_NULL)
136		visual = CopyFromParent;
137	else
138	{
139		XVisualInfo	info	= XVisualInfo();
140		bool		succ	= display.getVisualInfo(XVisualIDFromVisual(visual), info);
141
142		TCU_CHECK_INTERNAL(succ);
143
144		root				= RootWindow(dpy, info.screen);
145		m_colormap			= XCreateColormap(dpy, root, visual, AllocNone);
146		swa.colormap		= m_colormap;
147		mask |= CWColormap;
148	}
149
150	swa.border_pixel	= 0;
151	swa.event_mask		= ExposureMask|KeyPressMask|KeyReleaseMask|StructureNotifyMask;
152
153	mask |= CWOverrideRedirect;
154	swa.override_redirect = true;
155
156	if (width == glu::RenderConfig::DONT_CARE)
157		width = DEFAULT_WINDOW_WIDTH;
158	if (height == glu::RenderConfig::DONT_CARE)
159		height = DEFAULT_WINDOW_HEIGHT;
160
161	m_window = XCreateWindow(dpy, root, 0, 0, width, height, 0,
162							 CopyFromParent, InputOutput, visual, mask, &swa);
163	TCU_CHECK(m_window);
164
165	Atom deleteAtom = m_display.getDeleteAtom();
166	XSetWMProtocols(dpy, m_window, &deleteAtom, 1);
167
168}
169
170void Window::setVisibility (bool visible)
171{
172	::Display*	dpy			= m_display.getXDisplay();
173	int			eventType	= None;
174	XEvent		event;
175
176	if (visible == m_visible)
177		return;
178
179	if (visible)
180	{
181		XMapWindow(dpy, m_window);
182		eventType = MapNotify;
183	}
184	else
185	{
186		XUnmapWindow(dpy, m_window);
187		eventType = UnmapNotify;
188	}
189
190	// We are only interested about exposure/structure notify events, not user input
191	XSelectInput(dpy, m_window, ExposureMask | StructureNotifyMask);
192
193	do
194	{
195		XNextEvent(dpy, &event);
196	} while (event.type != eventType);
197
198	m_visible = visible;
199}
200
201void Window::getDimensions (int* width, int* height) const
202{
203	int x, y;
204	::Window root;
205	unsigned width_, height_, borderWidth, depth;
206
207	XGetGeometry(m_display.getXDisplay(), m_window, &root, &x, &y, &width_, &height_, &borderWidth, &depth);
208	if (width != DE_NULL)
209		*width = static_cast<int>(width_);
210	if (height != DE_NULL)
211		*height = static_cast<int>(height_);
212}
213
214void Window::setDimensions (int width, int height)
215{
216	const unsigned int	mask = CWWidth | CWHeight;
217	XWindowChanges		changes;
218	changes.width		= width;
219	changes.height		= height;
220
221	XConfigureWindow(m_display.getXDisplay(), m_window, mask, &changes);
222}
223
224void Window::processEvents (void)
225{
226	// A bit of a hack, since we don't really handle all the events.
227	m_display.processEvents();
228}
229
230Window::~Window (void)
231{
232	XDestroyWindow(m_display.getXDisplay(), m_window);
233	if (m_colormap != None)
234		XFreeColormap(m_display.getXDisplay(), m_colormap);
235}
236
237} // x11
238} // tcu
239