/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- * * This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ #include "WindowSurfaceProvider.h" #include "gfxPlatformGtk.h" #include "GtkCompositorWidget.h" #include "mozilla/gfx/Logging.h" #include "mozilla/layers/LayersTypes.h" #include "nsWindow.h" #include "mozilla/ScopeExit.h" #ifdef MOZ_WAYLAND # include "mozilla/StaticPrefs_widget.h" # include "WindowSurfaceWaylandMultiBuffer.h" #endif #ifdef MOZ_X11 # include "mozilla/X11Util.h" # include "WindowSurfaceX11Image.h" # include "WindowSurfaceX11SHM.h" #endif #undef LOG #ifdef MOZ_LOGGING # include "mozilla/Logging.h" # include "nsTArray.h" # include "Units.h" extern mozilla::LazyLogModule gWidgetLog; # define LOG(args) MOZ_LOG(gWidgetLog, mozilla::LogLevel::Debug, args) #else # define LOG(args) #endif /* MOZ_LOGGING */ namespace mozilla { namespace widget { using namespace mozilla::layers; WindowSurfaceProvider::WindowSurfaceProvider() : mWindowSurface(nullptr), mMutex("WindowSurfaceProvider"), mWindowSurfaceValid(false) #ifdef MOZ_X11 , mIsShaped(false), mXDepth(0), mXWindow(0), mXVisual(nullptr) #endif { } WindowSurfaceProvider::~WindowSurfaceProvider() { #ifdef MOZ_WAYLAND MOZ_DIAGNOSTIC_ASSERT(!mWidget, "nsWindow reference is still live, we're leaking it!"); #endif #ifdef MOZ_X11 MOZ_DIAGNOSTIC_ASSERT(!mXWindow, "mXWindow should be released on quit!"); #endif } #ifdef MOZ_WAYLAND bool WindowSurfaceProvider::Initialize(RefPtr aWidget) { mWindowSurfaceValid = false; mWidget = std::move(aWidget); return true; } bool WindowSurfaceProvider::Initialize(GtkCompositorWidget* aCompositorWidget) { mWindowSurfaceValid = false; mCompositorWidget = aCompositorWidget; mWidget = static_cast(aCompositorWidget->RealWidget()); return true; } #endif #ifdef MOZ_X11 bool WindowSurfaceProvider::Initialize(Window aWindow, bool aIsShaped) { mWindowSurfaceValid = false; // Grab the window's visual and depth XWindowAttributes windowAttrs; if (!XGetWindowAttributes(DefaultXDisplay(), aWindow, &windowAttrs)) { NS_WARNING("GtkCompositorWidget(): XGetWindowAttributes() failed!"); return false; } mXWindow = aWindow; mXVisual = windowAttrs.visual; mXDepth = windowAttrs.depth; mIsShaped = aIsShaped; return true; } #endif void WindowSurfaceProvider::CleanupResources() { MutexAutoLock lock(mMutex); mWindowSurfaceValid = false; #ifdef MOZ_WAYLAND mWidget = nullptr; #endif #ifdef MOZ_X11 mXWindow = 0; mXVisual = 0; mXDepth = 0; mIsShaped = false; #endif } RefPtr WindowSurfaceProvider::CreateWindowSurface() { #ifdef MOZ_WAYLAND if (GdkIsWaylandDisplay()) { // We're called too early or we're unmapped. if (!mWidget) { return nullptr; } return MakeRefPtr(mWidget, mCompositorWidget); } #endif #ifdef MOZ_X11 if (GdkIsX11Display()) { // We're called too early or we're unmapped. if (!mXWindow) { return nullptr; } // Blit to the window with the following priority: // 1. MIT-SHM // 2. XPutImage # ifdef MOZ_HAVE_SHMIMAGE if (!mIsShaped && nsShmImage::UseShm()) { LOG(("Drawing to Window 0x%lx will use MIT-SHM\n", (Window)mXWindow)); return MakeRefPtr(DefaultXDisplay(), mXWindow, mXVisual, mXDepth); } # endif // MOZ_HAVE_SHMIMAGE LOG(("Drawing to Window 0x%lx will use XPutImage\n", (Window)mXWindow)); return MakeRefPtr(DefaultXDisplay(), mXWindow, mXVisual, mXDepth, mIsShaped); } #endif MOZ_RELEASE_ASSERT(false); } // We need to ignore thread safety checks here. We need to hold mMutex // between StartRemoteDrawingInRegion()/EndRemoteDrawingInRegion() calls // which confuses it. MOZ_PUSH_IGNORE_THREAD_SAFETY already_AddRefed WindowSurfaceProvider::StartRemoteDrawingInRegion( const LayoutDeviceIntRegion& aInvalidRegion, layers::BufferMode* aBufferMode) { if (aInvalidRegion.IsEmpty()) { return nullptr; } // We return a reference to mWindowSurface inside draw target so we need to // hold the mutex untill EndRemoteDrawingInRegion() call where draw target // is returned. // If we return null dt, EndRemoteDrawingInRegion() won't be called to // release mutex. mMutex.Lock(); auto unlockMutex = MakeScopeExit([&] { mMutex.Unlock(); }); if (!mWindowSurfaceValid) { mWindowSurface = nullptr; mWindowSurfaceValid = true; } if (!mWindowSurface) { mWindowSurface = CreateWindowSurface(); if (!mWindowSurface) { return nullptr; } } *aBufferMode = BufferMode::BUFFER_NONE; RefPtr dt = mWindowSurface->Lock(aInvalidRegion); #ifdef MOZ_X11 if (!dt && GdkIsX11Display() && !mWindowSurface->IsFallback()) { // We can't use WindowSurfaceX11Image fallback on Wayland but // Lock() call on WindowSurfaceWayland should never fail. gfxWarningOnce() << "Failed to lock WindowSurface, falling back to XPutImage backend."; mWindowSurface = MakeRefPtr( DefaultXDisplay(), mXWindow, mXVisual, mXDepth, mIsShaped); dt = mWindowSurface->Lock(aInvalidRegion); } #endif if (dt) { // We have valid dt, mutex will be released in EndRemoteDrawingInRegion(). unlockMutex.release(); } return dt.forget(); } void WindowSurfaceProvider::EndRemoteDrawingInRegion( gfx::DrawTarget* aDrawTarget, const LayoutDeviceIntRegion& aInvalidRegion) { // Unlock mutex from StartRemoteDrawingInRegion(). mMutex.AssertCurrentThreadOwns(); auto unlockMutex = MakeScopeExit([&] { mMutex.Unlock(); }); // Commit to mWindowSurface only if we have a valid one. if (!mWindowSurface || !mWindowSurfaceValid) { return; } #if defined(MOZ_WAYLAND) if (GdkIsWaylandDisplay()) { // We're called too early or we're unmapped. // Don't draw anything. if (!mWidget || !mWidget->IsMapped()) { return; } if (moz_container_wayland_is_commiting_to_parent( mWidget->GetMozContainer())) { // If we're drawing directly to wl_surface owned by Gtk we need to use it // in main thread to sync with Gtk access to it. NS_DispatchToMainThread(NS_NewRunnableFunction( "WindowSurfaceProvider::EndRemoteDrawingInRegion", [widget = RefPtr{mWidget}, this, aInvalidRegion]() { if (!widget->IsMapped()) { return; } MutexAutoLock lock(mMutex); // Commit to mWindowSurface only when we have a valid one. if (mWindowSurface && mWindowSurfaceValid) { mWindowSurface->Commit(aInvalidRegion); } })); return; } } #endif mWindowSurface->Commit(aInvalidRegion); } MOZ_POP_THREAD_SAFETY } // namespace widget } // namespace mozilla