/* 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/. */

#ifndef MOZ_WAYLAND_SURFACE_H_
#define MOZ_WAYLAND_SURFACE_H_

#include "nsWaylandDisplay.h"
#include "mozilla/Mutex.h"
#include "mozilla/Atomics.h"
#include "WaylandSurfaceLock.h"
#include "mozilla/GRefPtr.h"

/* Workaround for bug at wayland-util.h,
 * present in wayland-devel < 1.12
 */
struct wl_surface;
struct wl_subsurface;
struct wl_egl_window;

class MessageLoop;

namespace mozilla::widget {

class WaylandBuffer;
class BufferTransaction;

// WaylandSurface is a wrapper for Wayland rendering target
// which is wl_surface / wl_subsurface.
class WaylandSurface final {
  friend WaylandSurfaceLock;

  NS_INLINE_DECL_THREADSAFE_REFCOUNTING(WaylandSurface);

  WaylandSurface();

  // aRootLayer is a WaylandSurface root which is used by layered (HDR)
  // rendering.
  void Init(RefPtr<WaylandSurface> aRootLayer = nullptr);

#ifdef MOZ_LOGGING
  nsAutoCString GetDebugTag() const;
  void* GetLoggingWidget() const { return mLoggingWidget; };
  void SetLoggingWidget(void* aWidget) { mLoggingWidget = aWidget; }
#endif

  // Fire VSync handler registered to this surface.
  void VSyncCallbackHandler(struct wl_callback* aCallback, uint32_t aTime,
                            bool aEmulated, bool aRoutedFromChildSurface);

  // Set VSync handler which is fired when it's good time for painting
  // and WalandSurface is visible and VSync is enabled.
  //
  // If aEmulateVSyncCallback is set to true, we file VSync handler even
  // if WaylandSurface is not visible. It's used for painting to hidden
  // surface.
  //
  // Once set, the aVSyncCallbackHandler is preserved between unmap/map.
  //
  // It's VSync source responsibility to disable emulated VSync events
  // by SetVSyncCallbackStateLocked().
  void SetVSyncCallbackHandlerLocked(
      const WaylandSurfaceLock& aProofOfLock,
      const std::function<void(wl_callback*, uint32_t, bool)>&
          aVSyncCallbackHandler,
      bool aEmulateVSyncCallback = false);

  // Clears VSync callback handler. It's used if frame callback handler
  // contains strong reference to WaylandSurface class owner
  // which we want to clear.
  void ClearVSyncCallbackHandlerLocked(const WaylandSurfaceLock& aProofOfLock);

  // Enable/Disable any frame callback emission (includes emulated ones).
  void SetVSyncCallbackStateLocked(const WaylandSurfaceLock& aProofOfLock,
                                   bool aEnabled);
  // Register handler which is called on VSync state change set by
  // SetVSyncCallbackStateLocked().
  void SetVSyncCallbackStateHandlerLocked(
      const WaylandSurfaceLock& aProofOfLock,
      const std::function<void(bool)>& aVSyncCallbackStateHandler);

  // Set a routine which returns whether we should run emulated callback
  // or not. Don't overwrite existing one unless aForce is set.
  void SetVSyncEmulateCheckLocked(
      const WaylandSurfaceLock& aProofOfLock,
      const std::function<bool(void)>& aVSyncEmulateCheck, bool aForce = false);

  wl_egl_window* GetEGLWindow(DesktopIntSize aSize);
  bool HasEGLWindow() const { return !!mEGLWindow; }

  // Set WaylandSurface target size (viewport & ELG surface if it's present).
  void SetSize(DesktopIntSize aSize);

  // Apply changes to EGLWindow size set by SetSize().
  // ApplyEGLWindowSize() is called from compostor thread
  // right before GL rendering to set EGLWindow size / viewport size
  // for actual back buffer.
  //
  // aEGLWindowSize is scaled backbuffer size and it's used similary
  // as WaylandBuffer size at Attach().
  void ApplyEGLWindowSize(LayoutDeviceIntSize aEGLWindowSize);

  // Mapped means we have all internals created.
  bool IsMapped() const { return mIsMapped; }

  // We've got first frame callback so we're really visible now.
  bool IsVisible() const { return mIsVisible; }

  bool IsToplevelSurface() const { return !mParent; }

  // Called from frame callback and sets the visible flag
  void VisibleCallbackHandler();

