summaryrefslogtreecommitdiffstats
path: root/gfx/angle/src/libANGLE/renderer/gl/glx/WindowSurfaceGLX.cpp
blob: 21b1b316f47dfc28848584a8920da3d8bd2f8394 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
//
// 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),
      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()
{
    // 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()
{
    // 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;
}

}