1/*
2 * Copyright (C) 2013 Intel Corporation. All rights reserved.
3 *
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions
6 * are met:
7 * 1. Redistributions of source code must retain the above copyright
8 *    notice, this list of conditions and the following disclaimer.
9 * 2. Redistributions in binary form must reproduce the above copyright
10 *    notice, this list of conditions and the following disclaimer in the
11 *    documentation and/or other materials provided with the distribution.
12 *
13 * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
14 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
15 * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
16 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
17 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
18 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
19 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
20 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
21 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
22 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
23 * THE POSSIBILITY OF SUCH DAMAGE.
24 */
25
26#include "config.h"
27#include "X11Helper.h"
28
29namespace WebCore {
30
31// Used for handling XError.
32static bool validOperation = true;
33static int handleXPixmapCreationError(Display*, XErrorEvent* event)
34{
35    if (event->error_code == BadMatch || event->error_code == BadWindow || event->error_code == BadAlloc) {
36        validOperation = false;
37
38        switch (event->error_code) {
39        case BadMatch:
40            LOG_ERROR("BadMatch.");
41            break;
42        case BadWindow:
43            LOG_ERROR("BadWindow.");
44            break;
45        case BadAlloc:
46            LOG_ERROR("BadAlloc.");
47            break;
48        default:
49            break;
50        }
51    }
52
53    return 0;
54}
55
56struct DisplayConnection {
57    DisplayConnection()
58    {
59        m_display = XOpenDisplay(0);
60
61        if (!m_display)
62            LOG_ERROR("Failed to make connection with X");
63    }
64
65    ~DisplayConnection()
66    {
67        XCloseDisplay(m_display);
68    }
69
70    Display* display() { return m_display; }
71private:
72    Display* m_display;
73};
74
75struct OffScreenRootWindow {
76
77    OffScreenRootWindow()
78    {
79        m_window = 0;
80        Display* dpy = X11Helper::nativeDisplay();
81        if (!dpy)
82            return;
83
84        XSetWindowAttributes attributes;
85        attributes.override_redirect = true;
86        m_window = XCreateSimpleWindow(dpy, XDefaultRootWindow(dpy), -1, -1, 1, 1, 0, BlackPixel(dpy, 0), WhitePixel(dpy, 0));
87        // From http://tronche.com/gui/x/xlib/window/attributes/
88        XChangeWindowAttributes(dpy, m_window, CWOverrideRedirect, &attributes);
89        XMapWindow(dpy, m_window);
90
91        if (!m_window)
92            LOG_ERROR("Failed to create offscreen root window.");
93    }
94
95    ~OffScreenRootWindow()
96    {
97        if (!X11Helper::nativeDisplay())
98            return;
99
100        if (m_window) {
101            XUnmapWindow(X11Helper::nativeDisplay(), m_window);
102            XDestroyWindow(X11Helper::nativeDisplay(), m_window);
103            m_window = 0;
104        }
105    }
106
107    Window rootWindow()
108    {
109        return m_window;
110    }
111
112private:
113    Window m_window;
114};
115
116ScopedXPixmapCreationErrorHandler::ScopedXPixmapCreationErrorHandler()
117{
118    // XSync must be called to ensure that current errors are handled by the original handler.
119    XSync(X11Helper::nativeDisplay(), false);
120    m_previousErrorHandler = XSetErrorHandler(handleXPixmapCreationError);
121}
122
123ScopedXPixmapCreationErrorHandler::~ScopedXPixmapCreationErrorHandler()
124{
125    // Restore the original handler.
126    XSetErrorHandler(m_previousErrorHandler);
127}
128
129bool ScopedXPixmapCreationErrorHandler::isValidOperation() const
130{
131    validOperation = true;
132    // XSync is needed to catch possible errors as they are generated asynchronously.
133    XSync(X11Helper::nativeDisplay(), false);
134    return validOperation;
135}
136
137void X11Helper::resizeWindow(const IntRect& newRect, const uint32_t windowId)
138{
139    XResizeWindow(nativeDisplay(), windowId, newRect.width(), newRect.height());
140    XFlush(nativeDisplay());
141}
142
143void X11Helper::createPixmap(Pixmap* handleId, const XVisualInfo& visualInfo, const IntSize& size)
144{
145    *handleId = 0;
146    Display* display = nativeDisplay();
147    if (!display)
148        return;
149
150    if (!visualInfo.visual) {
151        LOG_ERROR("Failed to find valid XVisual.");
152        return;
153    }
154
155    Window xWindow = offscreenRootWindow();
156    if (!xWindow) {
157        LOG_ERROR("Failed to create offscreen root window.");
158        return;
159    }
160
161    Pixmap tempHandleId = XCreatePixmap(display, xWindow, size.width(), size.height(), visualInfo.depth);
162
163    if (!tempHandleId) {
164        LOG_ERROR("Failed to create offscreen pixmap.");
165        return;
166    }
167
168    *handleId = tempHandleId;
169    XSync(X11Helper::nativeDisplay(), false);
170}
171
172void X11Helper::destroyPixmap(const uint32_t pixmapId)
173{
174    if (!pixmapId)
175        return;
176
177    Display* display = nativeDisplay();
178    if (!display)
179        return;
180
181    XFreePixmap(display, pixmapId);
182    XSync(X11Helper::nativeDisplay(), false);
183}
184
185void X11Helper::createOffScreenWindow(uint32_t* handleId, const XVisualInfo& visInfo, const IntSize& size)
186{
187#if USE(GRAPHICS_SURFACE)
188    Display* display = nativeDisplay();
189    if (!display)
190        return;
191
192    if (!visInfo.visual) {
193        LOG_ERROR("Failed to find valid XVisual.");
194        return;
195    }
196
197    Window xWindow = offscreenRootWindow();
198    if (!xWindow)
199        return;
200
201    Colormap cmap = XCreateColormap(display, xWindow, visInfo.visual, AllocNone);
202    XSetWindowAttributes attribute;
203    attribute.background_pixel = WhitePixel(display, 0);
204    attribute.border_pixel = BlackPixel(display, 0);
205    attribute.colormap = cmap;
206#if USE(GLX)
207    attribute.event_mask = ResizeRedirectMask;
208#endif
209    uint32_t tempHandleId = XCreateWindow(display, xWindow, 0, 0, size.width(), size.height(), 0, visInfo.depth, InputOutput, visInfo.visual, CWBackPixel | CWBorderPixel | CWColormap, &attribute);
210
211    if (!tempHandleId) {
212        LOG_ERROR("Failed to create offscreen window.");
213        return;
214    }
215
216    XSetWindowBackgroundPixmap(display, tempHandleId, 0);
217#if USE(GLX)
218    XCompositeRedirectWindow(display, tempHandleId, CompositeRedirectManual);
219#endif
220    XMapWindow(display, tempHandleId);
221    *handleId = tempHandleId;
222#else
223    UNUSED_PARAM(handleId);
224    UNUSED_PARAM(visInfo);
225    UNUSED_PARAM(size);
226#endif
227}
228
229#if USE(EGL)
230void X11Helper::createOffScreenWindow(uint32_t* handleId, const EGLint id, bool supportsAlpha, const IntSize& size)
231{
232#if USE(GRAPHICS_SURFACE)
233    VisualID visualId = static_cast<VisualID>(id);
234
235    if (!visualId)
236        return;
237
238    // EGL has suggested a visual id, so get the rest of the visual info for that id.
239    XVisualInfo visualInfoTemplate;
240    memset(&visualInfoTemplate, 0, sizeof(XVisualInfo));
241    visualInfoTemplate.visualid = visualId;
242    int matchingCount = 0;
243    OwnPtrX11<XVisualInfo> matchingVisuals(XGetVisualInfo(nativeDisplay(), VisualIDMask, &visualInfoTemplate, &matchingCount));
244    XVisualInfo* foundVisual = 0;
245
246    if (matchingVisuals) {
247        for (int i = 0; i< matchingCount; i++) {
248            XVisualInfo* temp = &matchingVisuals[i];
249            int matchingdepth = supportsAlpha ? 32 : 24;
250
251            if (temp->visualid == visualId && temp->depth == matchingdepth) {
252                foundVisual = temp;
253                break;
254            }
255        }
256
257        if (foundVisual)
258            createOffScreenWindow(handleId, *foundVisual, size);
259    }
260#else
261    UNUSED_PARAM(handleId);
262    UNUSED_PARAM(id);
263    UNUSED_PARAM(size);
264#endif
265}
266
267void X11Helper::createPixmap(Pixmap* handleId, const EGLint id, bool hasAlpha, const IntSize& size)
268{
269    *handleId = 0;
270    VisualID visualId = static_cast<VisualID>(id);
271
272    if (!visualId)
273        return;
274
275    // EGL has suggested a visual id, so get the rest of the visual info for that id.
276    XVisualInfo visualInfoTemplate;
277    memset(&visualInfoTemplate, 0, sizeof(XVisualInfo));
278    visualInfoTemplate.visualid = visualId;
279    int matchingCount = 0;
280    OwnPtrX11<XVisualInfo> matchingVisuals(XGetVisualInfo(nativeDisplay(), VisualIDMask, &visualInfoTemplate, &matchingCount));
281    XVisualInfo* foundVisual = 0;
282    int requiredDepth = hasAlpha ? 32 : 24;
283
284    if (matchingVisuals) {
285        for (int i = 0; i< matchingCount; i++) {
286            XVisualInfo* temp = &matchingVisuals[i];
287
288            if (temp->visualid == visualId && temp->depth == requiredDepth) {
289                foundVisual = temp;
290                break;
291            }
292        }
293
294        if (foundVisual)
295            createPixmap(handleId, *foundVisual, size);
296    }
297}
298#endif
299
300void X11Helper::destroyWindow(const uint32_t windowId)
301{
302    if (!windowId)
303        return;
304
305    Display* display = nativeDisplay();
306    if (!display)
307        return;
308
309    XDestroyWindow(display, windowId);
310}
311
312bool X11Helper::isXRenderExtensionSupported()
313{
314    static bool queryDone = false;
315    static bool supportsXRenderExtension = false;
316
317    if (!queryDone) {
318        queryDone = true;
319#if USE(GRAPHICS_SURFACE) && USE(GLX)
320        Display* display = nativeDisplay();
321
322        if (display) {
323            int eventBasep, errorBasep;
324            supportsXRenderExtension = XRenderQueryExtension(display, &eventBasep, &errorBasep);
325        }
326#endif
327    }
328
329    return supportsXRenderExtension;
330}
331
332Display* X11Helper::nativeDisplay()
333{
334    // Display connection will only be broken at program shutdown.
335    static DisplayConnection displayConnection;
336    return displayConnection.display();
337}
338
339Window X11Helper::offscreenRootWindow()
340{
341    static OffScreenRootWindow offscreenWindow;
342    return offscreenWindow.rootWindow();
343}
344
345}
346