  // Indicate that Wayland surface uses Gdk resources which
  // need to be released on main thread by GdkCleanUpLocked().
  // It may be called after Unmap() to make sure
  // Gtk resources are not allocated again.
  bool IsPendingGdkCleanup() const { return mIsPendingGdkCleanup; }

  bool IsOpaqueSurfaceHandlerSet() const { return mIsOpaqueSurfaceHandlerSet; }

  bool HasBufferAttached() const { return mBufferAttached; }

  // Mapped as direct surface of MozContainer
  bool MapLocked(const WaylandSurfaceLock& aProofOfLock,
                 wl_surface* aParentWLSurface,
                 DesktopIntPoint aSubsurfacePosition);
  // Mapped as child of WaylandSurface (used by layers)
  bool MapLocked(const WaylandSurfaceLock& aProofOfLock,
                 WaylandSurfaceLock* aParentWaylandSurfaceLock,
                 DesktopIntPoint aSubsurfacePosition);
  // Unmap surface which hides it
  void UnmapLocked(WaylandSurfaceLock& aSurfaceLock);

  // Clean up Gdk resources, on main thread only
  void GdkCleanUpLocked(const WaylandSurfaceLock& aProofOfLock);

  // Allow to register and run a callback when associated widget (nsWindow)
  // is mapped.
  //
  // Map callback is called *after* WaylandSurface::MapLocked() call
  // by widget code on main thread.
  void SetMapCallbackLocked(
      const WaylandSurfaceLock& aProofOfLock,
      const std::function<void(WaylandSurfaceLock& aProofOfLock)>& aMapCB);
  void ClearMapCallbackLocked(const WaylandSurfaceLock& aProofOfLock);
  void RunMapCallbackLocked(WaylandSurfaceLock& aProofOfLock);

  // Allow to register and run a callback when associated widget (nsWindow)
  // is unmapped.
  //
  // Unmap callback is called *before* WaylandSurface::UnmapLocked() call
  // by widget code on main thread.
  void SetUnmapCallbackLocked(const WaylandSurfaceLock& aProofOfLock,
                              const std::function<void(void)>& aUnmapCB);
  void ClearUnmapCallbackLocked(const WaylandSurfaceLock& aProofOfLock);
  void RunUnmapCallback();

  // Attach WaylandBuffer which shows WaylandBuffer content
  // on screen.
  bool AttachLocked(const WaylandSurfaceLock& aSurfaceLock,
                    RefPtr<WaylandBuffer> aBuffer);
  bool IsBufferAttached(WaylandBuffer* aBuffer);

  // If there's any WaylandBuffer recently attached, detach it.
  // It makes the WaylandSurface invisible and it doesn't have any
  // content.
  void RemoveAttachedBufferLocked(const WaylandSurfaceLock& aProofOfLock);

  // Remove deleted transaction from WaylandSurface, it may release
  // referenced WaylandBuffer.
  void RemoveTransactionLocked(const WaylandSurfaceLock& aSurfaceLock,
                               RefPtr<BufferTransaction> aTransaction);

  // CommitLocked() is needed to call after some of *Locked() method
  // to submit the action to Wayland compositor by wl_surface_commit().

  // It's possible to stack more *Locked() methods
  // together and do commit after the last one to do the changes in atomic way.

  // Need of commit is tracked by mSurfaceNeedsCommit flag and
  // if it's set, CommitLocked() is called when WaylandSurfaceLock is destroyed
  // and WaylandSurface is unlocked.
  void CommitLocked(const WaylandSurfaceLock& aProofOfLock,
                    bool aForceCommit = false, bool aForceDisplayFlush = false);

  void EnableDMABufFormatsLocked(
      const WaylandSurfaceLock& aProofOfLock,
      const std::function<void(DMABufFormats*)>& aFormatRefreshCB);
  void DisableDMABufFormatsLocked(const WaylandSurfaceLock& aProofOfLock);

  // Place this WaylandSurface above aLowerSurface
  void PlaceAboveLocked(const WaylandSurfaceLock& aProofOfLock,
                        WaylandSurfaceLock& aLowerSurfaceLock);
  void MoveLocked(const WaylandSurfaceLock& aProofOfLock,
                  DesktopIntPoint aPosition);
  void SetViewportFollowsSizeChangesLocked(
      const WaylandSurfaceLock& aProofOfLock);
  void SetViewPortSourceRectLocked(const WaylandSurfaceLock& aProofOfLock,
                                   const DesktopRect& aRect);
  void SetViewPortDestLocked(const WaylandSurfaceLock& aProofOfLock,
                             const DesktopIntSize& aDestSize);
  void SetTransformFlippedLocked(const WaylandSurfaceLock& aProofOfLock,
                                 bool aFlippedX, bool aFlippedY);

