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(OPENGL) && PLATFORM(X11) 31 32#include <X11/extensions/Xcomposite.h> 33#include <X11/extensions/Xdamage.h> 34#include <cairo-xlib.h> 35#include <gdk/gdkx.h> 36#include <glib.h> 37#include <gtk/gtk.h> 38#include <wtf/HashMap.h> 39 40namespace WebCore { 41 42typedef HashMap<Window, RedirectedXCompositeWindow*> WindowHashMap; 43static WindowHashMap& getWindowHashMap() 44{ 45 DEFINE_STATIC_LOCAL(WindowHashMap, windowHashMap, ()); 46 return windowHashMap; 47} 48 49static int gDamageEventBase; 50static GdkFilterReturn filterXDamageEvent(GdkXEvent* gdkXEvent, GdkEvent* event, void*) 51{ 52 XEvent* xEvent = static_cast<XEvent*>(gdkXEvent); 53 if (xEvent->type != gDamageEventBase + XDamageNotify) 54 return GDK_FILTER_CONTINUE; 55 56 XDamageNotifyEvent* damageEvent = reinterpret_cast<XDamageNotifyEvent*>(xEvent); 57 WindowHashMap& windowHashMap = getWindowHashMap(); 58 WindowHashMap::iterator i = windowHashMap.find(damageEvent->drawable); 59 if (i == windowHashMap.end()) 60 return GDK_FILTER_CONTINUE; 61 62 i->value->callDamageNotifyCallback(); 63 XDamageSubtract(GDK_DISPLAY_XDISPLAY(gdk_display_get_default()), damageEvent->damage, None, None); 64 return GDK_FILTER_REMOVE; 65} 66 67static bool supportsXDamageAndXComposite() 68{ 69 static bool initialized = false; 70 static bool hasExtensions = false; 71 72 if (initialized) 73 return hasExtensions; 74 75 initialized = true; 76 77 int errorBase; 78 Display* display = GDK_DISPLAY_XDISPLAY(gdk_display_get_default()); 79 if (!XDamageQueryExtension(display, &gDamageEventBase, &errorBase)) 80 return false; 81 82 int eventBase; 83 if (!XCompositeQueryExtension(display, &eventBase, &errorBase)) 84 return false; 85 86 // We need to support XComposite version 0.2. 87 int major, minor; 88 XCompositeQueryVersion(display, &major, &minor); 89 if (major < 0 || (!major && minor < 2)) 90 return false; 91 92 hasExtensions = true; 93 return true; 94} 95 96PassOwnPtr<RedirectedXCompositeWindow> RedirectedXCompositeWindow::create(const IntSize& size, GLContextNeeded needsContext) 97{ 98 return supportsXDamageAndXComposite() ? adoptPtr(new RedirectedXCompositeWindow(size, needsContext)) : nullptr; 99} 100 101RedirectedXCompositeWindow::RedirectedXCompositeWindow(const IntSize& size, GLContextNeeded needsContext) 102 : m_size(size) 103 , m_window(0) 104 , m_parentWindow(0) 105 , m_pixmap(0) 106 , m_needsContext(needsContext) 107 , m_surface(0) 108 , m_needsNewPixmapAfterResize(false) 109 , m_damage(0) 110 , m_damageNotifyCallback(0) 111 , m_damageNotifyData(0) 112{ 113 Display* display = GDK_DISPLAY_XDISPLAY(gdk_display_get_default()); 114 Screen* screen = DefaultScreenOfDisplay(display); 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 RootWindowOfScreen(screen), 121 WidthOfScreen(screen) + 1, 0, 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 158RedirectedXCompositeWindow::~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 175void 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 182 if (m_needsContext == CreateGLContext) { 183 context()->waitNative(); 184 // This swap is based on code in Chromium. It tries to work-around a bug in the Intel drivers 185 // where a swap is necessary to ensure the front and back buffers are properly resized. 186 if (context() == GLContext::getCurrent()) 187 context()->swapBuffers(); 188 } 189 190 m_size = size; 191 m_needsNewPixmapAfterResize = true; 192} 193 194GLContext* RedirectedXCompositeWindow::context() 195{ 196 ASSERT(m_needsContext == CreateGLContext); 197 198 if (m_context) 199 return m_context.get(); 200 201 ASSERT(m_window); 202 m_context = GLContext::createContextForWindow(m_window, GLContext::sharingContext()); 203 return m_context.get(); 204} 205 206void RedirectedXCompositeWindow::cleanupPixmapAndPixmapSurface() 207{ 208 if (!m_pixmap) 209 return; 210 211 XFreePixmap(cairo_xlib_surface_get_display(m_surface.get()), m_pixmap); 212 m_pixmap = 0; 213 m_surface = nullptr; 214} 215 216cairo_surface_t* RedirectedXCompositeWindow::cairoSurfaceForWidget(GtkWidget* widget) 217{ 218 if (!m_needsNewPixmapAfterResize && m_surface) 219 return m_surface.get(); 220 221 m_needsNewPixmapAfterResize = false; 222 223 // It's important that the new pixmap be created with the same Display pointer as the target 224 // widget or else drawing speed can be 100x slower. 225 Display* newPixmapDisplay = GDK_DISPLAY_XDISPLAY(gtk_widget_get_display(widget)); 226 Pixmap newPixmap = XCompositeNameWindowPixmap(newPixmapDisplay, m_window); 227 if (!newPixmap) { 228 cleanupPixmapAndPixmapSurface(); 229 return 0; 230 } 231 232 XWindowAttributes windowAttributes; 233 if (!XGetWindowAttributes(newPixmapDisplay, m_window, &windowAttributes)) { 234 cleanupPixmapAndPixmapSurface(); 235 XFreePixmap(newPixmapDisplay, newPixmap); 236 return 0; 237 } 238 239 RefPtr<cairo_surface_t> newSurface = adoptRef(cairo_xlib_surface_create(newPixmapDisplay, newPixmap, 240 windowAttributes.visual, 241 m_size.width(), m_size.height())); 242 243 // Nvidia drivers seem to prepare their redirected window pixmap asynchronously, so for a few fractions 244 // of a second after each resize, while doing continuous resizing (which constantly destroys and creates 245 // pixmap window-backings), the pixmap memory is uninitialized. To work around this issue, paint the old 246 // pixmap to the new one to properly initialize it. 247 if (m_surface) { 248 RefPtr<cairo_t> cr = adoptRef(cairo_create(newSurface.get())); 249 cairo_set_source_rgb(cr.get(), 1, 1, 1); 250 cairo_paint(cr.get()); 251 cairo_set_source_surface(cr.get(), m_surface.get(), 0, 0); 252 cairo_paint(cr.get()); 253 } 254 255 cleanupPixmapAndPixmapSurface(); 256 m_pixmap = newPixmap; 257 m_surface = newSurface; 258 259 return m_surface.get(); 260} 261 262void RedirectedXCompositeWindow::callDamageNotifyCallback() 263{ 264 if (m_damageNotifyCallback) 265 m_damageNotifyCallback(m_damageNotifyData); 266} 267 268} // namespace WebCore 269 270#endif // USE(OPENGL) && PLATFORM(X11) 271