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