blob: 2af34a86285ca15d77628834f9118b8f4441e045 [file] [log] [blame]
//
// 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 "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,
Window window,
Display *display,
glx::FBConfig fbConfig)
: SurfaceGLX(state),
mParent(window),
mWindow(0),
mDisplay(display),
mGLX(glx),
mGLXDisplay(glxDisplay),
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 egl::Display *display)
{
// 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::EglBadMatch() << "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::EglBadNativeWindow() << "Failed to get the XVisualInfo for the child window.";
}
Visual *visual = visualInfo->visual;
if (!getWindowDimensions(mParent, &mParentWidth, &mParentHeight))
{
return egl::EglBadNativeWindow() << "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::EglBadNativeWindow() << "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);
XSelectInput(mDisplay, mWindow, ExposureMask); // For XExposeEvent forwarding from child window
XFlush(mDisplay);
XFree(visualInfo);
XFreeColormap(mDisplay, colormap);
mGLXDisplay->syncXCommands();
return egl::NoError();
}
egl::Error WindowSurfaceGLX::makeCurrent(const gl::Context *context)
{
return egl::NoError();
}
egl::Error WindowSurfaceGLX::swap(const gl::Context *context)
{
// 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::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(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_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::EglBadCurrentSurface() << "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::EglBadSurface() << "glXGetSyncValuesOML failed.";
}
return egl::NoError();
}
} // namespace rx