1 //
2 // Copyright 2015 The ANGLE Project Authors. All rights reserved.
3 // Use of this source code is governed by a BSD-style license that can be
4 // found in the LICENSE file.
5 //
6
7 // WindowSurfaceGLX.cpp: GLX implementation of egl::Surface for windows
8
9 #include "libANGLE/renderer/gl/glx/WindowSurfaceGLX.h"
10
11 #include "common/debug.h"
12
13 #include "libANGLE/renderer/gl/glx/DisplayGLX.h"
14 #include "libANGLE/renderer/gl/glx/FunctionsGLX.h"
15
16 namespace rx
17 {
18
IgnoreX11Errors(Display *,XErrorEvent *)19 static int IgnoreX11Errors(Display *, XErrorEvent *)
20 {
21 return 0;
22 }
23
WindowSurfaceGLX(const egl::SurfaceState & state,const FunctionsGLX & glx,DisplayGLX * glxDisplay,Window window,Display * display,glx::FBConfig fbConfig)24 WindowSurfaceGLX::WindowSurfaceGLX(const egl::SurfaceState &state,
25 const FunctionsGLX &glx,
26 DisplayGLX *glxDisplay,
27 Window window,
28 Display *display,
29 glx::FBConfig fbConfig)
30 : SurfaceGLX(state),
31 mParent(window),
32 mWindow(0),
33 mDisplay(display),
34 mGLX(glx),
35 mGLXDisplay(glxDisplay),
36 mFBConfig(fbConfig),
37 mGLXWindow(0)
38 {}
39
~WindowSurfaceGLX()40 WindowSurfaceGLX::~WindowSurfaceGLX()
41 {
42 if (mGLXWindow)
43 {
44 mGLX.destroyWindow(mGLXWindow);
45 }
46
47 if (mWindow)
48 {
49 // When destroying the window, it may happen that the window has already been
50 // destroyed by the application (this happens in Chromium). There is no way to
51 // atomically check that a window exists and to destroy it so instead we call
52 // XDestroyWindow, ignoring any errors.
53 auto oldErrorHandler = XSetErrorHandler(IgnoreX11Errors);
54 XDestroyWindow(mDisplay, mWindow);
55 XSync(mDisplay, False);
56 XSetErrorHandler(oldErrorHandler);
57 }
58
59 mGLXDisplay->syncXCommands();
60 }
61
initialize(const egl::Display * display)62 egl::Error WindowSurfaceGLX::initialize(const egl::Display *display)
63 {
64 // Check that the window's visual ID is valid, as part of the AMGLE_x11_visual
65 // extension.
66 {
67 XWindowAttributes windowAttributes;
68 XGetWindowAttributes(mDisplay, mParent, &windowAttributes);
69 unsigned long visualId = windowAttributes.visual->visualid;
70
71 if (!mGLXDisplay->isValidWindowVisualId(visualId))
72 {
73 return egl::EglBadMatch() << "The visual of native_window doesn't match the visual "
74 "given with ANGLE_X11_VISUAL_ID";
75 }
76 }
77
78 // The visual of the X window, GLX window and GLX context must match,
79 // however we received a user-created window that can have any visual
80 // and wouldn't work with our GLX context. To work in all cases, we
81 // create a child window with the right visual that covers all of its
82 // parent.
83 XVisualInfo *visualInfo = mGLX.getVisualFromFBConfig(mFBConfig);
84 if (!visualInfo)
85 {
86 return egl::EglBadNativeWindow() << "Failed to get the XVisualInfo for the child window.";
87 }
88 Visual *visual = visualInfo->visual;
89
90 if (!getWindowDimensions(mParent, &mParentWidth, &mParentHeight))
91 {
92 return egl::EglBadNativeWindow() << "Failed to get the parent window's dimensions.";
93 }
94
95 // The depth, colormap and visual must match otherwise we get a X error
96 // so we specify the colormap attribute. Also we do not want the window
97 // to be taken into account for input so we specify the event and
98 // do-not-propagate masks to 0 (the defaults). Finally we specify the
99 // border pixel attribute so that we can use a different visual depth
100 // than our parent (seems like X uses that as a condition to render
101 // the subwindow in a different buffer)
102 XSetWindowAttributes attributes;
103 unsigned long attributeMask = CWColormap | CWBorderPixel;
104
105 Colormap colormap = XCreateColormap(mDisplay, mParent, visual, AllocNone);
106 if (!colormap)
107 {
108 XFree(visualInfo);
109 return egl::EglBadNativeWindow() << "Failed to create the Colormap for the child window.";
110 }
111 attributes.colormap = colormap;
112 attributes.border_pixel = 0;
113
114 // TODO(cwallez) set up our own error handler to see if the call failed
115 mWindow = XCreateWindow(mDisplay, mParent, 0, 0, mParentWidth, mParentHeight, 0,
116 visualInfo->depth, InputOutput, visual, attributeMask, &attributes);
117 mGLXWindow = mGLX.createWindow(mFBConfig, mWindow, nullptr);
118
119 XMapWindow(mDisplay, mWindow);
120 XSelectInput(mDisplay, mWindow, ExposureMask); // For XExposeEvent forwarding from child window
121 XFlush(mDisplay);
122
123 XFree(visualInfo);
124 XFreeColormap(mDisplay, colormap);
125
126 mGLXDisplay->syncXCommands();
127
128 return egl::NoError();
129 }
130
makeCurrent(const gl::Context * context)131 egl::Error WindowSurfaceGLX::makeCurrent(const gl::Context *context)
132 {
133 return egl::NoError();
134 }
135
swap(const gl::Context * context)136 egl::Error WindowSurfaceGLX::swap(const gl::Context *context)
137 {
138 // We need to swap before resizing as some drivers clobber the back buffer
139 // when the window is resized.
140 mGLXDisplay->setSwapInterval(mGLXWindow, &mSwapControl);
141 mGLX.swapBuffers(mGLXWindow);
142
143 egl::Error error = checkForResize();
144 if (error.isError())
145 {
146 return error;
147 }
148
149 return egl::NoError();
150 }
151
postSubBuffer(const gl::Context * context,EGLint x,EGLint y,EGLint width,EGLint height)152 egl::Error WindowSurfaceGLX::postSubBuffer(const gl::Context *context,
153 EGLint x,
154 EGLint y,
155 EGLint width,
156 EGLint height)
157 {
158 UNIMPLEMENTED();
159 return egl::NoError();
160 }
161
querySurfacePointerANGLE(EGLint attribute,void ** value)162 egl::Error WindowSurfaceGLX::querySurfacePointerANGLE(EGLint attribute, void **value)
163 {
164 UNIMPLEMENTED();
165 return egl::NoError();
166 }
167
bindTexImage(const gl::Context * context,gl::Texture * texture,EGLint buffer)168 egl::Error WindowSurfaceGLX::bindTexImage(const gl::Context *context,
169 gl::Texture *texture,
170 EGLint buffer)
171 {
172 UNIMPLEMENTED();
173 return egl::NoError();
174 }
175
releaseTexImage(const gl::Context * context,EGLint buffer)176 egl::Error WindowSurfaceGLX::releaseTexImage(const gl::Context *context, EGLint buffer)
177 {
178 UNIMPLEMENTED();
179 return egl::NoError();
180 }
181
setSwapInterval(EGLint interval)182 void WindowSurfaceGLX::setSwapInterval(EGLint interval)
183 {
184 mSwapControl.targetSwapInterval = interval;
185 }
186
getWidth() const187 EGLint WindowSurfaceGLX::getWidth() const
188 {
189 // The size of the window is always the same as the cached size of its parent.
190 return mParentWidth;
191 }
192
getHeight() const193 EGLint WindowSurfaceGLX::getHeight() const
194 {
195 // The size of the window is always the same as the cached size of its parent.
196 return mParentHeight;
197 }
198
isPostSubBufferSupported() const199 EGLint WindowSurfaceGLX::isPostSubBufferSupported() const
200 {
201 UNIMPLEMENTED();
202 return EGL_FALSE;
203 }
204
getSwapBehavior() const205 EGLint WindowSurfaceGLX::getSwapBehavior() const
206 {
207 return EGL_BUFFER_DESTROYED;
208 }
209
checkForResize()210 egl::Error WindowSurfaceGLX::checkForResize()
211 {
212 // TODO(cwallez) set up our own error handler to see if the call failed
213 unsigned int newParentWidth, newParentHeight;
214 if (!getWindowDimensions(mParent, &newParentWidth, &newParentHeight))
215 {
216 return egl::EglBadCurrentSurface() << "Failed to retrieve the size of the parent window.";
217 }
218
219 if (mParentWidth != newParentWidth || mParentHeight != newParentHeight)
220 {
221 mParentWidth = newParentWidth;
222 mParentHeight = newParentHeight;
223
224 mGLX.waitGL();
225 XResizeWindow(mDisplay, mWindow, mParentWidth, mParentHeight);
226 mGLX.waitX();
227 XSync(mDisplay, False);
228 }
229
230 return egl::NoError();
231 }
232
getDrawable() const233 glx::Drawable WindowSurfaceGLX::getDrawable() const
234 {
235 return mGLXWindow;
236 }
237
getWindowDimensions(Window window,unsigned int * width,unsigned int * height) const238 bool WindowSurfaceGLX::getWindowDimensions(Window window,
239 unsigned int *width,
240 unsigned int *height) const
241 {
242 Window root;
243 int x, y;
244 unsigned int border, depth;
245 return XGetGeometry(mDisplay, window, &root, &x, &y, width, height, &border, &depth) != 0;
246 }
247
getSyncValues(EGLuint64KHR * ust,EGLuint64KHR * msc,EGLuint64KHR * sbc)248 egl::Error WindowSurfaceGLX::getSyncValues(EGLuint64KHR *ust, EGLuint64KHR *msc, EGLuint64KHR *sbc)
249 {
250 if (!mGLX.getSyncValuesOML(mGLXWindow, reinterpret_cast<int64_t *>(ust),
251 reinterpret_cast<int64_t *>(msc), reinterpret_cast<int64_t *>(sbc)))
252 {
253 return egl::EglBadSurface() << "glXGetSyncValuesOML failed.";
254 }
255 return egl::NoError();
256 }
257
getMscRate(EGLint * numerator,EGLint * denominator)258 egl::Error WindowSurfaceGLX::getMscRate(EGLint *numerator, EGLint *denominator)
259 {
260 if (!mGLX.getMscRateOML(mGLXWindow, reinterpret_cast<int32_t *>(numerator),
261 reinterpret_cast<int32_t *>(denominator)))
262 {
263 return egl::EglBadSurface() << "glXGetMscRateOML failed.";
264 }
265 return egl::NoError();
266 }
267
268 } // namespace rx
269