  void SetOpaqueRegion(const gfx::IntRegion& aRegion);
  void SetOpaqueRegionLocked(const WaylandSurfaceLock& aProofOfLock,
                             const gfx::IntRegion& aRegion);
  void SetOpaqueLocked(const WaylandSurfaceLock& aProofOfLock);
  void ClearOpaqueRegionLocked(const WaylandSurfaceLock& aProofOfLock);
  void OpaqueCallbackHandler();

  void ClearOpaqueCallbackLocked(const WaylandSurfaceLock& aProofOfLock);
  void SetOpaqueCallbackLocked(const WaylandSurfaceLock& aProofOfLock);

  bool DisableUserInputLocked(const WaylandSurfaceLock& aProofOfLock);
  void InvalidateRegionLocked(const WaylandSurfaceLock& aProofOfLock,
                              const gfx::IntRegion& aInvalidRegion);
  void InvalidateLocked(const WaylandSurfaceLock& aProofOfLock);

  // We use two scale systems in Firefox/Wayland. Ceiled (integer) scale and
  // fractional scale. Ceiled scale is easy to implement but comes with
  // rendering overhead while fractional rendering paints buffers with exact
  // scale.
  //
  // Fractional scale is used as rendering optimization.
  // For instance if 225% scale is used, ceiled scale is 3
  // and fractional 2.20.
  //
  // If we paint content with ceiled scale 3 and desktop uses scale 225%,
  // Wayland compositor downscales buffer to 2.20 on rendering
  // but we paint more pixels than necessary (so we use name ceiled).
  //
  // Scale is used by wp_viewport. If a surface has a surface-local size
  // of 100 px by 50 px and wishes to submit buffers with a scale of 1.5,
  // then a buffer of 150px by 75 px should be used and the wp_viewport
  // destination rectangle should be 100 px by 50 px.
  // The wl_surface buffer scale should remain set to 1.
  //
  // For scale 2 (200%) we use surface size 200 x 100 px and set
  // viewport size to 100 x 50 px.
  //
  // We're getting fractional scale number with a small delay from
  // wp_fractional_scale_v1 after first commit to surface.
  // Meanwhile we can use ceiled scale number instead of fractional one or
  // get fractional scale from parent window (if there's any).
  //
  enum ScaleType {
    Disabled = 0,
    Ceiled = 1,
    Fractional = 2,
    Coordinates = 3,
  };

  void SetScaleTypeLocked(const WaylandSurfaceLock& aProofOfLock,
                          ScaleType aScaleType, bool aSetHandler);
  bool IsCoordinatesScaleLocked(const WaylandSurfaceLock& aProofOfLock) const {
    MOZ_DIAGNOSTIC_ASSERT(&aProofOfLock == mSurfaceLock);
    return mScaleType == ScaleType::Coordinates;
  }

  // Right now we support two scale change callbacks.
  // ScaleCallbackType::Widget is used by nsWindow & co to promote scale
  // changes to layout.
  // ScaleCallbackType::Layers is used by HDR compositor to propagate
  // changes to rendered layers/subsurfaces.
  //
  // At least one scale callbacks needs to be set before SetScaleTypeLocked()
  // to run the callback.
  enum ScaleCallbackType {
    Widget = 0,
    Layers = 1,
    CallbackNum = 2,
  };
  void SetScaleCallbackLocked(const WaylandSurfaceLock& aProofOfLock,
                              ScaleCallbackType aCallbackType,
                              std::function<void(void)> aScaleCallback);
  bool HasScaleCallbacksLocked(const WaylandSurfaceLock& aProofOfLock);
  void ClearScaleCallbacksLocked(const WaylandSurfaceLock& aProofOfLock);

  // Returns scale as float point number. If WaylandSurface is not mapped,
  // return fractional scale of parent surface or monitor.
  static constexpr const double sNoScale = -1;
  double GetScale() const;
  uint32_t GetCoordinatesScale() const { return mCoordinatesScale; }
  double GetCoordinatesScaleRounded() const {
    return ((double)mCoordinatesScale) / (1 << 24);
  }
  bool HasCoordinatesScaleLocked(const WaylandSurfaceLock& aProofOfLock) const {
    return !!mCoordinatesScaleManager;
  }

