/* * Copyright (C) 2012 Igalia S.L. * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #include "config.h" #include "RedirectedXCompositeWindow.h" #if PLATFORM(X11) #include #include #include #include #include #include #include namespace WebCore { typedef HashMap WindowHashMap; static WindowHashMap& getWindowHashMap() { DEFINE_STATIC_LOCAL(WindowHashMap, windowHashMap, ()); return windowHashMap; } static int gDamageEventBase; static GdkFilterReturn filterXDamageEvent(GdkXEvent* gdkXEvent, GdkEvent* event, void*) { XEvent* xEvent = static_cast(gdkXEvent); if (xEvent->type != gDamageEventBase + XDamageNotify) return GDK_FILTER_CONTINUE; XDamageNotifyEvent* damageEvent = reinterpret_cast(xEvent); WindowHashMap& windowHashMap = getWindowHashMap(); WindowHashMap::iterator i = windowHashMap.find(damageEvent->drawable); if (i == windowHashMap.end()) return GDK_FILTER_CONTINUE; i->value->callDamageNotifyCallback(); XDamageSubtract(GDK_DISPLAY_XDISPLAY(gdk_display_get_default()), damageEvent->damage, None, None); return GDK_FILTER_REMOVE; } static bool supportsXDamageAndXComposite() { static bool initialized = false; static bool hasExtensions = false; if (initialized) return hasExtensions; initialized = true; int errorBase; Display* display = GDK_DISPLAY_XDISPLAY(gdk_display_get_default()); if (!XDamageQueryExtension(display, &gDamageEventBase, &errorBase)) return false; int eventBase; if (!XCompositeQueryExtension(display, &eventBase, &errorBase)) return false; // We need to support XComposite version 0.2. int major, minor; XCompositeQueryVersion(display, &major, &minor); if (major < 0 || (!major && minor < 2)) return false; hasExtensions = true; return true; } PassOwnPtr RedirectedXCompositeWindow::create(const IntSize& size) { return supportsXDamageAndXComposite() ? adoptPtr(new RedirectedXCompositeWindow(size)) : nullptr; } RedirectedXCompositeWindow::RedirectedXCompositeWindow(const IntSize& size) : m_size(size) , m_window(0) , m_parentWindow(0) , m_pixmap(0) , m_surface(0) , m_needsNewPixmapAfterResize(false) , m_damage(0) , m_damageNotifyCallback(0) , m_damageNotifyData(0) { Display* display = GLContext::sharedX11Display(); Screen* screen = DefaultScreenOfDisplay(display); // This is based on code from Chromium: src/content/common/gpu/image_transport_surface_linux.cc XSetWindowAttributes windowAttributes; windowAttributes.override_redirect = True; m_parentWindow = XCreateWindow(display, RootWindowOfScreen(screen), WidthOfScreen(screen) + 1, 0, 1, 1, 0, CopyFromParent, InputOutput, CopyFromParent, CWOverrideRedirect, &windowAttributes); XMapWindow(display, m_parentWindow); windowAttributes.event_mask = StructureNotifyMask; windowAttributes.override_redirect = False; m_window = XCreateWindow(display, m_parentWindow, 0, 0, size.width(), size.height(), 0, CopyFromParent, InputOutput, CopyFromParent, CWEventMask, &windowAttributes); XMapWindow(display, m_window); if (getWindowHashMap().isEmpty()) gdk_window_add_filter(0, reinterpret_cast(filterXDamageEvent), 0); getWindowHashMap().add(m_window, this); while (1) { XEvent event; XWindowEvent(display, m_window, StructureNotifyMask, &event); if (event.type == MapNotify && event.xmap.window == m_window) break; } XSelectInput(display, m_window, NoEventMask); XCompositeRedirectWindow(display, m_window, CompositeRedirectManual); m_damage = XDamageCreate(display, m_window, XDamageReportNonEmpty); } RedirectedXCompositeWindow::~RedirectedXCompositeWindow() { ASSERT(m_damage); ASSERT(m_window); ASSERT(m_parentWindow); getWindowHashMap().remove(m_window); if (getWindowHashMap().isEmpty()) gdk_window_remove_filter(0, reinterpret_cast(filterXDamageEvent), 0); Display* display = GLContext::sharedX11Display(); XDamageDestroy(display, m_damage); XDestroyWindow(display, m_window); XDestroyWindow(display, m_parentWindow); cleanupPixmapAndPixmapSurface(); } void RedirectedXCompositeWindow::resize(const IntSize& size) { Display* display = GLContext::sharedX11Display(); XResizeWindow(display, m_window, size.width(), size.height()); XFlush(display); context()->waitNative(); // This swap is based on code in Chromium. It tries to work-around a bug in the Intel drivers // where a swap is necessary to ensure the front and back buffers are properly resized. if (context() == GLContext::getCurrent()) context()->swapBuffers(); m_size = size; m_needsNewPixmapAfterResize = true; } GLContext* RedirectedXCompositeWindow::context() { if (m_context) return m_context.get(); ASSERT(m_window); m_context = GLContext::createContextForWindow(m_window, GLContext::sharingContext()); return m_context.get(); } void RedirectedXCompositeWindow::cleanupPixmapAndPixmapSurface() { if (!m_pixmap) return; XFreePixmap(cairo_xlib_surface_get_display(m_surface.get()), m_pixmap); m_pixmap = 0; m_surface = nullptr; } cairo_surface_t* RedirectedXCompositeWindow::cairoSurfaceForWidget(GtkWidget* widget) { if (!m_needsNewPixmapAfterResize && m_surface) return m_surface.get(); m_needsNewPixmapAfterResize = false; // It's important that the new pixmap be created with the same Display pointer as the target // widget or else drawing speed can be 100x slower. Display* newPixmapDisplay = GDK_DISPLAY_XDISPLAY(gtk_widget_get_display(widget)); Pixmap newPixmap = XCompositeNameWindowPixmap(newPixmapDisplay, m_window); if (!newPixmap) { cleanupPixmapAndPixmapSurface(); return 0; } XWindowAttributes windowAttributes; if (!XGetWindowAttributes(newPixmapDisplay, m_window, &windowAttributes)) { cleanupPixmapAndPixmapSurface(); XFreePixmap(newPixmapDisplay, newPixmap); return 0; } RefPtr newSurface = adoptRef(cairo_xlib_surface_create(newPixmapDisplay, newPixmap, windowAttributes.visual, m_size.width(), m_size.height())); // Nvidia drivers seem to prepare their redirected window pixmap asynchronously, so for a few fractions // of a second after each resize, while doing continuous resizing (which constantly destroys and creates // pixmap window-backings), the pixmap memory is uninitialized. To work around this issue, paint the old // pixmap to the new one to properly initialize it. if (m_surface) { RefPtr cr = adoptRef(cairo_create(newSurface.get())); cairo_set_source_rgb(cr.get(), 1, 1, 1); cairo_paint(cr.get()); cairo_set_source_surface(cr.get(), m_surface.get(), 0, 0); cairo_paint(cr.get()); } cleanupPixmapAndPixmapSurface(); m_pixmap = newPixmap; m_surface = newSurface; return m_surface.get(); } void RedirectedXCompositeWindow::callDamageNotifyCallback() { if (m_damageNotifyCallback) m_damageNotifyCallback(m_damageNotifyData); } } // namespace WebCore #endif // PLATFORM(X11)