summaryrefslogtreecommitdiff
path: root/mozilla-wayland-trunk.patch
diff options
context:
space:
mode:
Diffstat (limited to 'mozilla-wayland-trunk.patch')
-rw-r--r--mozilla-wayland-trunk.patch519
1 files changed, 519 insertions, 0 deletions
diff --git a/mozilla-wayland-trunk.patch b/mozilla-wayland-trunk.patch
new file mode 100644
index 0000000..8c3746a
--- /dev/null
+++ b/mozilla-wayland-trunk.patch
@@ -0,0 +1,519 @@
+Wayland patches from Firefox 63.
+
+diff -up firefox-62.0.3/widget/gtk/mozcontainer.cpp.old firefox-62.0.3/widget/gtk/mozcontainer.cpp
+--- firefox-62.0.3/widget/gtk/mozcontainer.cpp.old 2018-10-08 11:52:33.442449788 +0200
++++ firefox-62.0.3/widget/gtk/mozcontainer.cpp 2018-08-21 14:22:12.668885853 +0200
+@@ -605,6 +605,15 @@ moz_container_get_wl_surface(MozContaine
+ return nullptr;
+
+ moz_container_map_surface(container);
++ // Set the scale factor for the buffer right after we create it.
++ if (container->surface) {
++ static auto sGdkWindowGetScaleFactorPtr = (gint (*)(GdkWindow*))
++ dlsym(RTLD_DEFAULT, "gdk_window_get_scale_factor");
++ if (sGdkWindowGetScaleFactorPtr && window) {
++ gint scaleFactor = (*sGdkWindowGetScaleFactorPtr)(window);
++ wl_surface_set_buffer_scale(container->surface, scaleFactor);
++ }
++ }
+ }
+
+ return container->surface;
+diff -up firefox-62.0.3/widget/gtk/WindowSurfaceWayland.cpp.old firefox-62.0.3/widget/gtk/WindowSurfaceWayland.cpp
+--- firefox-62.0.3/widget/gtk/WindowSurfaceWayland.cpp.old 2018-10-08 11:52:47.907392869 +0200
++++ firefox-62.0.3/widget/gtk/WindowSurfaceWayland.cpp 2018-09-06 11:01:18.801964790 +0200
+@@ -151,8 +151,9 @@ static nsWaylandDisplay* WaylandDisplayG
+ static void WaylandDisplayRelease(wl_display *aDisplay);
+ static void WaylandDisplayLoop(wl_display *aDisplay);
+
+-// TODO: is the 60pfs loop correct?
+-#define EVENT_LOOP_DELAY (1000/60)
++// TODO: Bug 1467125 - We need to integrate wl_display_dispatch_queue_pending() with
++// compositor event loop.
++#define EVENT_LOOP_DELAY (1000/240)
+
+ // Get WaylandDisplay for given wl_display and actual calling thread.
+ static nsWaylandDisplay*
+@@ -523,7 +524,7 @@ WindowBackBuffer::Detach()
+ }
+
+ bool
+-WindowBackBuffer::SetImageDataFromBackBuffer(
++WindowBackBuffer::SetImageDataFromBuffer(
+ class WindowBackBuffer* aSourceBuffer)
+ {
+ if (!IsMatchingSize(aSourceBuffer)) {
+@@ -550,6 +551,8 @@ frame_callback_handler(void *data, struc
+ {
+ auto surface = reinterpret_cast<WindowSurfaceWayland*>(data);
+ surface->FrameCallbackHandler();
++
++ gfxPlatformGtk::GetPlatform()->SetWaylandLastVsync(time);
+ }
+
+ static const struct wl_callback_listener frame_listener = {
+@@ -559,27 +562,40 @@ static const struct wl_callback_listener
+ WindowSurfaceWayland::WindowSurfaceWayland(nsWindow *aWindow)
+ : mWindow(aWindow)
+ , mWaylandDisplay(WaylandDisplayGet(aWindow->GetWaylandDisplay()))
+- , mFrontBuffer(nullptr)
+- , mBackBuffer(nullptr)
++ , mWaylandBuffer(nullptr)
++ , mBackupBuffer(nullptr)
+ , mFrameCallback(nullptr)
+- , mFrameCallbackSurface(nullptr)
++ , mLastCommittedSurface(nullptr)
+ , mDisplayThreadMessageLoop(MessageLoop::current())
+- , mDirectWlBufferDraw(true)
+- , mDelayedCommit(false)
+- , mFullScreenDamage(false)
++ , mDelayedCommitHandle(nullptr)
++ , mDrawToWaylandBufferDirectly(true)
++ , mPendingCommit(false)
++ , mWaylandBufferFullScreenDamage(false)
+ , mIsMainThread(NS_IsMainThread())
++ , mNeedScaleFactorUpdate(true)
+ {
+ }
+
+ WindowSurfaceWayland::~WindowSurfaceWayland()
+ {
+- delete mFrontBuffer;
+- delete mBackBuffer;
++ if (mPendingCommit) {
++ NS_WARNING("Deleted WindowSurfaceWayland with a pending commit!");
++ }
++
++ if (mDelayedCommitHandle) {
++ // Delete reference to this to prevent WaylandBufferDelayCommitHandler()
++ // operate on released this. mDelayedCommitHandle itself will
++ // be released at WaylandBufferDelayCommitHandler().
++ *mDelayedCommitHandle = nullptr;
++ }
+
+ if (mFrameCallback) {
+ wl_callback_destroy(mFrameCallback);
+ }
+
++ delete mWaylandBuffer;
++ delete mBackupBuffer;
++
+ if (!mIsMainThread) {
+ // We can be destroyed from main thread even though we was created/used
+ // in compositor thread. We have to unref/delete WaylandDisplay in compositor
+@@ -593,73 +609,64 @@ WindowSurfaceWayland::~WindowSurfaceWayl
+ }
+ }
+
+-void
+-WindowSurfaceWayland::UpdateScaleFactor()
+-{
+- wl_surface* waylandSurface = mWindow->GetWaylandSurface();
+- if (waylandSurface) {
+- wl_surface_set_buffer_scale(waylandSurface, mWindow->GdkScaleFactor());
+- }
+-}
+-
+ WindowBackBuffer*
+-WindowSurfaceWayland::GetFrontBufferToDraw(int aWidth, int aHeight)
++WindowSurfaceWayland::GetWaylandBufferToDraw(int aWidth, int aHeight)
+ {
+- if (!mFrontBuffer) {
+- mFrontBuffer = new WindowBackBuffer(mWaylandDisplay, aWidth, aHeight);
+- mBackBuffer = new WindowBackBuffer(mWaylandDisplay, aWidth, aHeight);
+- return mFrontBuffer;
++ if (!mWaylandBuffer) {
++ mWaylandBuffer = new WindowBackBuffer(mWaylandDisplay, aWidth, aHeight);
++ mBackupBuffer = new WindowBackBuffer(mWaylandDisplay, aWidth, aHeight);
++ return mWaylandBuffer;
+ }
+
+- if (!mFrontBuffer->IsAttached()) {
+- if (!mFrontBuffer->IsMatchingSize(aWidth, aHeight)) {
+- mFrontBuffer->Resize(aWidth, aHeight);
++ if (!mWaylandBuffer->IsAttached()) {
++ if (!mWaylandBuffer->IsMatchingSize(aWidth, aHeight)) {
++ mWaylandBuffer->Resize(aWidth, aHeight);
+ // There's a chance that scale factor has been changed
+ // when buffer size changed
+- UpdateScaleFactor();
++ mNeedScaleFactorUpdate = true;
+ }
+- return mFrontBuffer;
++ return mWaylandBuffer;
+ }
+
+ // Front buffer is used by compositor, draw to back buffer
+- if (mBackBuffer->IsAttached()) {
++ if (mBackupBuffer->IsAttached()) {
+ NS_WARNING("No drawing buffer available");
+ return nullptr;
+ }
+
+- MOZ_ASSERT(!mDelayedCommit,
++ MOZ_ASSERT(!mPendingCommit,
+ "Uncommitted buffer switch, screen artifacts ahead.");
+
+- WindowBackBuffer *tmp = mFrontBuffer;
+- mFrontBuffer = mBackBuffer;
+- mBackBuffer = tmp;
++ WindowBackBuffer *tmp = mWaylandBuffer;
++ mWaylandBuffer = mBackupBuffer;
++ mBackupBuffer = tmp;
+
+- if (mBackBuffer->IsMatchingSize(aWidth, aHeight)) {
++ if (mBackupBuffer->IsMatchingSize(aWidth, aHeight)) {
+ // Former front buffer has the same size as a requested one.
+ // Gecko may expect a content already drawn on screen so copy
+ // existing data to the new buffer.
+- mFrontBuffer->SetImageDataFromBackBuffer(mBackBuffer);
++ mWaylandBuffer->SetImageDataFromBuffer(mBackupBuffer);
+ // When buffer switches we need to damage whole screen
+ // (https://bugzilla.redhat.com/show_bug.cgi?id=1418260)
+- mFullScreenDamage = true;
++ mWaylandBufferFullScreenDamage = true;
+ } else {
+ // Former buffer has different size from the new request. Only resize
+ // the new buffer and leave gecko to render new whole content.
+- mFrontBuffer->Resize(aWidth, aHeight);
++ mWaylandBuffer->Resize(aWidth, aHeight);
+ }
+
+- return mFrontBuffer;
++ return mWaylandBuffer;
+ }
+
+ already_AddRefed<gfx::DrawTarget>
+-WindowSurfaceWayland::LockFrontBuffer(int aWidth, int aHeight)
++WindowSurfaceWayland::LockWaylandBuffer(int aWidth, int aHeight)
+ {
+- WindowBackBuffer* buffer = GetFrontBufferToDraw(aWidth, aHeight);
++ WindowBackBuffer* buffer = GetWaylandBufferToDraw(aWidth, aHeight);
+ if (buffer) {
+ return buffer->Lock();
+ }
+
+- NS_WARNING("WindowSurfaceWayland::LockFrontBuffer(): No buffer available");
++ NS_WARNING("WindowSurfaceWayland::LockWaylandBuffer(): No buffer available");
+ return nullptr;
+ }
+
+@@ -687,8 +694,8 @@ WindowSurfaceWayland::LockImageSurface(c
+ A) Lock() is called to whole surface. In that case we don't need
+ to clip/buffer the drawing and we can return wl_buffer directly
+ for drawing.
+- - mFrontBuffer is available - that's an ideal situation.
+- - mFrontBuffer is locked by compositor - flip buffers and draw.
++ - mWaylandBuffer is available - that's an ideal situation.
++ - mWaylandBuffer is locked by compositor - flip buffers and draw.
+ - if we can't flip buffers - go B)
+
+ B) Lock() is requested for part(s) of screen. We need to provide temporary
+@@ -704,30 +711,30 @@ WindowSurfaceWayland::Lock(const LayoutD
+ gfx::IntSize lockSize(bounds.XMost(), bounds.YMost());
+
+ // Are we asked for entire nsWindow to draw?
+- mDirectWlBufferDraw = (aRegion.GetNumRects() == 1 &&
+- bounds.x == 0 && bounds.y == 0 &&
+- lockSize.width == screenRect.width &&
+- lockSize.height == screenRect.height);
+-
+- if (mDirectWlBufferDraw) {
+- RefPtr<gfx::DrawTarget> dt = LockFrontBuffer(screenRect.width,
+- screenRect.height);
++ mDrawToWaylandBufferDirectly = (aRegion.GetNumRects() == 1 &&
++ bounds.x == 0 && bounds.y == 0 &&
++ lockSize.width == screenRect.width &&
++ lockSize.height == screenRect.height);
++
++ if (mDrawToWaylandBufferDirectly) {
++ RefPtr<gfx::DrawTarget> dt = LockWaylandBuffer(screenRect.width,
++ screenRect.height);
+ if (dt) {
+ return dt.forget();
+ }
+
+ // We don't have any front buffer available. Try indirect drawing
+ // to mImageSurface which is mirrored to front buffer at commit.
+- mDirectWlBufferDraw = false;
++ mDrawToWaylandBufferDirectly = false;
+ }
+
+ return LockImageSurface(lockSize);
+ }
+
+ bool
+-WindowSurfaceWayland::CommitImageSurface(const LayoutDeviceIntRegion& aRegion)
++WindowSurfaceWayland::CommitImageSurfaceToWaylandBuffer(const LayoutDeviceIntRegion& aRegion)
+ {
+- MOZ_ASSERT(!mDirectWlBufferDraw);
++ MOZ_ASSERT(!mDrawToWaylandBufferDirectly);
+
+ LayoutDeviceIntRect screenRect = mWindow->GetBounds();
+ gfx::IntRect bounds = aRegion.GetBounds().ToUnknownRect();
+@@ -737,8 +744,8 @@ WindowSurfaceWayland::CommitImageSurface
+ return false;
+ }
+
+- RefPtr<gfx::DrawTarget> dt = LockFrontBuffer(screenRect.width,
+- screenRect.height);
++ RefPtr<gfx::DrawTarget> dt = LockWaylandBuffer(screenRect.width,
++ screenRect.height);
+ RefPtr<gfx::SourceSurface> surf =
+ gfx::Factory::CreateSourceSurfaceForCairoSurface(mImageSurface->CairoSurface(),
+ mImageSurface->GetSize(),
+@@ -766,92 +773,145 @@ WindowSurfaceWayland::CommitImageSurface
+ return true;
+ }
+
++static void
++WaylandBufferDelayCommitHandler(WindowSurfaceWayland **aSurface)
++{
++ if (*aSurface) {
++ (*aSurface)->DelayedCommitHandler();
++ } else {
++ // Referenced WindowSurfaceWayland is already deleted.
++ // Do nothing but just release the mDelayedCommitHandle allocated at
++ // WindowSurfaceWayland::CommitWaylandBuffer().
++ free(aSurface);
++ }
++}
++
+ void
+-WindowSurfaceWayland::Commit(const LayoutDeviceIntRegion& aInvalidRegion)
++WindowSurfaceWayland::CommitWaylandBuffer()
+ {
+- MOZ_ASSERT(mIsMainThread == NS_IsMainThread());
++ MOZ_ASSERT(mPendingCommit, "Committing empty surface!");
+
+ wl_surface* waylandSurface = mWindow->GetWaylandSurface();
+ if (!waylandSurface) {
+- // Target window is already destroyed - don't bother to render there.
+- NS_WARNING("WindowSurfaceWayland::Commit(): parent wl_surface is already hidden/deleted.");
++ // Target window is not created yet - delay the commit. This can happen only
++ // when the window is newly created and there's no active
++ // frame callback pending.
++ MOZ_ASSERT(!mFrameCallback || waylandSurface != mLastCommittedSurface,
++ "Missing wayland surface at frame callback!");
++
++ // Do nothing if there's already mDelayedCommitHandle pending.
++ if (!mDelayedCommitHandle) {
++ mDelayedCommitHandle = static_cast<WindowSurfaceWayland**>(
++ moz_xmalloc(sizeof(*mDelayedCommitHandle)));
++ *mDelayedCommitHandle = this;
++
++ MessageLoop::current()->PostDelayedTask(
++ NewRunnableFunction("WaylandBackBufferCommit",
++ &WaylandBufferDelayCommitHandler,
++ mDelayedCommitHandle),
++ EVENT_LOOP_DELAY);
++ }
+ return;
+ }
+ wl_proxy_set_queue((struct wl_proxy *)waylandSurface,
+ mWaylandDisplay->GetEventQueue());
+
+- if (!mDirectWlBufferDraw) {
+- // We have new content at mImageSurface - copy data to mFrontBuffer first.
+- CommitImageSurface(aInvalidRegion);
++ // We have an active frame callback request so handle it.
++ if (mFrameCallback) {
++ if (waylandSurface == mLastCommittedSurface) {
++ // We have an active frame callback pending from our recent surface.
++ // It means we should defer the commit to FrameCallbackHandler().
++ return;
++ }
++ // If our stored wl_surface does not match the actual one it means the frame
++ // callback is no longer active and we should release it.
++ wl_callback_destroy(mFrameCallback);
++ mFrameCallback = nullptr;
++ mLastCommittedSurface = nullptr;
+ }
+
+- if (mFullScreenDamage) {
++ if (mWaylandBufferFullScreenDamage) {
+ LayoutDeviceIntRect rect = mWindow->GetBounds();
+ wl_surface_damage(waylandSurface, 0, 0, rect.width, rect.height);
+- mFullScreenDamage = false;
++ mWaylandBufferFullScreenDamage = false;
+ } else {
+- for (auto iter = aInvalidRegion.RectIter(); !iter.Done(); iter.Next()) {
++ gint scaleFactor = mWindow->GdkScaleFactor();
++ for (auto iter = mWaylandBufferDamage.RectIter(); !iter.Done(); iter.Next()) {
+ const mozilla::LayoutDeviceIntRect &r = iter.Get();
+- wl_surface_damage(waylandSurface, r.x, r.y, r.width, r.height);
++ // We need to remove the scale factor because the wl_surface_damage
++ // also multiplies by current scale factor.
++ wl_surface_damage(waylandSurface, r.x/scaleFactor, r.y/scaleFactor,
++ r.width/scaleFactor, r.height/scaleFactor);
+ }
+ }
+
+- // Frame callback is always connected to actual wl_surface. When the surface
+- // is unmapped/deleted the frame callback is never called. Unfortunatelly
+- // we don't know if the frame callback is not going to be called.
+- // But our mozcontainer code deletes wl_surface when the GdkWindow is hidden
+- // creates a new one when is visible.
+- if (mFrameCallback && mFrameCallbackSurface == waylandSurface) {
+- // Do nothing here - we have a valid wl_surface and the buffer will be
+- // commited to compositor in next frame callback event.
+- mDelayedCommit = true;
+- return;
+- } else {
+- if (mFrameCallback) {
+- // Delete frame callback connected to obsoleted wl_surface.
+- wl_callback_destroy(mFrameCallback);
+- }
++ // Clear all back buffer damage as we're committing
++ // all requested regions.
++ mWaylandBufferDamage.SetEmpty();
++
++ mFrameCallback = wl_surface_frame(waylandSurface);
++ wl_callback_add_listener(mFrameCallback, &frame_listener, this);
+
+- mFrameCallback = wl_surface_frame(waylandSurface);
+- wl_callback_add_listener(mFrameCallback, &frame_listener, this);
+- mFrameCallbackSurface = waylandSurface;
+-
+- // There's no pending frame callback so we can draw immediately
+- // and create frame callback for possible subsequent drawing.
+- mFrontBuffer->Attach(waylandSurface);
+- mDelayedCommit = false;
++ if (mNeedScaleFactorUpdate || mLastCommittedSurface != waylandSurface) {
++ wl_surface_set_buffer_scale(waylandSurface, mWindow->GdkScaleFactor());
++ mNeedScaleFactorUpdate = false;
+ }
++
++ mWaylandBuffer->Attach(waylandSurface);
++ mLastCommittedSurface = waylandSurface;
++
++ // There's no pending commit, all changes are sent to compositor.
++ mPendingCommit = false;
++}
++
++void
++WindowSurfaceWayland::Commit(const LayoutDeviceIntRegion& aInvalidRegion)
++{
++ MOZ_ASSERT(mIsMainThread == NS_IsMainThread());
++
++ // We have new content at mImageSurface - copy data to mWaylandBuffer first.
++ if (!mDrawToWaylandBufferDirectly) {
++ CommitImageSurfaceToWaylandBuffer(aInvalidRegion);
++ }
++
++ // If we're not at fullscreen damage add drawing area from aInvalidRegion
++ if (!mWaylandBufferFullScreenDamage) {
++ mWaylandBufferDamage.OrWith(aInvalidRegion);
++ }
++
++ // We're ready to commit.
++ mPendingCommit = true;
++ CommitWaylandBuffer();
+ }
+
+ void
+ WindowSurfaceWayland::FrameCallbackHandler()
+ {
+ MOZ_ASSERT(mIsMainThread == NS_IsMainThread());
++ MOZ_ASSERT(mFrameCallback != nullptr,
++ "FrameCallbackHandler() called without valid frame callback!");
++ MOZ_ASSERT(mLastCommittedSurface != nullptr,
++ "FrameCallbackHandler() called without valid wl_surface!");
+
+- if (mFrameCallback) {
+- wl_callback_destroy(mFrameCallback);
+- mFrameCallback = nullptr;
+- mFrameCallbackSurface = nullptr;
++ wl_callback_destroy(mFrameCallback);
++ mFrameCallback = nullptr;
++
++ if (mPendingCommit) {
++ CommitWaylandBuffer();
+ }
++}
+
+- if (mDelayedCommit) {
+- wl_surface* waylandSurface = mWindow->GetWaylandSurface();
+- if (!waylandSurface) {
+- // Target window is already destroyed - don't bother to render there.
+- NS_WARNING("WindowSurfaceWayland::FrameCallbackHandler(): parent wl_surface is already hidden/deleted.");
+- return;
+- }
+- wl_proxy_set_queue((struct wl_proxy *)waylandSurface,
+- mWaylandDisplay->GetEventQueue());
++void
++WindowSurfaceWayland::DelayedCommitHandler()
++{
++ MOZ_ASSERT(mDelayedCommitHandle != nullptr, "Missing mDelayedCommitHandle!");
+
+- // Send pending surface to compositor and register frame callback
+- // for possible subsequent drawing.
+- mFrameCallback = wl_surface_frame(waylandSurface);
+- wl_callback_add_listener(mFrameCallback, &frame_listener, this);
+- mFrameCallbackSurface = waylandSurface;
++ *mDelayedCommitHandle = nullptr;
++ free(mDelayedCommitHandle);
++ mDelayedCommitHandle = nullptr;
+
+- mFrontBuffer->Attach(waylandSurface);
+- mDelayedCommit = false;
++ if (mPendingCommit) {
++ CommitWaylandBuffer();
+ }
+ }
+
+diff -up firefox-62.0.3/widget/gtk/WindowSurfaceWayland.h.old firefox-62.0.3/widget/gtk/WindowSurfaceWayland.h
+--- firefox-62.0.3/widget/gtk/WindowSurfaceWayland.h.old 2018-10-08 11:52:52.154376159 +0200
++++ firefox-62.0.3/widget/gtk/WindowSurfaceWayland.h 2018-09-06 11:01:18.802964787 +0200
+@@ -74,7 +74,7 @@ public:
+ bool IsAttached() { return mAttached; }
+
+ bool Resize(int aWidth, int aHeight);
+- bool SetImageDataFromBackBuffer(class WindowBackBuffer* aSourceBuffer);
++ bool SetImageDataFromBuffer(class WindowBackBuffer* aSourceBuffer);
+
+ bool IsMatchingSize(int aWidth, int aHeight)
+ {
+@@ -111,28 +111,32 @@ public:
+ already_AddRefed<gfx::DrawTarget> Lock(const LayoutDeviceIntRegion& aRegion) override;
+ void Commit(const LayoutDeviceIntRegion& aInvalidRegion) final;
+ void FrameCallbackHandler();
++ void DelayedCommitHandler();
+
+ private:
+- WindowBackBuffer* GetFrontBufferToDraw(int aWidth, int aHeight);
+- void UpdateScaleFactor();
++ WindowBackBuffer* GetWaylandBufferToDraw(int aWidth, int aHeight);
+
+- already_AddRefed<gfx::DrawTarget> LockFrontBuffer(int aWidth, int aHeight);
++ already_AddRefed<gfx::DrawTarget> LockWaylandBuffer(int aWidth, int aHeight);
+ already_AddRefed<gfx::DrawTarget> LockImageSurface(const gfx::IntSize& aLockSize);
+- bool CommitImageSurface(const LayoutDeviceIntRegion& aRegion);
++ bool CommitImageSurfaceToWaylandBuffer(const LayoutDeviceIntRegion& aRegion);
++ void CommitWaylandBuffer();
+
+ // TODO: Do we need to hold a reference to nsWindow object?
+ nsWindow* mWindow;
+ nsWaylandDisplay* mWaylandDisplay;
+- WindowBackBuffer* mFrontBuffer;
+- WindowBackBuffer* mBackBuffer;
++ WindowBackBuffer* mWaylandBuffer;
++ LayoutDeviceIntRegion mWaylandBufferDamage;
++ WindowBackBuffer* mBackupBuffer;
+ RefPtr<gfxImageSurface> mImageSurface;
+ wl_callback* mFrameCallback;
+- wl_surface* mFrameCallbackSurface;
++ wl_surface* mLastCommittedSurface;
+ MessageLoop* mDisplayThreadMessageLoop;
+- bool mDirectWlBufferDraw;
+- bool mDelayedCommit;
+- bool mFullScreenDamage;
++ WindowSurfaceWayland** mDelayedCommitHandle;
++ bool mDrawToWaylandBufferDirectly;
++ bool mPendingCommit;
++ bool mWaylandBufferFullScreenDamage;
+ bool mIsMainThread;
++ bool mNeedScaleFactorUpdate;
+ };
+
+ } // namespace widget
bgstack15