  // Called when screen ceiled scale changes or sets initial scale before we map
  // and paint the surface.
  void SetCeiledScaleLocked(const WaylandSurfaceLock& aProofOfLock,
                            int aScreenCeiledScale);
  // Sets coordinates scale explicitly to the surface,
  bool SetCoordinatesScaleLocked(const WaylandSurfaceLock& aProofOfLock,
                                 uint32_t scale_8_24);

  static void AfterPaintHandler(GdkFrameClock* aClock, void* aData);

  // See https://gitlab.gnome.org/GNOME/gtk/-/merge_requests/3111 why we use it.
  // If child surface covers whole area of parent surface and it's opaque,
  // parent surface will not get any events (frame callbacks) from compositor
  // as it's considered as invisible.
  //
  // Firefox uses the parent wl_surface (owned by GdkWindow) to get input
  // events. Without gdk_wayland_window_add_frame_callback_surface() call,
  // Gdk is not getting any events from compostor and we're frozen.
  //
  // So gdk_wayland_window_add_frame_callback_surface() registers wl_surface
  // owned by WaylandSurface to GtkWindow and requests frame callback
  // for it. Such frame callback is then routed to GdkWindow and it's used
  // to fire events like native GdkWindow ones.
  //
  // To make sure WaylandSurface's wl_surface frame callback is generated,
  // we need to commit the wl_surface regularly as Gdk registers frame callback
  // for it at on_frame_clock_after_paint() event of GdkWindow.
  bool AddOpaqueSurfaceHandlerLocked(const WaylandSurfaceLock& aProofOfLock,
                                     GdkWindow* aGdkWindow,
                                     bool aRegisterCommitHandler);
  bool RemoveOpaqueSurfaceHandlerLocked(const WaylandSurfaceLock& aProofOfLock);

  // Additional callback to call from on_frame_clock_after_paint()
  // and before this wl_surface is commited.
  // It can be used to update subsurfaces from main thread.
  void SetGdkCommitCallbackLocked(
      const WaylandSurfaceLock& aProofOfLock,
      const std::function<void(void)>& aGdkCommitCB);
  void ClearGdkCommitCallbackLocked(const WaylandSurfaceLock& aProofOfLock);

  RefPtr<DMABufFormats> GetDMABufFormats() const { return mFormats; }

  GdkWindow* GetGdkWindow() const;

  static bool IsOpaqueRegionEnabled();

  void SetParentLocked(const WaylandSurfaceLock& aProofOfLock,
                       RefPtr<WaylandSurface> aParent);

  bool EnableColorManagementLocked(const WaylandSurfaceLock& aProofOfLock,
                                   mozilla::gfx::YUVColorSpace aColorSpace,
                                   gfx::TransferFunction aTransferFunction);
  void SetColorRepresentationLocked(const WaylandSurfaceLock& aProofOfLock,
                                    mozilla::gfx::YUVColorSpace aColorSpace,
                                    bool aFullRange,
                                    uint32_t aWPChromaLocation);

  static void ImageDescriptionFailed(
      void* aData, struct wp_image_description_v1* aImageDescription,
      uint32_t aCause, const char* aMsg);
  static void ImageDescriptionReady(
      void* aData, struct wp_image_description_v1* aImageDescription,
      uint32_t aIdentity);

  void AssertCurrentThreadOwnsMutex();

  void ForceCommit() { mSurfaceNeedsCommit = true; }
  void SetCommitStateLocked(const WaylandSurfaceLock& aProofOfLock,
                            bool aCommitAllowed) {
    mCommitAllowed = aCommitAllowed;
  }

 private:
  ~WaylandSurface();

  bool MapLocked(const WaylandSurfaceLock& aProofOfLock,
                 wl_surface* aParentWLSurface,
                 WaylandSurfaceLock* aParentWaylandSurfaceLock,
                 DesktopIntPoint aSubsurfacePosition, bool aSubsurfaceDesync);

  wl_surface* Lock(WaylandSurfaceLock* aWaylandSurfaceLock);
  void Unlock(struct wl_surface** aSurface,
              WaylandSurfaceLock* aWaylandSurfaceLock);
  void Commit(WaylandSurfaceLock* aProofOfLock, bool aForceCommit,
              bool aForceDisplayFlush);

