| // | |
| // Copyright 2015 The ANGLE Project Authors. All rights reserved. | |
| // Use of this source code is governed by a BSD-style license that can be | |
| // found in the LICENSE file. | |
| // | |
| // WindowSurfaceGLX.cpp: GLX implementation of egl::Surface for windows | |
| #include "common/debug.h" | |
| #include "libANGLE/renderer/gl/glx/DisplayGLX.h" | |
| #include "libANGLE/renderer/gl/glx/FunctionsGLX.h" | |
| #include "libANGLE/renderer/gl/glx/WindowSurfaceGLX.h" | |
| namespace rx | |
| { | |
| static int IgnoreX11Errors(Display *, XErrorEvent *) | |
| { | |
| return 0; | |
| } | |
| WindowSurfaceGLX::WindowSurfaceGLX(const egl::SurfaceState &state, | |
| const FunctionsGLX &glx, | |
| DisplayGLX *glxDisplay, | |
| Window window, | |
| Display *display, | |
| glx::FBConfig fbConfig) | |
| : SurfaceGLX(state), | |
| mParent(window), | |
| mWindow(0), | |
| mDisplay(display), | |
| mUseChildWindow(false), | |
| mParentWidth(0), | |
| mParentHeight(0), | |
| mGLX(glx), | |
| mGLXDisplay(glxDisplay), | |
| mFBConfig(fbConfig), | |
| mGLXWindow(0) | |
| {} | |
| WindowSurfaceGLX::~WindowSurfaceGLX() | |
| { | |
| if (mGLXWindow) | |
| { | |
| mGLX.destroyWindow(mGLXWindow); | |
| } | |
| if (mUseChildWindow && mWindow) | |
| { | |
| // When destroying the window, it may happen that the window has already been | |
| // destroyed by the application (this happens in Chromium). There is no way to | |
| // atomically check that a window exists and to destroy it so instead we call | |
| // XDestroyWindow, ignoring any errors. | |
| auto oldErrorHandler = XSetErrorHandler(IgnoreX11Errors); | |
| XDestroyWindow(mDisplay, mWindow); | |
| XSync(mDisplay, False); | |
| XSetErrorHandler(oldErrorHandler); | |
| } | |
| mGLXDisplay->syncXCommands(true); | |
| } | |
| egl::Error WindowSurfaceGLX::initialize(const egl::Display *display) | |
| { | |
| mUseChildWindow = !mGLXDisplay->isWindowVisualIdSpecified(); | |
| XVisualInfo *visualInfo = nullptr; | |
| Colormap colormap = 0; | |
| if (!mUseChildWindow) | |
| { | |
| XWindowAttributes windowAttributes; | |
| XGetWindowAttributes(mDisplay, mParent, &windowAttributes); | |
| unsigned long visualId = windowAttributes.visual->visualid; | |
| // If the window's visual ID is different from the one provided by | |
| // ANGLE_X11_VISUAL_ID, fallback to using a child window. | |
| if (!mGLXDisplay->isMatchingWindowVisualId(visualId)) | |
| { | |
| mUseChildWindow = true; | |
| } | |
| } | |
| if (mUseChildWindow) | |
| { | |
| // The visual of the X window, GLX window and GLX context must match, | |
| // however we received a user-created window that can have any visual | |
| // and wouldn't work with our GLX context. To work in all cases, we | |
| // create a child window with the right visual that covers all of its | |
| // parent. | |
| visualInfo = mGLX.getVisualFromFBConfig(mFBConfig); | |
| if (!visualInfo) | |
| { | |
| return egl::Error(EGL_BAD_NATIVE_WINDOW, | |
| "Failed to get the XVisualInfo for the child window."); | |
| } | |
| Visual *visual = visualInfo->visual; | |
| if (!getWindowDimensions(mParent, &mParentWidth, &mParentHeight)) | |
| { | |
| return egl::Error(EGL_BAD_NATIVE_WINDOW, | |
| "Failed to get the parent window's dimensions."); | |
| } | |
| // The depth, colormap and visual must match otherwise we get a X error | |
| // so we specify the colormap attribute. Also we do not want the window | |
| // to be taken into account for input so we specify the event and | |
| // do-not-propagate masks to 0 (the defaults). Finally we specify the | |
| // border pixel attribute so that we can use a different visual depth | |
| // than our parent (seems like X uses that as a condition to render | |
| // the subwindow in a different buffer) | |
| XSetWindowAttributes attributes; | |
| unsigned long attributeMask = CWColormap | CWBorderPixel; | |
| colormap = XCreateColormap(mDisplay, mParent, visual, AllocNone); | |
| if (!colormap) | |
| { | |
| XFree(visualInfo); | |
| return egl::Error(EGL_BAD_NATIVE_WINDOW, | |
| "Failed to create the Colormap for the child window."); | |
| } | |
| attributes.colormap = colormap; | |
| attributes.border_pixel = 0; | |
| // TODO(cwallez) set up our own error handler to see if the call failed | |
| mWindow = XCreateWindow(mDisplay, mParent, 0, 0, mParentWidth, mParentHeight, 0, | |
| visualInfo->depth, InputOutput, visual, attributeMask, &attributes); | |
| } | |
| mGLXWindow = mGLX.createWindow(mFBConfig, (mUseChildWindow ? mWindow : mParent), nullptr); | |
| if (mUseChildWindow) | |
| { | |
| XMapWindow(mDisplay, mWindow); | |
| } | |
| XFlush(mDisplay); | |
| if (mUseChildWindow) | |
| { | |
| XFree(visualInfo); | |
| XFreeColormap(mDisplay, colormap); | |
| } | |
| mGLXDisplay->syncXCommands(true); | |
| return egl::NoError(); | |
| } | |
| egl::Error WindowSurfaceGLX::makeCurrent(const gl::Context *context) | |
| { | |
| return egl::NoError(); | |
| } | |
| egl::Error WindowSurfaceGLX::swap(const gl::Context *context, SurfaceSwapFeedback *feedback) | |
| { | |
| // We need to swap before resizing as some drivers clobber the back buffer | |
| // when the window is resized. | |
| mGLXDisplay->setSwapInterval(mGLXWindow, &mSwapControl); | |
| mGLX.swapBuffers(mGLXWindow); | |
| if (mUseChildWindow) | |
| { | |
| egl::Error error = checkForResize(); | |
| if (error.isError()) | |
| { | |
| return error; | |
| } | |
| } | |
| return egl::NoError(); | |
| } | |
| egl::Error WindowSurfaceGLX::postSubBuffer(const gl::Context *context, | |
| EGLint x, | |
| EGLint y, | |
| EGLint width, | |
| EGLint height) | |
| { | |
| UNIMPLEMENTED(); | |
| return egl::NoError(); | |
| } | |
| egl::Error WindowSurfaceGLX::querySurfacePointerANGLE(EGLint attribute, void **value) | |
| { | |
| UNIMPLEMENTED(); | |
| return egl::NoError(); | |
| } | |
| egl::Error WindowSurfaceGLX::bindTexImage(const gl::Context *context, | |
| gl::Texture *texture, | |
| EGLint buffer) | |
| { | |
| UNIMPLEMENTED(); | |
| return egl::NoError(); | |
| } | |
| egl::Error WindowSurfaceGLX::releaseTexImage(const gl::Context *context, EGLint buffer) | |
| { | |
| UNIMPLEMENTED(); | |
| return egl::NoError(); | |
| } | |
| void WindowSurfaceGLX::setSwapInterval(const egl::Display *display, EGLint interval) | |
| { | |
| mSwapControl.targetSwapInterval = interval; | |
| } | |
| gl::Extents WindowSurfaceGLX::getSize() const | |
| { | |
| if (mUseChildWindow) | |
| { | |
| // If there's a child window, the size of the window is always the same as the cached | |
| // size of its parent. | |
| return gl::Extents(mParentWidth, mParentHeight, 1); | |
| } | |
| else | |
| { | |
| unsigned int parentWidth, parentHeight; | |
| if (!getWindowDimensions(mParent, &parentWidth, &parentHeight)) | |
| { | |
| return gl::Extents(mParentWidth, mParentHeight, 1); | |
| } | |
| return gl::Extents(parentWidth, parentHeight, 1); | |
| } | |
| } | |
| EGLint WindowSurfaceGLX::isPostSubBufferSupported() const | |
| { | |
| UNIMPLEMENTED(); | |
| return EGL_FALSE; | |
| } | |
| EGLint WindowSurfaceGLX::getSwapBehavior() const | |
| { | |
| return EGL_BUFFER_DESTROYED; | |
| } | |
| egl::Error WindowSurfaceGLX::checkForResize() | |
| { | |
| // TODO(cwallez) set up our own error handler to see if the call failed | |
| unsigned int newParentWidth, newParentHeight; | |
| if (!getWindowDimensions(mParent, &newParentWidth, &newParentHeight)) | |
| { | |
| return egl::Error(EGL_BAD_CURRENT_SURFACE, | |
| "Failed to retrieve the size of the parent window."); | |
| } | |
| if (mParentWidth != newParentWidth || mParentHeight != newParentHeight) | |
| { | |
| mParentWidth = newParentWidth; | |
| mParentHeight = newParentHeight; | |
| mGLX.waitGL(); | |
| XResizeWindow(mDisplay, mWindow, mParentWidth, mParentHeight); | |
| mGLX.waitX(); | |
| XSync(mDisplay, False); | |
| } | |
| return egl::NoError(); | |
| } | |
| glx::Drawable WindowSurfaceGLX::getDrawable() const | |
| { | |
| return mGLXWindow; | |
| } | |
| bool WindowSurfaceGLX::getWindowDimensions(Window window, | |
| unsigned int *width, | |
| unsigned int *height) const | |
| { | |
| Window root; | |
| int x, y; | |
| unsigned int border, depth; | |
| return XGetGeometry(mDisplay, window, &root, &x, &y, width, height, &border, &depth) != 0; | |
| } | |
| egl::Error WindowSurfaceGLX::getSyncValues(EGLuint64KHR *ust, EGLuint64KHR *msc, EGLuint64KHR *sbc) | |
| { | |
| if (!mGLX.getSyncValuesOML(mGLXWindow, reinterpret_cast<int64_t *>(ust), | |
| reinterpret_cast<int64_t *>(msc), reinterpret_cast<int64_t *>(sbc))) | |
| { | |
| return egl::Error(EGL_BAD_SURFACE, "glXGetSyncValuesOML failed."); | |
| } | |
| return egl::NoError(); | |
| } | |
| egl::Error WindowSurfaceGLX::getMscRate(EGLint *numerator, EGLint *denominator) | |
| { | |
| if (!mGLX.getMscRateOML(mGLXWindow, reinterpret_cast<int32_t *>(numerator), | |
| reinterpret_cast<int32_t *>(denominator))) | |
| { | |
| return egl::Error(EGL_BAD_SURFACE, "glXGetMscRateOML failed."); | |
| } | |
| if (mGLXDisplay->getRenderer()->getFeatures().clampMscRate.enabled) | |
| { | |
| // Clamp any refresh rate under 2Hz to 30Hz | |
| if (*numerator < *denominator * 2) | |
| { | |
| *numerator = 30; | |
| *denominator = 1; | |
| } | |
| } | |
| return egl::NoError(); | |
| } | |
| } // namespace rx |