/* -*- Mode: C; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ /* vim:expandtab:shiftwidth=2:tabstop=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/. */
// We're supposed to run on main thread only.
AssertIsOnMainThread();
// mReadyToDrawFrameCallback/callback can be nullptr when redering directly // to GtkWidget and InitialFrameCallbackHandler is called by us from main // thread by WaylandSurface::Map().
MOZ_RELEASE_ASSERT(mReadyToDrawFrameCallback == callback);
std::vector<std::function<void(void)>> cbs;
{
WaylandSurfaceLock lock(this);
MozClearPointer(mReadyToDrawFrameCallback, wl_callback_destroy); // It's possible that we're already unmapped so quit in such case. if (!mSurface) {
LOGWAYLAND(" WaylandSurface is unmapped, quit."); if (!mReadToDrawCallbacks.empty()) {
NS_WARNING("Unmapping WaylandSurface with active draw callback!");
mReadToDrawCallbacks.clear();
} return;
} if (mIsReadyToDraw) { return;
}
mIsReadyToDraw = true;
cbs = std::move(mReadToDrawCallbacks);
}
// We can't call the callbacks under lock #ifdef MOZ_LOGGING int callbackNum = 0; #endif for (autoconst& cb : cbs) {
LOGWAYLAND(" initial callback fire [%d]", callbackNum++);
cb();
}
// If there's any frame callback waiting, register the handler now to fire // them if (!mPersistentFrameCallbackHandlers.empty() ||
!mOneTimeFrameCallbackHandlers.empty()) {
LOGWAYLAND(" initial callback: Register regular frame callback");
WaylandSurfaceLock lock(this);
RequestFrameCallbackLocked(lock,
IsEmulatedFrameCallbackPendingLocked(lock));
}
}
bool WaylandSurface::IsEmulatedFrameCallbackPendingLocked( const WaylandSurfaceLock& aProofOfLock) const { if (mBufferAttached) { returnfalse;
} for (autoconst& cb : mPersistentFrameCallbackHandlers) { if (cb.mEmulated) { returntrue;
}
} returnfalse;
}
void WaylandSurface::FrameCallbackHandler(struct wl_callback* aCallback,
uint32_t aTime, bool aRoutedFromChildSurface) { // We're supposed to run on main thread only.
AssertIsOnMainThread();
// It's possible to get regular frame callback right after unmap // if frame callbacks was already in event queue so ignore it. if (!emulatedCallback && !aRoutedFromChildSurface && !mFrameCallback) {
MOZ_DIAGNOSTIC_ASSERT(!mIsMapped); return;
}
if (aCallback) {
ClearFrameCallbackLocked(lock);
}
// We're getting regular frame callback from this surface so we must // have buffer attached. if (!emulatedCallback && !aRoutedFromChildSurface) {
mBufferAttached = true;
}
// We don't support emulated one-time callbacks if (!emulatedCallback) {
cbs = std::move(mOneTimeFrameCallbackHandlers);
}
std::copy(mPersistentFrameCallbackHandlers.begin(),
mPersistentFrameCallbackHandlers.end(), back_inserter(cbs));
}
// We can't run the callbacks under WaylandSurfaceLock #ifdef MOZ_LOGGING int callbackNum = 0; #endif for (autoconst& cb : cbs) {
LOGVERBOSE(" frame callback fire [%d]", callbackNum++); constauto& [callback, runEmulated] = cb; if (emulatedCallback && !runEmulated) { continue;
}
callback(aCallback, aTime);
}
// Fire frame callback again if there's any pending frame callback if (!mPersistentFrameCallbackHandlers.empty() ||
!mOneTimeFrameCallbackHandlers.empty()) {
WaylandSurfaceLock lock(this, mFrameCallbackForceCommit); bool enableCallbackEmulation = emulatedCallback || aRoutedFromChildSurface;
RequestFrameCallbackLocked(
lock,
enableCallbackEmulation && IsEmulatedFrameCallbackPendingLocked(lock));
} elseif (mFrameCallbackForceCommit) {
WaylandSurfaceLock lock(this, /* force commit */ true);
}
}
void WaylandSurface::RunUnmapCallback() {
AssertIsOnMainThread();
MOZ_DIAGNOSTIC_ASSERT(
mIsMapped, "RunUnmapCallback is supposed to run before surface unmap!"); if (mUnmapCallback) {
mUnmapCallback();
mUnmapCallback = nullptr;
}
}
// If mCommitToParentSurface is set, mSurface may be already deleted as // unamp/hide Gtk handler is called before us and we can't do anything // with it (at least I don't know how to override it). // So make it cleat and don't use it. // It doesn't matter much as we use direct rendering for D&D popups only. if (mCommitToParentSurface) {
mSurface = nullptr;
}
// We can't release mSurface if it's used by Gdk for frame callback routing. if (!mIsPendingGdkCleanup) {
MozClearPointer(mSurface, wl_surface_destroy);
}
// Remove references to WaylandBuffers attached to mSurface, // we don't want to get any buffer release callback when we're unmapped.
ReleaseAllWaylandBuffersLocked(aProofOfLock);
// Region should be in surface-logical coordinates, so we need to divide by // the buffer scale. We use round-in in order to be safe with subpixels.
UnknownScaleFactor scale(GetScaleSafe());
wl_region* region =
wl_compositor_create_region(WaylandDisplayGet()->GetCompositor()); for (auto iter = aRegion.RectIter(); !iter.Done(); iter.Next()) { constauto& rect = gfx::RoundedIn(iter.Get().ToUnknownRect() / scale);
wl_region_add(region, rect.x, rect.y, rect.Width(), rect.Height());
LOGVERBOSE(" region [%d, %d] -> [%d x %d]", rect.x, rect.y, rect.Width(),
rect.Height());
}
wl_surface_set_opaque_region(mSurface, region);
wl_region_destroy(region);
mSurfaceNeedsCommit = true;
}
MOZ_DIAGNOSTIC_ASSERT(!mFractionalScaleListener); auto* manager = WaylandDisplayGet()->GetFractionalScaleManager(); if (!manager) {
LOGWAYLAND( "WaylandSurface::SetupFractionalScale(): Failed to get " "FractionalScaleManager"); returnfalse;
}
mFractionalScaleListener =
wp_fractional_scale_manager_v1_get_fractional_scale(manager, mSurface);
wp_fractional_scale_v1_add_listener(mFractionalScaleListener,
&fractional_scale_listener, this);
// Create Viewport with aFollowsSizeChanges enabled, // regular rendering uses Viewport for fraction scale only. if (aManageViewport &&
!CreateViewportLocked(aProofOfLock, /* aFollowsSizeChanges */ true)) { returnfalse;
}
mFractionalScaleCallback = std::move(aFractionalScaleCallback);
// Init scale to default values and load ceiled screen scale from GdkWindow. // We use it as fallback before we get mScreenScale from system.
mScaleType = ScaleType::Fractional;
// Allow to set scale for unmapped surfaces unconditionally. // It sets initial scale factor so we have something to work with. if (!mIsMapped || IsCeiledScaleLocked(aProofOfLock)) { // mScreenScale = (double)aScreenCeiledScale;
mScreenScale = aScreenCeiledScale;
LOGWAYLAND("WaylandSurface::SetCeiledScaleLocked() scale %f",
(double)mScreenScale);
}
}
wl_surface* WaylandSurface::Lock(WaylandSurfaceLock* aWaylandSurfaceLock) // Disable thread safety analysis, it reports: // mutex 'mMutex' is still held at the end of function // which we want.
MOZ_NO_THREAD_SAFETY_ANALYSIS {
mMutex.Lock();
mSurfaceLock = aWaylandSurfaceLock; return mIsReadyToDraw ? mSurface : nullptr;
}
auto scale = GetScaleSafe(); // TODO: Correct size rounding
nsIntSize scaledSize((int)floor(aUnscaledSize.width * scale),
(int)floor(aUnscaledSize.height * scale)); if (!mEGLWindow) {
mEGLWindow =
wl_egl_window_create(mSurface, scaledSize.width, scaledSize.height);
LOGWAYLAND( "WaylandSurface::GetEGLWindow() created eglwindow [%p] size %d x %d",
(void*)mEGLWindow, scaledSize.width, scaledSize.height);
} else {
LOGWAYLAND("WaylandSurface::GetEGLWindow() resized to %d x %d",
scaledSize.width, scaledSize.height);
wl_egl_window_resize(mEGLWindow, scaledSize.width, scaledSize.height, 0, 0);
}
if (mEGLWindow) {
SetSizeLocked(lock, scaledSize, aUnscaledSize);
}
return mEGLWindow;
}
// Return false if scale factor doesn't match buffer size. // We need to skip painting in such case do avoid Wayland compositor freaking. bool WaylandSurface::SetEGLWindowSize(nsIntSize aScaledSize) {
WaylandSurfaceLock lock(this);
// We may be called after unmap so we're missing egl window completelly. // In such case don't return false which would block compositor. // We return true here and don't block flush WebRender queue. // We'll be repainted if our window become visible again anyway. if (!mEGLWindow) { returntrue;
}
auto scale = GetScaleSafe(); // TODO: Avoid precision lost here? Load coordinates from window?
nsIntSize unscaledSize((int)round(aScaledSize.width / scale),
(int)round(aScaledSize.height / scale));
LOGVERBOSE( "WaylandSurface::SetEGLWindowSize() scaled [%d x %d] unscaled [%d x %d] " "scale %f",
aScaledSize.width, aScaledSize.height, unscaledSize.width,
unscaledSize.height, scale);
if (mCommitToParentSurface) { // When committing to parent surface we must use wl_surface_damage(). // A parent surface is created as v.3 object which does not support // wl_surface_damage_buffer().
wl_surface_damage(mSurface, 0, 0, INT32_MAX, INT32_MAX);
} else { for (auto iter = aInvalidRegion.RectIter(); !iter.Done(); iter.Next()) {
gfx::IntRect r = iter.Get();
wl_surface_damage_buffer(mSurface, r.x, r.y, r.width, r.height);
}
}
mSurfaceNeedsCommit = true;
}
// Set our size according to attached buffer
SetSizeLocked(aProofOfLock, gfx::IntSize(0, 0), gfx::IntSize(0, 0));
wl_surface_attach(mSurface, nullptr, 0, 0);
mSurfaceNeedsCommit = true;
mBufferAttached = false;
}
void WaylandSurface::DetachedByWaylandCompositorLocked( const WaylandSurfaceLock& aProofOfLock,
RefPtr<WaylandBuffer> aWaylandBuffer) { // We should be called from Wayland compostor on Main thread only.
AssertIsOnMainThread();
// Remove WaylandBuffer from our internal list so it can be released. if (!UntrackWaylandBufferLocked(aProofOfLock, aWaylandBuffer, /* aRemove */ true)) {
MOZ_DIAGNOSTIC_CRASH("Wayland buffer is not attached?");
}
// We don't clear mBufferAttached here. WaylandBuffer may be still used by // Wayland compositor until wl_surface is deleted or a new wl_buffer is // attached or wl_buffer is eplicitly removed by RemoveAttachedBufferLocked().
}
// WaylandSurface is reffed by WaylandSurfaceLock
WaylandSurface* lowerSurface = aLowerSurfaceLock.GetWaylandSurface();
// lowerSurface has to be sibling or child of this
MOZ_DIAGNOSTIC_ASSERT(lowerSurface->mParent == mParent ||
lowerSurface->mParent == this);
// It's possible that lowerSurface becomed unmapped. In such rare case // just skip the operation, we may be deleted anyway. if (lowerSurface->mSurface) {
wl_subsurface_place_above(mSubsurface, lowerSurface->mSurface);
}
mSurfaceNeedsCommit = true;
}
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung ist noch experimentell.