  // Get buffer transaction for WaylandBuffer, create new or recycle one.
  BufferTransaction* GetNextTransactionLocked(
      const WaylandSurfaceLock& aSurfaceLock, WaylandBuffer* aBuffer);
  // Force release/detele all transactions and wl_buffers attached to them.
  void ReleaseAllWaylandTransactionsLocked(WaylandSurfaceLock& aSurfaceLock);

  void SetVSyncCallbackLocked(const WaylandSurfaceLock& aProofOfLock);
  void ClearVSyncCallbackLocked(const WaylandSurfaceLock& aProofOfLock);
  bool HasEmulatedVSyncCallbackLocked(
      const WaylandSurfaceLock& aProofOfLock) const;
  bool IsEmulatedVSyncEnabledLocked(const WaylandSurfaceLock& aProofOfLock);
  void RequestEmulatedVSyncLocked(const WaylandSurfaceLock& aProofOfLock);

  // Configures requested scale type. If aSetHandler is set it also
  // install wayland-protocol handlers to call the scale change callbacks.
  //
  // We usually want to install handler to toplevel surfaces only
  // and propagate the scale change to child surfaces.
  bool ConfigureScaleLocked(const WaylandSurfaceLock& aProofOfLock,
                            ScaleType aScaleType, bool aSetProtocolHandler);
  bool ConfigureCoordinateScaleLocked(const WaylandSurfaceLock& aProofOfLock,
                                      bool aSetProtocolHandler);
  bool ConfigureFractionalScaleLocked(const WaylandSurfaceLock& aProofOfLock,
                                      bool aSetProtocolHandler);

  // Calculate 'stable' rounded size for subsurface based
  // on its size and position.
  LayoutDeviceIntSize GetScaledSize(const DesktopIntSize& aSize) const;

  // Weak ref to owning widget (nsWindow or NativeLayerWayland),
  // used for diagnostics/logging only.
  void* mLoggingWidget = nullptr;

  // mIsMapped means we're supposed to be visible
  // (or not if Wayland compositor decides so).
  mozilla::Atomic<bool, mozilla::Relaxed> mIsMapped{false};

  // mIsVisible means we're really visible as we've got frame callback.
  mozilla::Atomic<bool, mozilla::Relaxed> mIsVisible{false};

  // We used Gdk functions which needs clean up in main thread.
  mozilla::Atomic<bool, mozilla::Relaxed> mIsPendingGdkCleanup{false};

  std::function<void(void)> mGdkCommitCallback;
  std::function<void(WaylandSurfaceLock& aProofOfLock)> mMapCallback;
  std::function<void(void)> mUnmapCallback;

  DesktopIntSize mSize;

  // Parent GdkWindow where we paint to, directly or via subsurface.
  RefPtr<GdkWindow> mGdkWindow;

  // Parent wl_surface owned by mGdkWindow. It's used when we're attached
  // directly to MozContainer.
  wl_surface* mParentSurface = nullptr;

  // Parent WaylandSurface.
  //
  // Layer rendering (compositor) uses mSurface directly attached to
  // wl_surface owned by mParent.
  //
  // For non-compositing rendering (old) mParent is WaylandSurface
  // owned by parent nsWindow.
  RefPtr<WaylandSurface> mParent;

  // wl_surface setup/states
  wl_surface* mSurface = nullptr;
  mozilla::Atomic<bool, mozilla::Relaxed> mSurfaceNeedsCommit{false};
  bool mCommitAllowed = true;

  // When subsurface is desynced, we need to commit to parent surface
  // to see the change in subsurface (this one).
  // In such case we set mSurfaceNeedsCommit to parent for it.
  bool mSubsurfaceDesync = true;

  wl_subsurface* mSubsurface = nullptr;
  DesktopIntPoint mSubsurfacePosition;

  // Wayland buffers recently attached to this surface or held by
  // Wayland compositor.
  // There may be more than one buffer attached, for instance if
  // previous buffer is hold by compositor. We need to keep
  // there buffers live until compositor notify us that we
  // can release them.
  AutoTArray<RefPtr<BufferTransaction>, 3> mBufferTransactions;
  uintptr_t mLatestAttachedBuffer = 0;

  // Indicates mSurface has buffer attached so we can attach subsurface
  // to it and expect to get frame callbacks from Wayland compositor.
  // We set it at AttachLocked() or when we get first frame callback
  // (when EGL is used).
  mozilla::Atomic<bool, mozilla::Relaxed> mBufferAttached{false};

