@@ -55,18 +55,22 @@ class Q_DECL_HIDDEN WXWaylandPrivate : public WWrapObjectPrivate
5555 // Async property reading
5656 struct PerWindowProps
5757 {
58+ xcb_window_t windowId = XCB_WINDOW_NONE ;
5859 QVector<WXWayland::AsyncPropRequest> requests;
5960 QMap<xcb_atom_t , QByteArray> results;
6061 QVector<xcb_get_property_cookie_t > cookies;
62+ QVector<bool > done;
6163 std::function<void (xcb_window_t , const QMap<xcb_atom_t , QByteArray> &)> callback;
62- QTimer *timer = nullptr ;
63- bool propNotifySeen = false ;
64+ QTimer *timeoutTimer = nullptr ;
65+ QTimer *pollTimer = nullptr ;
6466 };
6567
66- QMap<xcb_window_t , PerWindowProps> asyncProps;
68+ QMap<quint64, PerWindowProps> asyncProps;
69+ quint64 nextAsyncPropRequestId = 1 ;
6770
6871 void xcbPollReplies ();
69- void xcbAsyncTimeoutForWindow (xcb_window_t windowId);
72+ void xcbAsyncTimeoutForRequest (quint64 requestId);
73+ void cleanupAsyncRequest (QMap<quint64, PerWindowProps>::iterator it, bool discardPending);
7074
7175 W_DECLARE_PUBLIC (WXWayland)
7276
@@ -101,28 +105,9 @@ bool xwayland_user_event_handler(wlr_xwayland *xwayland, xcb_generic_event_t *ev
101105
102106 auto *d = self->d_func ();
103107
104- // Trigger async property reading infrastructure if this window is being tracked .
108+ // Progress any pending async property reads while X11 events are flowing .
105109 if (!d->asyncProps .isEmpty ()) {
106110 d->xcbPollReplies ();
107- auto it = d->asyncProps .find (pe->window );
108- if (it != d->asyncProps .end ()) {
109- auto &props = it.value ();
110- props.propNotifySeen = true ;
111- // Resend requests on PROPNOTIFY to get the latest value after the change.
112- xcb_connection_t *conn = self->xcbConnection ();
113- props.cookies .clear ();
114- for (const auto &req : std::as_const (props.requests )) {
115- auto cookie = xcb_get_property_unchecked (conn,
116- false ,
117- pe->window ,
118- req.atom ,
119- req.type ,
120- 0 ,
121- 1024 );
122- props.cookies .append (cookie);
123- }
124- xcb_flush (conn);
125- }
126111 }
127112
128113 const auto &list = self->surfaceList ();
@@ -477,8 +462,10 @@ void WXWayland::readAsyncProperties(
477462 }
478463
479464 WXWaylandPrivate::PerWindowProps props;
465+ props.windowId = windowId;
480466 props.requests = requests;
481467 props.callback = std::move (callback);
468+ props.done .resize (requests.size ());
482469
483470 props.cookies .reserve (requests.size ());
484471 for (const auto &req : std::as_const (requests)) {
@@ -488,15 +475,24 @@ void WXWayland::readAsyncProperties(
488475 }
489476 xcb_flush (conn);
490477
491- // Create per-window timer.
492- props.timer = new QTimer (this );
493- props.timer ->setSingleShot (true );
494- connect (props.timer , &QTimer::timeout, this , [d, windowId]() {
495- d->xcbAsyncTimeoutForWindow (windowId);
478+ const quint64 requestId = d->nextAsyncPropRequestId ++;
479+ if (d->nextAsyncPropRequestId == 0 )
480+ d->nextAsyncPropRequestId = 1 ;
481+
482+ props.timeoutTimer = new QTimer (this );
483+ props.timeoutTimer ->setSingleShot (true );
484+ connect (props.timeoutTimer , &QTimer::timeout, this , [d, requestId]() {
485+ d->xcbAsyncTimeoutForRequest (requestId);
486+ });
487+ props.timeoutTimer ->start (timeoutMs);
488+
489+ props.pollTimer = new QTimer (this );
490+ connect (props.pollTimer , &QTimer::timeout, this , [d]() {
491+ d->xcbPollReplies ();
496492 });
497- props.timer ->start (timeoutMs );
493+ props.pollTimer ->start (1 );
498494
499- d->asyncProps .insert (windowId , std::move (props));
495+ d->asyncProps .insert (requestId , std::move (props));
500496}
501497
502498void WXWaylandPrivate::xcbPollReplies ()
@@ -508,16 +504,21 @@ void WXWaylandPrivate::xcbPollReplies()
508504
509505 xcb_connection_t *conn = q->xcbConnection ();
510506
511- QList<xcb_window_t > toRemove;
507+ struct CompletedRequest
508+ {
509+ xcb_window_t windowId = XCB_WINDOW_NONE ;
510+ QMap<xcb_atom_t , QByteArray> results;
511+ std::function<void (xcb_window_t , const QMap<xcb_atom_t , QByteArray> &)> callback;
512+ };
513+ QVector<CompletedRequest> completed;
512514
513- for (auto it = asyncProps.begin (); it != asyncProps.end (); ++it) {
514- xcb_window_t windowId = it.key ();
515+ for (auto it = asyncProps.begin (); it != asyncProps.end ();) {
515516 auto &props = it.value ();
516517
517518 bool windowDone = true ;
518519 for (int i = 0 ; i < props.cookies .size (); ++i) {
519- if (props.results . contains (props. requests [i]. atom ) )
520- continue ; // already got this one
520+ if (props.done [i])
521+ continue ;
521522
522523 void *replyPtr = nullptr ;
523524 xcb_generic_error_t *err = nullptr ;
@@ -528,6 +529,8 @@ void WXWaylandPrivate::xcbPollReplies()
528529 continue ;
529530 }
530531
532+ props.done [i] = true ;
533+
531534 if (ret == 1 && replyPtr) {
532535 auto *reply = static_cast <xcb_get_property_reply_t *>(replyPtr);
533536 if (reply->type != 0 && reply->value_len > 0 ) {
@@ -541,33 +544,28 @@ void WXWaylandPrivate::xcbPollReplies()
541544 }
542545 }
543546
544- // Fire callback when all replies received AND propNotifySeen.
545- if (windowDone && props.propNotifySeen ) {
546- auto resultCopy = props.results ;
547- props.callback (windowId, resultCopy);
548-
549- toRemove.append (windowId);
547+ if (windowDone) {
548+ CompletedRequest request;
549+ request.windowId = props.windowId ;
550+ request.results = props.results ;
551+ request.callback = std::move (props.callback );
552+ completed.append (std::move (request));
553+ cleanupAsyncRequest (it, false );
554+ it = asyncProps.erase (it);
555+ } else {
556+ ++it;
550557 }
551558 }
552559
553- // Cleanup completed windows.
554- for (auto windowId : std::as_const (toRemove)) {
555- auto it = asyncProps.find (windowId);
556- if (it != asyncProps.end ()) {
557- if (it->timer ) {
558- it->timer ->stop ();
559- delete it->timer ;
560- }
561- asyncProps.erase (it);
562- }
563- }
560+ for (auto &request : completed)
561+ request.callback (request.windowId , request.results );
564562}
565563
566- void WXWaylandPrivate::xcbAsyncTimeoutForWindow ( xcb_window_t windowId )
564+ void WXWaylandPrivate::xcbAsyncTimeoutForRequest (quint64 requestId )
567565{
568566 W_Q (WXWayland);
569567
570- auto it = asyncProps.find (windowId );
568+ auto it = asyncProps.find (requestId );
571569 if (it == asyncProps.end ())
572570 return ;
573571
@@ -577,11 +575,12 @@ void WXWaylandPrivate::xcbAsyncTimeoutForWindow(xcb_window_t windowId)
577575 if (conn) {
578576 // Drain remaining replies.
579577 for (int i = 0 ; i < props.cookies .size (); ++i) {
580- if (props.results . contains (props. requests [i]. atom ) )
578+ if (props.done [i])
581579 continue ;
582580 void *replyPtr = nullptr ;
583581 xcb_generic_error_t *err = nullptr ;
584582 int ret = xcb_poll_for_reply64 (conn, props.cookies [i].sequence , &replyPtr, &err);
583+ props.done [i] = true ;
585584 if (ret == 1 && replyPtr) {
586585 auto *reply = static_cast <xcb_get_property_reply_t *>(replyPtr);
587586 if (reply->type != 0 && reply->value_len > 0 ) {
@@ -595,31 +594,60 @@ void WXWaylandPrivate::xcbAsyncTimeoutForWindow(xcb_window_t windowId)
595594 free (replyPtr);
596595 if (err)
597596 free (err);
597+ if (ret == 0 && props.cookies [i].sequence )
598+ xcb_discard_reply (conn, props.cookies [i].sequence );
598599 }
599600 }
600601 }
601602
603+ auto windowId = props.windowId ;
602604 auto resultCopy = props.results ;
603- props.callback (windowId, resultCopy);
605+ auto callback = std::move (props.callback );
606+ cleanupAsyncRequest (it, false );
607+ asyncProps.erase (it);
608+ callback (windowId, resultCopy);
609+ }
610+
611+ void WXWaylandPrivate::cleanupAsyncRequest (QMap<quint64, PerWindowProps>::iterator it,
612+ bool discardPending)
613+ {
614+ W_Q (WXWayland);
615+ auto &props = it.value ();
616+
617+ if (discardPending) {
618+ auto *conn = q->xcbConnection ();
619+ if (conn) {
620+ for (int i = 0 ; i < props.cookies .size (); ++i) {
621+ if (!props.done [i] && props.cookies [i].sequence )
622+ xcb_discard_reply (conn, props.cookies [i].sequence );
623+ }
624+ }
625+ }
604626
605- // Cleanup.
606- if (props.timer ) {
607- delete props.timer ;
627+ if (props.timeoutTimer ) {
628+ props.timeoutTimer ->stop ();
629+ props.timeoutTimer ->deleteLater ();
630+ props.timeoutTimer = nullptr ;
631+ }
632+
633+ if (props.pollTimer ) {
634+ props.pollTimer ->stop ();
635+ props.pollTimer ->deleteLater ();
636+ props.pollTimer = nullptr ;
608637 }
609- asyncProps.erase (it);
610638}
611639
612640void WXWayland::cancelAsyncProperties (xcb_window_t windowId)
613641{
614642 W_D (WXWayland);
615- auto it = d->asyncProps .find (windowId);
616- if (it == d->asyncProps .end ())
617- return ;
618-
619- if (it->timer ) {
620- delete it->timer ;
643+ for (auto it = d->asyncProps .begin (); it != d->asyncProps .end ();) {
644+ if (it.value ().windowId == windowId) {
645+ d->cleanupAsyncRequest (it, true );
646+ it = d->asyncProps .erase (it);
647+ } else {
648+ ++it;
649+ }
621650 }
622- d->asyncProps .erase (it);
623651}
624652
625653bool WXWayland::event (QEvent *ev)
0 commit comments