@@ -741,8 +741,10 @@ namespace portal {
741741 }
742742
743743 dbus_.reset ();
744-
745744 valid_ = false ;
745+
746+ // Reset cached no target node indicator as the portal was reset
747+ no_target_node_ = false ;
746748 }
747749 } catch (const std::exception &e) {
748750 BOOST_LOG (error) << " [portalgrab] Exception during session invalidation: " sv << e.what ();
@@ -759,6 +761,14 @@ namespace portal {
759761 maxframerate_failed_ = true ;
760762 }
761763
764+ bool has_no_target_node () const {
765+ return no_target_node_;
766+ }
767+
768+ void set_no_target_node () {
769+ no_target_node_ = true ;
770+ }
771+
762772 private:
763773 session_cache_t () = default ;
764774
@@ -779,6 +789,7 @@ namespace portal {
779789 std::vector<pipewire_screenstream_t > pipewire_streams_;
780790 bool valid_ = false ;
781791 bool maxframerate_failed_ = false ;
792+ bool no_target_node_ = false ;
782793 };
783794
784795 session_cache_t &session_cache_t ::instance() {
@@ -1111,6 +1122,10 @@ namespace portal {
11111122 }
11121123 break ;
11131124 case PW_STREAM_STATE_ERROR:
1125+ if (std::string (err_msg).contains (" no target node available" ) && !session_cache_t::instance ().has_no_target_node ()) {
1126+ BOOST_LOG (warning) << " [portalgrab] Pipewire node unavailable" sv;
1127+ session_cache_t::instance ().set_no_target_node ();
1128+ }else
11141129 if (old != PW_STREAM_STATE_STREAMING && !session_cache_t::instance ().is_maxframerate_failed ()) {
11151130 BOOST_LOG (warning) << " [portalgrab] Negotiation failed, will retry without maxFramerate" sv;
11161131 session_cache_t::instance ().set_maxframerate_failed ();
@@ -1294,7 +1309,6 @@ namespace portal {
12941309 if (session_cache_t::instance ().get_or_create_session (pipewire_fd, pipewire_streams) < 0 ) {
12951310 return -1 ;
12961311 }
1297-
12981312 // Match display_name to a stream from the pipewire_streams vector
12991313 pipewire_screenstream_t stream = match_display_name_to_stream (pipewire_streams, display_name);
13001314 if (!display_name.empty () && (stream.width < 0 || stream.height < 0 )) {
@@ -1320,6 +1334,12 @@ namespace portal {
13201334
13211335 framerate = config.framerate ;
13221336
1337+ // Fail if the session cache indicates that there is no target node available (e.g. after user cancel)
1338+ if (session_cache_t::instance ().has_no_target_node ()) {
1339+ BOOST_LOG (warning) << " [portalgrab] Display init failed as PipeWire stream was stopped by user." sv;
1340+ return -1 ;
1341+ }
1342+
13231343 if (!shared_state) {
13241344 shared_state = std::make_shared<shared_state_t >();
13251345 } else {
@@ -1331,7 +1351,6 @@ namespace portal {
13311351
13321352 // Start PipeWire now so format negotiation can proceed before capture start
13331353 pipewire.ensure_stream (mem_type, width, height, framerate, dmabuf_infos.data (), n_dmabuf_infos, display_is_nvidia);
1334-
13351354 int timeout_ms = 1500 ;
13361355 int negotiated_w = 0 ;
13371356 int negotiated_h = 0 ;
@@ -1346,23 +1365,18 @@ namespace portal {
13461365 timeout_ms -= 10 ;
13471366 }
13481367
1349- // Check previous logical dimensions
1350- // FIXME: Handle switch_display_events that go to the same display without disconnecting the stream
1351- // if (previous_width.load() == width && previous_height.load() == height && previous_pos_x.load() == pos_x && previous_pos_y.load() == pos_y) {
1352- // if (capture_running.load()) {
1353- // {
1354- // std::scoped_lock lock(pipewire.frame_mutex());
1355- // stream_stopped.store(true);
1356- // }
1357- // pipewire.frame_cv().notify_all();
1358- // }
1359- // } else {
1360- previous_width.store (width);
1361- previous_height.store (height);
1362- previous_pos_x.store (pos_x);
1363- previous_pos_y.store (pos_y);
1364- // }
1368+ // Stop running capture if the pipewire stream is not connected for any reason or the use ended the portal session
1369+ if ((timeout_ms <= 0 && !shared_state->stream_connected .load ()) || session_cache_t::instance ().has_no_target_node ()) {
1370+ if (capture_running.load ()) {
1371+ {
1372+ std::scoped_lock lock (pipewire.frame_mutex ());
1373+ stream_stopped.store (true );
1374+ }
1375+ pipewire.frame_cv ().notify_all ();
1376+ }
1377+ }
13651378
1379+ // Set width and height to the values negotiated by pipewire
13661380 if (negotiated_w > 0 && negotiated_h > 0 && (negotiated_w != width || negotiated_h != height)) {
13671381 BOOST_LOG (info) << " [portalgrab] Using negotiated resolution " sv
13681382 << negotiated_w << " x" << negotiated_h;
@@ -1441,8 +1455,6 @@ namespace portal {
14411455 BOOST_LOG (warning) << " [portalgrab] PipeWire stream stopped by user." sv;
14421456 capture_running.store (false );
14431457 stream_stopped.store (false );
1444- previous_height.store (0 );
1445- previous_width.store (0 );
14461458 pipewire.frame_cv ().notify_all ();
14471459 return platf::capture_e::error;
14481460 }
@@ -1471,8 +1483,6 @@ namespace portal {
14711483 case platf::capture_e::interrupted:
14721484 capture_running.store (false );
14731485 stream_stopped.store (false );
1474- previous_height.store (0 );
1475- previous_width.store (0 );
14761486 pipewire.frame_cv ().notify_all ();
14771487 return status;
14781488 case platf::capture_e::timeout:
@@ -1481,8 +1491,6 @@ namespace portal {
14811491 BOOST_LOG (info) << " [portalgrab] PipeWire: timeout -> interrupt nudge" ;
14821492 capture_running.store (false );
14831493 stream_stopped.store (false );
1484- previous_height.store (0 );
1485- previous_width.store (0 );
14861494 pipewire.frame_cv ().notify_all ();
14871495 return platf::capture_e::interrupted;
14881496 }
@@ -1724,10 +1732,6 @@ namespace portal {
17241732 std::optional<std::uint64_t > last_seq {};
17251733 std::uint64_t sequence {};
17261734 uint32_t framerate;
1727- static inline std::atomic<uint32_t > previous_height {0 };
1728- static inline std::atomic<uint32_t > previous_width {0 };
1729- static inline std::atomic<uint32_t > previous_pos_x {0 };
1730- static inline std::atomic<uint32_t > previous_pos_y {0 };
17311735 static inline std::atomic<bool > stream_stopped {false };
17321736 static inline std::atomic<bool > capture_running {false };
17331737 std::shared_ptr<shared_state_t > shared_state;
0 commit comments