blob: aed5cbc98deff5dadd8753e0bab2f5e4e8977997 [file] [log] [blame]
//
// Copyright (c) 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 "libANGLE/renderer/gl/glx/WindowSurfaceGLX.h"
#include "common/debug.h"
#include "libANGLE/renderer/gl/glx/DisplayGLX.h"
#include "libANGLE/renderer/gl/glx/FunctionsGLX.h"
namespace rx
{
static int IgnoreX11Errors(Display *, XErrorEvent *)
{
return 0;
}
WindowSurfaceGLX::WindowSurfaceGLX(const egl::SurfaceState &state,
const FunctionsGLX &glx,
DisplayGLX *glxDisplay,
RendererGL *renderer,
Window window,
Display *display,
glx::Context context,
glx::FBConfig fbConfig)
: SurfaceGLX(state, renderer, glx),
mParent(window),
mWindow(0),
mDisplay(display),
mGLX(glx),
mGLXDisplay(glxDisplay),
mContext(context),
mFBConfig(fbConfig),
mGLXWindow(0)
{
}
WindowSurfaceGLX::~WindowSurfaceGLX()
{
if (mGLXWindow)
{
mGLX.destroyWindow(mGLXWindow);
}
if (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();
}
egl::Error WindowSurfaceGLX::initialize(const DisplayImpl *displayImpl)
{
// Check that the window's visual ID is valid, as part of the AMGLE_x11_visual
// extension.
{
XWindowAttributes windowAttributes;
XGetWindowAttributes(mDisplay, mParent, &windowAttributes);
unsigned long visualId = windowAttributes.visual->visualid;
if (!mGLXDisplay->isValidWindowVisualId(visualId))
{
return egl::Error(EGL_BAD_MATCH,
"The visual of native_window doesn't match the visual given with "
"ANGLE_X11_VISUAL_ID");
}
}
// 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.
XVisualInfo *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 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, mWindow, nullptr);
XMapWindow(mDisplay, mWindow);
XFlush(mDisplay);
XFree(visualInfo);
XFreeColormap(mDisplay, colormap);
mGLXDisplay->syncXCommands();
return egl::Error(EGL_SUCCESS);
}
egl::Error WindowSurfaceGLX::makeCurrent()
{
if (mGLX.makeCurrent(mGLXWindow, mContext) != True)
{
return egl::Error(EGL_BAD_DISPLAY);
}
return egl::Error(EGL_SUCCESS);
}
egl::Error WindowSurfaceGLX::swap(const DisplayImpl *displayImpl)
{
// 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);
egl::Error error = checkForResize();
if (error.isError())
{
return error;
}
return egl::Error(EGL_SUCCESS);
}
egl::Error WindowSurfaceGLX::postSubBuffer(EGLint x, EGLint y, EGLint width, EGLint height)
{
UNIMPLEMENTED();
return egl::Error(EGL_SUCCESS);
}
egl::Error WindowSurfaceGLX::querySurfacePointerANGLE(EGLint attribute, void **value)
{
UNIMPLEMENTED();
return egl::Error(EGL_SUCCESS);
}
egl::Error WindowSurfaceGLX::bindTexImage(gl::Texture *texture, EGLint buffer)
{
UNIMPLEMENTED();
return egl::Error(EGL_SUCCESS);
}
egl::Error WindowSurfaceGLX::releaseTexImage(EGLint buffer)
{
UNIMPLEMENTED();
return egl::Error(EGL_SUCCESS);
}
void WindowSurfaceGLX::setSwapInterval(EGLint interval)
{
mSwapControl.targetSwapInterval = interval;
}
EGLint WindowSurfaceGLX::getWidth() const
{
// The size of the window is always the same as the cached size of its parent.
return mParentWidth;
}
EGLint WindowSurfaceGLX::getHeight() const
{
// The size of the window is always the same as the cached size of its parent.
return mParentHeight;
}
EGLint WindowSurfaceGLX::isPostSubBufferSupported() const
{
UNIMPLEMENTED();
return EGL_FALSE;
}
EGLint WindowSurfaceGLX::getSwapBehavior() const
{
return EGL_BUFFER_PRESERVED;
}
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::Error(EGL_SUCCESS);
}
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;
}
} // namespace rx