[GTK] Use XDamage to simplify RedirectedXCompositeWindow
[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 <X11/extensions/Xdamage.h>
35 #include <cairo-xlib.h>
36 #include <gdk/gdkx.h>
37 #include <glib.h>
38 #include <gtk/gtk.h>
39 #include <wtf/HashMap.h>
40
41 namespace WebCore {
42
43 typedef HashMap<Window, RedirectedXCompositeWindow*> WindowHashMap;
44 static WindowHashMap& getWindowHashMap()
45 {
46     DEFINE_STATIC_LOCAL(WindowHashMap, windowHashMap, ());
47     return windowHashMap;
48 }
49
50 static int gDamageEventBase;
51 static GdkFilterReturn filterXDamageEvent(GdkXEvent* gdkXEvent, GdkEvent* event, void*)
52 {
53     XEvent* xEvent = static_cast<XEvent*>(gdkXEvent);
54     if (xEvent->type != gDamageEventBase + XDamageNotify)
55         return GDK_FILTER_CONTINUE;
56
57     XDamageNotifyEvent* damageEvent = reinterpret_cast<XDamageNotifyEvent*>(xEvent);
58     WindowHashMap& windowHashMap = getWindowHashMap();
59     WindowHashMap::iterator i = windowHashMap.find(damageEvent->drawable);
60     if (i == windowHashMap.end())
61         return GDK_FILTER_CONTINUE;
62
63     i->second->callDamageNotifyCallback();
64     XDamageSubtract(GDK_DISPLAY_XDISPLAY(gdk_display_get_default()), damageEvent->damage, None, None);
65     return GDK_FILTER_REMOVE;
66 }
67
68 static bool supportsXDamageAndXComposite()
69 {
70     static bool initialized = false;
71     static bool hasExtensions = false;
72
73     if (initialized)
74         return hasExtensions;
75
76     initialized = true;
77
78     int errorBase;
79     Display* display = GDK_DISPLAY_XDISPLAY(gdk_display_get_default());
80     if (!XDamageQueryExtension(display, &gDamageEventBase, &errorBase))
81         return false;
82
83     int eventBase;
84     if (!XCompositeQueryExtension(display, &eventBase, &errorBase))
85         return false;
86
87     // We need to support XComposite version 0.2.
88     int major, minor;
89     XCompositeQueryVersion(display, &major, &minor);
90     if (major < 0 || (!major && minor < 2))
91         return false;
92
93     hasExtensions = true;
94     return true;
95 }
96
97 PassOwnPtr<RedirectedXCompositeWindow> RedirectedXCompositeWindow::create(const IntSize& size)
98 {
99     return supportsXDamageAndXComposite() ? adoptPtr(new RedirectedXCompositeWindow(size)) : nullptr;
100 }
101
102 RedirectedXCompositeWindow::RedirectedXCompositeWindow(const IntSize& size)
103     : m_size(size)
104     , m_window(0)
105     , m_parentWindow(0)
106     , m_pixmap(0)
107     , m_surface(0)
108     , m_pendingResizeSourceId(0)
109     , m_needsNewPixmapAfterResize(false)
110     , m_damage(0)
111     , m_damageNotifyCallback(0)
112     , m_damageNotifyData(0)
113 {
114     Display* display = GDK_DISPLAY_XDISPLAY(gdk_display_get_default());
115
116     // This is based on code from Chromium: src/content/common/gpu/image_transport_surface_linux.cc
117     XSetWindowAttributes windowAttributes;
118     windowAttributes.override_redirect = True;
119     m_parentWindow = XCreateWindow(display,
120                                    RootWindow(display, DefaultScreen(display)),
121                                    -100, -100, 1, 1,
122                                    0,
123                                    CopyFromParent,
124                                    InputOutput,
125                                    CopyFromParent,
126                                    CWOverrideRedirect,
127                                    &windowAttributes);
128     XMapWindow(display, m_parentWindow);
129
130     windowAttributes.event_mask = StructureNotifyMask;
131     windowAttributes.override_redirect = False;
132     m_window = XCreateWindow(display,
133                              m_parentWindow,
134                              0, 0, size.width(), size.height(),
135                              0,
136                              CopyFromParent,
137                              InputOutput,
138                              CopyFromParent,
139                              CWEventMask,
140                              &windowAttributes);
141     XMapWindow(display, m_window);
142
143     if (getWindowHashMap().isEmpty())
144         gdk_window_add_filter(0, reinterpret_cast<GdkFilterFunc>(filterXDamageEvent), 0);
145     getWindowHashMap().add(m_window, this);
146
147     while (1) {
148         XEvent event;
149         XWindowEvent(display, m_window, StructureNotifyMask, &event);
150         if (event.type == MapNotify && event.xmap.window == m_window)
151             break;
152     }
153     XSelectInput(display, m_window, NoEventMask);
154     XCompositeRedirectWindow(display, m_window, CompositeRedirectManual);
155     m_damage = XDamageCreate(display, m_window, XDamageReportNonEmpty);
156 }
157
158 RedirectedXCompositeWindow::~RedirectedXCompositeWindow()
159 {
160     ASSERT(m_damage);
161     ASSERT(m_window);
162     ASSERT(m_parentWindow);
163
164     getWindowHashMap().remove(m_window);
165     if (getWindowHashMap().isEmpty())
166         gdk_window_remove_filter(0, reinterpret_cast<GdkFilterFunc>(filterXDamageEvent), 0);
167
168     Display* display = GDK_DISPLAY_XDISPLAY(gdk_display_get_default());
169     XDamageDestroy(display, m_damage);
170     XDestroyWindow(display, m_window);
171     XDestroyWindow(display, m_parentWindow);
172     cleanupPixmapAndPixmapSurface();
173 }
174
175 void RedirectedXCompositeWindow::resize(const IntSize& size)
176 {
177     Display* display = GDK_DISPLAY_XDISPLAY(gdk_display_get_default());
178     XResizeWindow(display, m_window, size.width(), size.height());
179
180     XFlush(display);
181     glXWaitX();
182
183     // This swap is based on code in Chromium. It tries to work-around a bug in the Intel drivers
184     // where a swap is necessary to ensure the front and back buffers are properly resized.
185     if (context() == GLContext::getCurrent())
186         context()->swapBuffers();
187
188     m_size = size;
189     m_needsNewPixmapAfterResize = true;
190 }
191
192 GLContext* RedirectedXCompositeWindow::context()
193 {
194     if (m_context)
195         return m_context.get();
196
197     ASSERT(m_window);
198     m_context = GLContext::createContextForWindow(m_window, GLContext::sharingContext());
199     return m_context.get();
200 }
201
202 void RedirectedXCompositeWindow::cleanupPixmapAndPixmapSurface()
203 {
204     if (!m_pixmap)
205         return;
206
207     XFreePixmap(cairo_xlib_surface_get_display(m_surface.get()), m_pixmap);
208     m_pixmap = 0;
209     m_surface = nullptr;
210 }
211
212 cairo_surface_t* RedirectedXCompositeWindow::cairoSurfaceForWidget(GtkWidget* widget)
213 {
214     if (!m_needsNewPixmapAfterResize && m_surface)
215         return m_surface.get();
216
217     m_needsNewPixmapAfterResize = false;
218
219     // It's important that the new pixmap be created with the same Display pointer as the target
220     // widget or else drawing speed can be 100x slower.
221     Display* newPixmapDisplay = GDK_DISPLAY_XDISPLAY(gtk_widget_get_display(widget));
222     Pixmap newPixmap = XCompositeNameWindowPixmap(newPixmapDisplay, m_window);
223     if (!newPixmap) {
224         cleanupPixmapAndPixmapSurface();
225         return 0;
226     }
227
228     XWindowAttributes windowAttributes;
229     if (!XGetWindowAttributes(newPixmapDisplay, m_window, &windowAttributes)) {
230         cleanupPixmapAndPixmapSurface();
231         XFreePixmap(newPixmapDisplay, newPixmap);
232         return 0;
233     }
234
235     RefPtr<cairo_surface_t> newSurface = adoptRef(cairo_xlib_surface_create(newPixmapDisplay, newPixmap,
236                                                                             windowAttributes.visual,
237                                                                             m_size.width(), m_size.height()));
238
239     // Nvidia drivers seem to prepare their redirected window pixmap asynchronously, so for a few fractions
240     // of a second after each resize, while doing continuous resizing (which constantly destroys and creates
241     // pixmap window-backings), the pixmap memory is uninitialized. To work around this issue, paint the old
242     // pixmap to the new one to properly initialize it.
243     if (m_surface) {
244         RefPtr<cairo_t> cr = adoptRef(cairo_create(newSurface.get()));
245         cairo_set_source_rgb(cr.get(), 1, 1, 1);
246         cairo_paint(cr.get());
247         cairo_set_source_surface(cr.get(), m_surface.get(), 0, 0);
248         cairo_paint(cr.get());
249     }
250
251     cleanupPixmapAndPixmapSurface();
252     m_pixmap = newPixmap;
253     m_surface = newSurface;
254
255     return m_surface.get();
256 }
257
258 void RedirectedXCompositeWindow::callDamageNotifyCallback()
259 {
260     if (m_damageNotifyCallback)
261         m_damageNotifyCallback(m_damageNotifyData);
262 }
263
264 } // namespace WebCore
265
266 #endif // USE(GLX)