0d7bdba43603e37915690c6e1f9b488d304cf3fc
[WebKit-https.git] / Source / WebCore / platform / gtk / RedirectedXCompositeWindow.cpp
1 /*
2  * Copyright (C) 2012 Igalia S.L.
3  * All rights reserved.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions
7  * are met:
8  * 1. Redistributions of source code must retain the above copyright
9  *    notice, this list of conditions and the following disclaimer.
10  * 2. Redistributions in binary form must reproduce the above copyright
11  *    notice, this list of conditions and the following disclaimer in the
12  *    documentation and/or other materials provided with the distribution.
13  *
14  * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY
15  * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
17  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL APPLE COMPUTER, INC. OR
18  * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
19  * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
20  * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
21  * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
22  * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
23  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
24  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
25  */
26
27 #include "config.h"
28 #include "RedirectedXCompositeWindow.h"
29
30 #if USE(GLX)
31 #include "GLContextGLX.h"
32 #include <GL/glx.h>
33 #include <X11/extensions/Xcomposite.h>
34 #include <cairo-xlib.h>
35 #include <gdk/gdkx.h>
36 #include <glib.h>
37 #include <gtk/gtk.h>
38
39 namespace WebCore {
40
41 PassOwnPtr<RedirectedXCompositeWindow> RedirectedXCompositeWindow::create(const IntSize& size)
42 {
43     return adoptPtr(new RedirectedXCompositeWindow(size));
44 }
45
46 RedirectedXCompositeWindow::RedirectedXCompositeWindow(const IntSize& size)
47     : m_usableSize(size)
48     , m_window(0)
49     , m_parentWindow(0)
50     , m_pixmap(0)
51     , m_surface(0)
52     , m_pendingResizeSourceId(0)
53     , m_needsNewPixmapAfterResize(false)
54 {
55     Display* display = GLContextGLX::sharedDisplay();
56
57     // This is based on code from Chromium: src/content/common/gpu/image_transport_surface_linux.cc
58     XSetWindowAttributes windowAttributes;
59     windowAttributes.override_redirect = True;
60     m_parentWindow = XCreateWindow(display,
61                                    RootWindow(display, DefaultScreen(display)),
62                                    -100, -100, 1, 1,
63                                    0,
64                                    CopyFromParent,
65                                    InputOutput,
66                                    CopyFromParent,
67                                    CWOverrideRedirect,
68                                    &windowAttributes);
69     XMapWindow(display, m_parentWindow);
70
71     windowAttributes.event_mask = StructureNotifyMask;
72     windowAttributes.override_redirect = False;
73     m_window = XCreateWindow(display,
74                              m_parentWindow,
75                              0, 0, size.width(), size.height(),
76                              0,
77                              CopyFromParent,
78                              InputOutput,
79                              CopyFromParent,
80                              CWEventMask,
81                              &windowAttributes);
82     XMapWindow(display, m_window);
83
84     while (1) {
85         XEvent event;
86         XWindowEvent(display, m_window, StructureNotifyMask, &event);
87         if (event.type == MapNotify && event.xmap.window == m_window)
88             break;
89     }
90     XSelectInput(display, m_window, NoEventMask);
91
92     XCompositeRedirectWindow(display, m_window, CompositeRedirectManual);
93
94     resize(size);
95 }
96
97 RedirectedXCompositeWindow::~RedirectedXCompositeWindow()
98 {
99     Display* display = GLContextGLX::sharedDisplay();
100     if (m_window)
101         XDestroyWindow(display, m_window);
102     if (m_parentWindow)
103         XDestroyWindow(display, m_parentWindow);
104     cleanupPixmapAndPixmapSurface();
105
106     if (m_pendingResizeSourceId)
107         g_source_remove(m_pendingResizeSourceId);
108 }
109
110 gboolean RedirectedXCompositeWindow::resizeLaterCallback(RedirectedXCompositeWindow* window)
111 {
112     window->resizeLater();
113     return FALSE;
114 }
115
116 void RedirectedXCompositeWindow::resizeLater()
117 {
118     m_usableSize = m_size;
119     m_pendingResizeSourceId = 0;
120 }
121
122 void RedirectedXCompositeWindow::resize(const IntSize& size)
123 {
124     // When enlarging a redirected window, for the first render, the newly exposed areas seem
125     // to contain uninitialized memory on Intel drivers. To avoid rendering artifacts while
126     // resizing, we wait to render those new areas until after a short timeout. Thus, the
127     // "usable size" of the window is smaller than the actual size of the window for the first
128     // render.
129     m_usableSize = size.shrunkTo(m_usableSize);
130     if (m_usableSize.width() < size.width() || m_usableSize.height() < size.height()) { // The window is growing.
131         // We're being very conservative here. Instead of risking drawing artifacts while doing continuous
132         // opaque resizing, we err on the side of having more undrawn areas.
133         if (m_pendingResizeSourceId)
134             g_source_remove(m_pendingResizeSourceId);
135         m_pendingResizeSourceId = g_timeout_add(0, reinterpret_cast<GSourceFunc>(resizeLaterCallback), this);
136     }
137
138     Display* display = GLContextGLX::sharedDisplay();
139     XResizeWindow(display, m_window, size.width(), size.height());
140
141     XFlush(display);
142     glXWaitX();
143
144     // This swap is based on code in Chromium. It tries to work-around a bug in the Intel drivers
145     // where a swap is necessary to ensure the front and back buffers are properly resized.
146     if (context() == GLContext::getCurrent())
147         context()->swapBuffers();
148
149     m_size = size;
150     m_needsNewPixmapAfterResize = true;
151 }
152
153 GLContext* RedirectedXCompositeWindow::context()
154 {
155     if (m_context)
156         return m_context.get();
157
158     ASSERT(m_window);
159     m_context = GLContext::createContextForWindow(m_window, GLContext::sharingContext());
160     return m_context.get();
161 }
162
163 void RedirectedXCompositeWindow::cleanupPixmapAndPixmapSurface()
164 {
165     if (!m_pixmap)
166         return;
167
168     XFreePixmap(cairo_xlib_surface_get_display(m_surface.get()), m_pixmap);
169     m_pixmap = 0;
170     m_surface = nullptr;
171 }
172
173 cairo_surface_t* RedirectedXCompositeWindow::cairoSurfaceForWidget(GtkWidget* widget)
174 {
175     if (!m_needsNewPixmapAfterResize && m_surface)
176         return m_surface.get();
177
178     m_needsNewPixmapAfterResize = false;
179
180     // It's important that the new pixmap be created with the same Display pointer as the target
181     // widget or else drawing speed can be 100x slower.
182     Display* newPixmapDisplay = GDK_DISPLAY_XDISPLAY(gtk_widget_get_display(widget));
183     Pixmap newPixmap = XCompositeNameWindowPixmap(newPixmapDisplay, m_window);
184     if (!newPixmap) {
185         cleanupPixmapAndPixmapSurface();
186         return 0;
187     }
188
189     XWindowAttributes windowAttributes;
190     if (!XGetWindowAttributes(newPixmapDisplay, m_window, &windowAttributes)) {
191         cleanupPixmapAndPixmapSurface();
192         XFreePixmap(newPixmapDisplay, newPixmap);
193         return 0;
194     }
195
196     RefPtr<cairo_surface_t> newSurface = adoptRef(cairo_xlib_surface_create(newPixmapDisplay, newPixmap,
197                                                                             windowAttributes.visual,
198                                                                             m_size.width(), m_size.height()));
199
200     // Nvidia drivers seem to prepare their redirected window pixmap asynchronously, so for a few fractions
201     // of a second after each resize, while doing continuous resizing (which constantly destroys and creates
202     // pixmap window-backings), the pixmap memory is uninitialized. To work around this issue, paint the old
203     // pixmap to the new one to properly initialize it.
204     if (m_surface) {
205         RefPtr<cairo_t> cr = adoptRef(cairo_create(newSurface.get()));
206         cairo_set_source_rgb(cr.get(), 1, 1, 1);
207         cairo_paint(cr.get());
208         cairo_set_source_surface(cr.get(), m_surface.get(), 0, 0);
209         cairo_paint(cr.get());
210     }
211
212     cleanupPixmapAndPixmapSurface();
213     m_pixmap = newPixmap;
214     m_surface = newSurface;
215
216     return m_surface.get();
217 }
218
219 } // namespace WebCore
220
221 #endif // USE(GLX)