  mozilla::Atomic<wl_egl_window*, mozilla::Relaxed> mEGLWindow{nullptr};

  bool mViewportFollowsSizeChanges = false;
  wp_viewport* mViewport = nullptr;
  DesktopRect mViewportSourceRect{-1, -1, -1, -1};
  DesktopIntSize mViewportDestinationSize{-1, -1};

  // Surface flip state on X/Y asix
  bool mBufferTransformFlippedX = false;
  bool mBufferTransformFlippedY = false;

  // Frame callback for mIsVisible flag
  wl_callback* mVisibleFrameCallback = nullptr;

  // VSync callback handler called every frame or by time for emulated ones.
  struct VSyncCallback {
    std::function<void(wl_callback*, uint32_t, bool)> mCb = nullptr;
    bool mEmulated = false;
    bool IsSet() const { return !!mCb; }
  };
  VSyncCallback mVSyncCallbackHandler;

  wl_callback* mVSyncFrameCallback = nullptr;

  bool mVSyncCallbackEnabled = true;
  std::function<void(bool)> mVSyncCallbackStateHandler = nullptr;
  std::function<bool(void)> mVSyncEmulateCheck = nullptr;

  guint mEmulatedVSyncCallbackTimerID = 0;
  constexpr static int sEmulatedVSyncCallbackTimeoutMs = (int)(1000.0 / 60.0);

  // Frame callback used to set opaque region to wl_surface.
  wl_region* mPendingOpaqueRegion = nullptr;
  wl_callback* mOpaqueRegionFrameCallback = nullptr;

  // WaylandSurface is used from Compositor/Rendering/Main threads.
  mozilla::Mutex mMutex{"WaylandSurface"};
  WaylandSurfaceLock* mSurfaceLock = nullptr;

  // We may mark part of mSurface as opaque (non-transparent) if it's supported
  // by Gtk which allows compositor to skip painting of covered parts.
  mozilla::Atomic<bool, mozilla::Relaxed> mIsOpaqueSurfaceHandlerSet{false};
  gulong mGdkAfterPaintId = 0;
  static bool sIsOpaqueRegionEnabled;
  static void (*sGdkWaylandWindowAddCallbackSurface)(GdkWindow*,
                                                     struct wl_surface*);
  static void (*sGdkWaylandWindowRemoveCallbackSurface)(GdkWindow*,
                                                        struct wl_surface*);

  ScaleType mScaleType = ScaleType::Disabled;

  // mScreenScale is set from main thread only but read from
  // different threads.
  mozilla::Atomic<double, mozilla::Relaxed> mScreenScale{sNoScale};
  // Coordinates scale is in fixed-point 8.24 format.
  // Use GetCoordinatesScaleRounded() to convert it to float point.
  mozilla::Atomic<uint32_t, mozilla::Relaxed> mCoordinatesScale{1 << 24};

  // wp_fractional_scale_v1 / xx_fractional_scale_v2 works differently.
  //
  // wp_fractional_scale_v1 is needed for scale changes listener only
  // so it's optional and we don't need it for every surface.
  //
  // xx_fractional_scale_v2 is used to set coordinates scale to particular
  // surface so it must be present.
  wp_fractional_scale_v1* mFractionalScaleListener = nullptr;
  xx_fractional_scale_v2* mCoordinatesScaleManager = nullptr;

  // Callback issued when fractional / coordinates scale changes.
  // Ceiled (integer) scale changes is monitored by nsWindow as it's
  // tied to GtkWindow.
  std::function<void(void)> mScaleCallbacks[ScaleCallbackType::CallbackNum] = {
      nullptr, nullptr};

  bool mUseDMABufFormats = false;
  // Wayland display notifies us when available DRM formats are are changed.
  // For instance if wl_surface becomes fullscreen we may get DRM formats
  // for direct scanout.
  std::function<void(DMABufFormats*)> mDMABufFormatRefreshCallback;
  RefPtr<DMABufFormats> mFormats;

  // HDR support
  bool mHDRSet = false;
  wp_color_management_surface_v1* mColorSurface = nullptr;
  wp_color_representation_surface_v1* mColorRepresentationSurface = nullptr;
  wp_image_description_v1* mImageDescription = nullptr;
};

}  // namespace mozilla::widget

#endif /* MOZ_WAYLAND_SURFACE_H_ */
