/* * Copyright (C) 2016 Igalia S.L. * * 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 "AcceleratedBackingStoreX11.h" #if USE(REDIRECTED_XCOMPOSITE_WINDOW) #include "DrawingAreaProxyImpl.h" #include "LayerTreeContext.h" #include "WebPageProxy.h" #include #include #include #include #include #include #include #include #include #include using namespace WebCore; namespace WebKit { static std::optional s_damageEventBase; static std::optional s_damageErrorBase; class XDamageNotifier { WTF_MAKE_NONCOPYABLE(XDamageNotifier); friend class NeverDestroyed; public: static XDamageNotifier& singleton() { static NeverDestroyed notifier; return notifier; } void add(Damage damage, std::function&& notifyFunction) { if (m_notifyFunctions.isEmpty()) gdk_window_add_filter(nullptr, reinterpret_cast(&filterXDamageEvent), this); m_notifyFunctions.add(damage, WTFMove(notifyFunction)); } void remove(Damage damage) { m_notifyFunctions.remove(damage); if (m_notifyFunctions.isEmpty()) gdk_window_remove_filter(nullptr, reinterpret_cast(&filterXDamageEvent), this); } private: XDamageNotifier() = default; static GdkFilterReturn filterXDamageEvent(GdkXEvent* event, GdkEvent*, XDamageNotifier* notifier) { auto* xEvent = static_cast(event); if (xEvent->type != s_damageEventBase.value() + XDamageNotify) return GDK_FILTER_CONTINUE; auto* damageEvent = reinterpret_cast(xEvent); if (notifier->notify(damageEvent->damage)) { XDamageSubtract(xEvent->xany.display, damageEvent->damage, None, None); return GDK_FILTER_REMOVE; } return GDK_FILTER_CONTINUE; } bool notify(Damage damage) const { if (const auto& notifyFunction = m_notifyFunctions.get(damage)) { notifyFunction(); return true; } return false; } HashMap> m_notifyFunctions; }; std::unique_ptr AcceleratedBackingStoreX11::create(WebPageProxy& webPage) { auto& display = downcast(PlatformDisplay::sharedDisplay()); if (!display.supportsXComposite() || !display.supportsXDamage(s_damageEventBase, s_damageErrorBase)) return nullptr; return std::unique_ptr(new AcceleratedBackingStoreX11(webPage)); } AcceleratedBackingStoreX11::AcceleratedBackingStoreX11(WebPageProxy& webPage) : AcceleratedBackingStore(webPage) { } static inline unsigned char xDamageErrorCode(unsigned char errorCode) { ASSERT(s_damageErrorBase); return static_cast(s_damageErrorBase.value()) + errorCode; } AcceleratedBackingStoreX11::~AcceleratedBackingStoreX11() { if (!m_surface && !m_damage) return; Display* display = downcast(PlatformDisplay::sharedDisplay()).native(); XErrorTrapper trapper(display, XErrorTrapper::Policy::Crash, { BadDrawable, xDamageErrorCode(BadDamage) }); if (m_damage) { XDamageNotifier::singleton().remove(m_damage.get()); m_damage.reset(); XSync(display, False); } } void AcceleratedBackingStoreX11::update(const LayerTreeContext& layerTreeContext) { Pixmap pixmap = layerTreeContext.contextID; if (m_surface && cairo_xlib_surface_get_drawable(m_surface.get()) == pixmap) return; Display* display = downcast(PlatformDisplay::sharedDisplay()).native(); if (m_surface) { XErrorTrapper trapper(display, XErrorTrapper::Policy::Crash, { BadDrawable, xDamageErrorCode(BadDamage) }); if (m_damage) { XDamageNotifier::singleton().remove(m_damage.get()); m_damage.reset(); XSync(display, False); } m_surface = nullptr; } if (!pixmap) return; DrawingAreaProxyImpl* drawingArea = static_cast(m_webPage.drawingArea()); if (!drawingArea) return; IntSize size = drawingArea->size(); float deviceScaleFactor = m_webPage.deviceScaleFactor(); size.scale(deviceScaleFactor); XErrorTrapper trapper(display, XErrorTrapper::Policy::Crash, { BadDrawable, xDamageErrorCode(BadDamage) }); ASSERT(downcast(PlatformDisplay::sharedDisplay()).native() == GDK_DISPLAY_XDISPLAY(gdk_display_get_default())); GdkVisual* visual = gdk_screen_get_rgba_visual(gdk_screen_get_default()); if (!visual) visual = gdk_screen_get_system_visual(gdk_screen_get_default()); m_surface = adoptRef(cairo_xlib_surface_create(display, pixmap, GDK_VISUAL_XVISUAL(visual), size.width(), size.height())); cairoSurfaceSetDeviceScale(m_surface.get(), deviceScaleFactor, deviceScaleFactor); m_damage = XDamageCreate(display, pixmap, XDamageReportNonEmpty); XDamageNotifier::singleton().add(m_damage.get(), [this] { if (m_webPage.isViewVisible()) gtk_widget_queue_draw(m_webPage.viewWidget()); }); XSync(display, False); } bool AcceleratedBackingStoreX11::paint(cairo_t* cr, const IntRect& clipRect) { if (!m_surface) return false; cairo_save(cr); AcceleratedBackingStore::paint(cr, clipRect); // The surface can be modified by the web process at any time, so we mark it // as dirty to ensure we always render the updated contents as soon as possible. cairo_surface_mark_dirty(m_surface.get()); cairo_rectangle(cr, clipRect.x(), clipRect.y(), clipRect.width(), clipRect.height()); cairo_set_source_surface(cr, m_surface.get(), 0, 0); cairo_set_operator(cr, CAIRO_OPERATOR_OVER); cairo_fill(cr); cairo_restore(cr); return true; } } // namespace WebKit #endif // USE(REDIRECTED_XCOMPOSITE_WINDOW)