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