RedirectedXCompositeWindow.cpp [plain text]
#include "config.h"
#include "RedirectedXCompositeWindow.h"
#if USE(REDIRECTED_XCOMPOSITE_WINDOW)
#include <WebCore/PlatformDisplayX11.h>
#include <X11/Xlib.h>
#include <X11/extensions/Xcomposite.h>
#include <X11/extensions/Xdamage.h>
#include <cairo-xlib.h>
#include <gdk/gdkx.h>
#include <glib.h>
#include <gtk/gtk.h>
#include <wtf/HashMap.h>
#include <wtf/NeverDestroyed.h>
using namespace WebCore;
namespace WebKit {
class XDamageNotifier {
WTF_MAKE_NONCOPYABLE(XDamageNotifier);
WTF_MAKE_FAST_ALLOCATED;
public:
static int s_damageEventBase;
XDamageNotifier()
{
}
void add(Window window, std::function<void()> notifyFunction)
{
if (m_notifyFunctions.isEmpty())
gdk_window_add_filter(nullptr, reinterpret_cast<GdkFilterFunc>(&filterXDamageEvent), this);
m_notifyFunctions.add(window, WTF::move(notifyFunction));
}
void remove(Window window)
{
m_notifyFunctions.remove(window);
if (m_notifyFunctions.isEmpty())
gdk_window_remove_filter(nullptr, reinterpret_cast<GdkFilterFunc>(&filterXDamageEvent), this);
}
private:
static GdkFilterReturn filterXDamageEvent(GdkXEvent* event, GdkEvent*, XDamageNotifier* notifier)
{
return notifier->filterXEvent(static_cast<XEvent*>(event));
}
GdkFilterReturn filterXEvent(XEvent* event) const
{
if (event->type != s_damageEventBase + XDamageNotify)
return GDK_FILTER_CONTINUE;
XDamageNotifyEvent* damageEvent = reinterpret_cast<XDamageNotifyEvent*>(event);
if (const auto& notifyFunction = m_notifyFunctions.get(damageEvent->drawable)) {
notifyFunction();
XDamageSubtract(event->xany.display, damageEvent->damage, None, None);
return GDK_FILTER_REMOVE;
}
return GDK_FILTER_CONTINUE;
}
HashMap<Window, std::function<void()>> m_notifyFunctions;
};
int XDamageNotifier::s_damageEventBase = 0;
static XDamageNotifier& xDamageNotifier()
{
static NeverDestroyed<XDamageNotifier> notifier;
return notifier;
}
static bool supportsXDamageAndXComposite(GdkWindow* window)
{
static bool initialized = false;
static bool hasExtensions = false;
if (initialized)
return hasExtensions;
initialized = true;
Display* display = GDK_DISPLAY_XDISPLAY(gdk_window_get_display(window));
int errorBase;
if (!XDamageQueryExtension(display, &XDamageNotifier::s_damageEventBase, &errorBase))
return false;
int eventBase;
if (!XCompositeQueryExtension(display, &eventBase, &errorBase))
return false;
int major, minor;
XCompositeQueryVersion(display, &major, &minor);
if (major < 0 || (!major && minor < 2))
return false;
hasExtensions = true;
return true;
}
std::unique_ptr<RedirectedXCompositeWindow> RedirectedXCompositeWindow::create(GdkWindow* parentWindow, std::function<void()> damageNotify)
{
ASSERT(GDK_IS_WINDOW(parentWindow));
return supportsXDamageAndXComposite(parentWindow) ? std::unique_ptr<RedirectedXCompositeWindow>(new RedirectedXCompositeWindow(parentWindow, damageNotify)) : nullptr;
}
RedirectedXCompositeWindow::RedirectedXCompositeWindow(GdkWindow* parentWindow, std::function<void()> damageNotify)
: m_display(GDK_DISPLAY_XDISPLAY(gdk_window_get_display(parentWindow)))
, m_needsNewPixmapAfterResize(false)
{
ASSERT(downcast<PlatformDisplayX11>(PlatformDisplay::sharedDisplay()).native() == m_display);
Screen* screen = DefaultScreenOfDisplay(m_display);
GdkVisual* visual = gdk_window_get_visual(parentWindow);
XUniqueColormap colormap(XCreateColormap(m_display, RootWindowOfScreen(screen), GDK_VISUAL_XVISUAL(visual), AllocNone));
XSetWindowAttributes windowAttributes;
windowAttributes.override_redirect = True;
windowAttributes.colormap = colormap.get();
windowAttributes.border_pixel = 0;
m_parentWindow = XCreateWindow(m_display,
RootWindowOfScreen(screen),
WidthOfScreen(screen) + 1, 0, 1, 1,
0,
gdk_visual_get_depth(visual),
InputOutput,
GDK_VISUAL_XVISUAL(visual),
CWOverrideRedirect | CWColormap | CWBorderPixel,
&windowAttributes);
XMapWindow(m_display, m_parentWindow.get());
windowAttributes.event_mask = StructureNotifyMask;
windowAttributes.override_redirect = False;
m_window = XCreateWindow(m_display,
m_parentWindow.get(),
0, 0,
std::max(1, m_size.width()),
std::max(1, m_size.height()),
0,
CopyFromParent,
InputOutput,
CopyFromParent,
CWEventMask,
&windowAttributes);
XMapWindow(m_display, m_window.get());
xDamageNotifier().add(m_window.get(), WTF::move(damageNotify));
while (1) {
XEvent event;
XWindowEvent(m_display, m_window.get(), StructureNotifyMask, &event);
if (event.type == MapNotify && event.xmap.window == m_window.get())
break;
}
XSelectInput(m_display, m_window.get(), NoEventMask);
XCompositeRedirectWindow(m_display, m_window.get(), CompositeRedirectManual);
m_damage = XDamageCreate(m_display, m_window.get(), XDamageReportNonEmpty);
}
RedirectedXCompositeWindow::~RedirectedXCompositeWindow()
{
ASSERT(m_display);
ASSERT(m_damage);
ASSERT(m_window);
ASSERT(m_parentWindow);
xDamageNotifier().remove(m_window.get());
m_damage.reset();
m_window.reset();
m_parentWindow.reset();
}
void RedirectedXCompositeWindow::resize(const IntSize& size)
{
if (size == m_size)
return;
XResizeWindow(m_display, m_window.get(), std::max(1, size.width()), std::max(1, size.height()));
XFlush(m_display);
m_size = size;
m_needsNewPixmapAfterResize = true;
if (m_size.isEmpty())
cleanupPixmapAndPixmapSurface();
}
void RedirectedXCompositeWindow::cleanupPixmapAndPixmapSurface()
{
if (!m_pixmap)
return;
m_surface = nullptr;
m_pixmap.reset();
}
cairo_surface_t* RedirectedXCompositeWindow::surface()
{
ASSERT(!m_size.isEmpty());
if (!m_needsNewPixmapAfterResize && m_surface)
return m_surface.get();
m_needsNewPixmapAfterResize = false;
XUniquePixmap newPixmap(XCompositeNameWindowPixmap(m_display, m_window.get()));
if (!newPixmap) {
cleanupPixmapAndPixmapSurface();
return nullptr;
}
XWindowAttributes windowAttributes;
if (!XGetWindowAttributes(m_display, m_window.get(), &windowAttributes)) {
cleanupPixmapAndPixmapSurface();
return nullptr;
}
RefPtr<cairo_surface_t> newSurface = adoptRef(cairo_xlib_surface_create(m_display, newPixmap.get(), windowAttributes.visual, m_size.width(), m_size.height()));
if (m_surface) {
RefPtr<cairo_t> 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 = WTF::move(newPixmap);
m_surface = newSurface;
return m_surface.get();
}
}
#endif // USE(REDIRECTED_XCOMPOSITE_WINDOW)