diff --git a/src/view-backend-private.cpp b/src/view-backend-private.cpp index 7e3a738..e20e208 100644 --- a/src/view-backend-private.cpp +++ b/src/view-backend-private.cpp @@ -28,6 +28,7 @@ #include #include #include +#include ViewBackend::ViewBackend(ClientBundle* clientBundle, struct wpe_view_backend* backend) : m_clientBundle(clientBundle) @@ -38,8 +39,9 @@ ViewBackend::ViewBackend(ClientBundle* clientBundle, struct wpe_view_backend* ba ViewBackend::~ViewBackend() { - unregisterSurface(m_bridgeId); - + m_backend = nullptr; + for (auto bridgeId : m_bridgeIds) + WS::Instance::singleton().unregisterViewBackend(bridgeId); if (m_clientFd != -1) close(m_clientFd); } @@ -90,14 +92,6 @@ void ViewBackend::exportEGLStreamProducer(struct wl_resource* bufferResource) m_clientBundle->exportEGLStreamProducer(bufferResource); } -void ViewBackend::dispatchFrameCallbacks() -{ - if (G_LIKELY(m_bridgeId)) - WS::Instance::singleton().dispatchFrameCallbacks(m_bridgeId); - - wpe_view_backend_dispatch_frame_displayed(m_backend); -} - void ViewBackend::releaseBuffer(struct wl_resource* buffer_resource) { wl_buffer_send_release(buffer_resource); @@ -106,17 +100,28 @@ void ViewBackend::releaseBuffer(struct wl_resource* buffer_resource) void ViewBackend::registerSurface(uint32_t bridgeId) { - m_bridgeId = bridgeId; - WS::Instance::singleton().registerViewBackend(m_bridgeId, *this); + // The current surface will be made inactive (if any); make sure that + // surfaces other than the new active one do not have callbacks pending + // to be dispatched. + dispatchFrameCallbacks(); + + m_bridgeIds.push_back(bridgeId); + WS::Instance::singleton().registerViewBackend(m_bridgeIds.back(), *this); } void ViewBackend::unregisterSurface(uint32_t bridgeId) { - if (!bridgeId || m_bridgeId != bridgeId) + auto it = std::find(m_bridgeIds.begin(), m_bridgeIds.end(), bridgeId); + if (it == m_bridgeIds.end()) return; - WS::Instance::singleton().unregisterViewBackend(m_bridgeId); - m_bridgeId = 0; + // If the item being removed corresponds to the active surface, make + // sure to avoid having frame callbacks pending dispatch before removing. + if (bridgeId == m_bridgeIds.back()) + dispatchFrameCallbacks(bridgeId); + + m_bridgeIds.erase(it); + WS::Instance::singleton().unregisterViewBackend(bridgeId); } void ViewBackend::didReceiveMessage(uint32_t messageId, uint32_t messageBody) diff --git a/src/view-backend-private.h b/src/view-backend-private.h index 363b787..ae90822 100644 --- a/src/view-backend-private.h +++ b/src/view-backend-private.h @@ -30,6 +30,7 @@ #include #include +#include class ViewBackend; @@ -67,10 +68,31 @@ class ViewBackend final : public WS::APIClient, public FdoIPC::MessageReceiver { void exportLinuxDmabuf(const struct linux_dmabuf_buffer *dmabuf_buffer) override; void exportShmBuffer(struct wl_resource* bufferResource, struct wl_shm_buffer* shmBuffer) override; void exportEGLStreamProducer(struct wl_resource*) override; - void dispatchFrameCallbacks(); + + void bridgeConnectionLost(uint32_t id) override + { + unregisterSurface(id); + dispatchFrameCallbacks(); + } + + inline void dispatchFrameCallbacks() + { + if (G_LIKELY(!m_bridgeIds.empty())) + dispatchFrameCallbacks(m_bridgeIds.back()); + } + void releaseBuffer(struct wl_resource* buffer_resource); private: + inline void dispatchFrameCallbacks(uint32_t bridgeId) + { + WS::Instance::singleton().dispatchFrameCallbacks(bridgeId); + if (G_LIKELY(m_backend)) + wpe_view_backend_dispatch_frame_displayed(m_backend); + else + g_warning("Ignored dispatch frame displayed for bridgeId %" PRIu32 ": WPE view backend is gone", bridgeId); + } + void didReceiveMessage(uint32_t messageId, uint32_t messageBody) override; void registerSurface(uint32_t); @@ -78,7 +100,7 @@ class ViewBackend final : public WS::APIClient, public FdoIPC::MessageReceiver { static gboolean s_socketCallback(GSocket*, GIOCondition, gpointer); - uint32_t m_bridgeId { 0 }; + std::vector m_bridgeIds; ClientBundle* m_clientBundle; struct wpe_view_backend* m_backend; diff --git a/src/ws.cpp b/src/ws.cpp index 379a97b..5c5731b 100644 --- a/src/ws.cpp +++ b/src/ws.cpp @@ -545,8 +545,11 @@ void Instance::unregisterSurface(Surface* surface) [surface](const std::pair& value) -> bool { return value.second == surface; }); - if (it != m_viewBackendMap.end()) + if (it != m_viewBackendMap.end()) { m_viewBackendMap.erase(it); + if (surface->apiClient) + surface->apiClient->bridgeConnectionLost(it->first); + } } void Instance::dispatchFrameCallbacks(uint32_t bridgeId) diff --git a/src/ws.h b/src/ws.h index fbc8250..4371c0e 100644 --- a/src/ws.h +++ b/src/ws.h @@ -45,6 +45,12 @@ struct APIClient { virtual void exportLinuxDmabuf(const struct linux_dmabuf_buffer *dmabuf_buffer) = 0; virtual void exportShmBuffer(struct wl_resource*, struct wl_shm_buffer*) = 0; virtual void exportEGLStreamProducer(struct wl_resource*) = 0; + + // Invoked when the association with the surface associated with a given + // wpe_bridge identifier is no longer valid, typically due to the nested + // compositor client being disconnected before having the chance to read + // and process a FdoIPC::UnregisterSurface message. + virtual void bridgeConnectionLost(uint32_t bridgeId) = 0; }; struct Surface {