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