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