@@ -495,117 +495,102 @@ void Wayland_SendWakeupEvent(SDL_VideoDevice *_this, SDL_Window *window)
495495 WAYLAND_wl_display_flush (d -> display );
496496}
497497
498- static int dispatch_queued_events (SDL_VideoData * viddata )
499- {
500- int rc ;
501-
502- /*
503- * NOTE: When reconnection is implemented, check if libdecor needs to be
504- * involved in the reconnection process.
505- */
506- #ifdef HAVE_LIBDECOR_H
507- if (viddata -> shell .libdecor ) {
508- libdecor_dispatch (viddata -> shell .libdecor , 0 );
509- }
510- #endif
511-
512- rc = WAYLAND_wl_display_dispatch_pending (viddata -> display );
513- return rc >= 0 ? 1 : rc ;
514- }
515-
516498int Wayland_WaitEventTimeout (SDL_VideoDevice * _this , Sint64 timeoutNS )
517499{
518500 SDL_VideoData * d = _this -> internal ;
519501 SDL_WaylandSeat * seat ;
520- bool key_repeat_active = false;
521-
522- WAYLAND_wl_display_flush (d -> display );
502+ Uint64 start = SDL_GetTicksNS ();
503+ const int display_fd = WAYLAND_wl_display_get_fd (d -> display );
504+ int ret ;
505+ bool poll_alarm_set = false;
523506
524507#ifdef SDL_USE_IME
525508 SDL_Window * keyboard_focus = SDL_GetKeyboardFocus ();
526509 if (!d -> text_input_manager && keyboard_focus && SDL_TextInputActive (keyboard_focus )) {
527- SDL_IME_PumpEvents ();
510+ // If a DBus IME is active with no text input protocol, periodically wake to poll it.
511+ if (timeoutNS < 0 || SDL_MS_TO_NS (200 ) <= timeoutNS ) {
512+ timeoutNS = SDL_MS_TO_NS (200 );
513+ poll_alarm_set = true;
514+ }
528515 }
529516#endif
530517
531- #ifdef SDL_USE_LIBDBUS
532- SDL_DBus_PumpEvents ();
533- #endif
534-
535518 // If key repeat is active, we'll need to cap our maximum wait time to handle repeats
536519 wl_list_for_each (seat , & d -> seat_list , link ) {
537520 if (keyboard_repeat_is_set (& seat -> keyboard .repeat )) {
538- Wayland_SeatSetKeymap (seat );
539-
540- const Uint64 elapsed = SDL_GetTicksNS () - seat -> keyboard .repeat .sdl_press_time_ns ;
541- if (keyboard_repeat_handle (& seat -> keyboard .repeat , elapsed )) {
542- // A repeat key event was already due
543- return 1 ;
544- } else {
545- const Uint64 next_repeat_wait_time = (seat -> keyboard .repeat .next_repeat_ns - elapsed ) + 1 ;
546- if (timeoutNS >= 0 ) {
547- timeoutNS = SDL_min (timeoutNS , next_repeat_wait_time );
548- } else {
549- timeoutNS = next_repeat_wait_time ;
550- }
551- key_repeat_active = true;
521+ const Uint64 elapsed = start - seat -> keyboard .repeat .sdl_press_time_ns ;
522+ const Uint64 next_repeat_wait_time = (seat -> keyboard .repeat .next_repeat_ns - elapsed ) + 1 ;
523+ if (timeoutNS < 0 || next_repeat_wait_time <= timeoutNS ) {
524+ timeoutNS = next_repeat_wait_time ;
525+ poll_alarm_set = true;
552526 }
553527 }
554528 }
555529
556- /* wl_display_prepare_read() will return -1 if the default queue is not empty.
557- * If the default queue is empty, it will prepare us for our SDL_IOReady() call. */
558530 if (WAYLAND_wl_display_prepare_read (d -> display ) == 0 ) {
559- // Use SDL_IOR_NO_RETRY to ensure SIGINT will break us out of our wait
560- int err = SDL_IOReady (WAYLAND_wl_display_get_fd (d -> display ), SDL_IOR_READ | SDL_IOR_NO_RETRY , timeoutNS );
561- if (err > 0 ) {
562- // There are new events available to read
563- WAYLAND_wl_display_read_events (d -> display );
564- return dispatch_queued_events (d );
565- } else if (err == 0 ) {
566- int ret = 0 ;
567-
568- // No events available within the timeout
569- WAYLAND_wl_display_cancel_read (d -> display );
531+ if (timeoutNS > 0 ) {
532+ const Uint64 now = SDL_GetTicksNS ();
533+ const Uint64 elapsed = now - start ;
534+ start = now ;
535+ timeoutNS = elapsed <= timeoutNS ? timeoutNS - elapsed : 0 ;
536+ }
570537
571- // If key repeat is active, we might have woken up to generate a key event
572- if (key_repeat_active ) {
573- wl_list_for_each (seat , & d -> seat_list , link ) {
574- if (keyboard_repeat_is_set (& seat -> keyboard .repeat )) {
575- Wayland_SeatSetKeymap (seat );
538+ ret = WAYLAND_wl_display_flush (d -> display );
576539
577- const Uint64 elapsed = SDL_GetTicksNS () - seat -> keyboard .repeat .sdl_press_time_ns ;
578- if (keyboard_repeat_handle (& seat -> keyboard .repeat , elapsed )) {
579- ++ ret ;
580- }
581- }
582- }
540+ if (ret == -1 && errno == EAGAIN ) {
541+ // Unable to write to the socket; poll until the socket can be written to, it times out, or is interrupted.
542+ ret = SDL_IOReady (display_fd , SDL_IOR_WRITE | SDL_IOR_NO_RETRY , timeoutNS );
543+
544+ if (ret <= 0 ) {
545+ // The poll operation timed out or experienced an error, so see if there are any events to read without waiting.
546+ timeoutNS = 0 ;
583547 }
548+ }
584549
585- return ret ;
586- } else {
587- // Error returned from poll()/select()
550+ if (ret < 0 ) {
551+ // Pump events on an interrupt or broken pipe to handle the error.
552+ WAYLAND_wl_display_cancel_read (d -> display );
553+ return errno == EINTR || errno == EPIPE ? 1 : ret ;
554+ }
555+
556+ if (timeoutNS > 0 ) {
557+ const Uint64 now = SDL_GetTicksNS ();
558+ const Uint64 elapsed = now - start ;
559+ start = now ;
560+ timeoutNS = elapsed <= timeoutNS ? timeoutNS - elapsed : 0 ;
561+ }
562+
563+ // Use SDL_IOR_NO_RETRY to catch EINTR.
564+ ret = SDL_IOReady (display_fd , SDL_IOR_READ | SDL_IOR_NO_RETRY , timeoutNS );
565+ if (ret <= 0 ) {
566+ // Timeout or error, cancel the read.
588567 WAYLAND_wl_display_cancel_read (d -> display );
589568
590- if (errno == EINTR ) {
591- /* If the wait was interrupted by a signal, we may have generated a
592- * SDL_EVENT_QUIT event. Let the caller know to call SDL_PumpEvents(). */
593- return 1 ;
569+ // The poll timed out with no data to read, but signal the caller to pump events if polling is required.
570+ if (ret == 0 ) {
571+ return poll_alarm_set ? 1 : 0 ;
594572 } else {
595- return err ;
573+ // Pump events on an interrupt or broken pipe to handle the error.
574+ return errno == EINTR || errno == EPIPE ? 1 : ret ;
596575 }
597576 }
598- } else {
599- // We already had pending events
600- return dispatch_queued_events (d );
577+
578+ ret = WAYLAND_wl_display_read_events (d -> display );
579+ if (ret == -1 ) {
580+ return ret ;
581+ }
601582 }
583+
584+ // Signal to the caller that there might be an event available.
585+ return 1 ;
602586}
603587
604588void Wayland_PumpEvents (SDL_VideoDevice * _this )
605589{
606590 SDL_VideoData * d = _this -> internal ;
607591 SDL_WaylandSeat * seat ;
608- int err ;
592+ const int display_fd = WAYLAND_wl_display_get_fd (d -> display );
593+ int ret = 0 ;
609594
610595#ifdef SDL_USE_IME
611596 SDL_Window * keyboard_focus = SDL_GetKeyboardFocus ();
@@ -618,37 +603,70 @@ void Wayland_PumpEvents(SDL_VideoDevice *_this)
618603 SDL_DBus_PumpEvents ();
619604#endif
620605
606+ // Synthesize key repeat events.
607+ wl_list_for_each (seat , & d -> seat_list , link ) {
608+ if (keyboard_repeat_is_set (& seat -> keyboard .repeat )) {
609+ Wayland_SeatSetKeymap (seat );
610+
611+ const Uint64 elapsed = SDL_GetTicksNS () - seat -> keyboard .repeat .sdl_press_time_ns ;
612+ keyboard_repeat_handle (& seat -> keyboard .repeat , elapsed );
613+ }
614+ }
615+
621616#ifdef HAVE_LIBDECOR_H
622617 if (d -> shell .libdecor ) {
623618 libdecor_dispatch (d -> shell .libdecor , 0 );
624619 }
625620#endif
626621
627- WAYLAND_wl_display_flush (d -> display );
628-
629- /* wl_display_prepare_read() will return -1 if the default queue is not empty.
630- * If the default queue is empty, it will prepare us for our SDL_IOReady() call. */
631- if (WAYLAND_wl_display_prepare_read (d -> display ) == 0 ) {
632- if (SDL_IOReady (WAYLAND_wl_display_get_fd (d -> display ), SDL_IOR_READ , 0 ) > 0 ) {
633- WAYLAND_wl_display_read_events (d -> display );
634- } else {
635- WAYLAND_wl_display_cancel_read (d -> display );
622+ /* If the queue isn't empty, dispatch any old events, and try to prepare for reading again.
623+ * If preparing to read returns -1 on the second try, wl_display_read_events() enqueued new
624+ * events at some point between dispatching the old events and preparing for the read,
625+ * probably from another thread, which means that the events in the queue are current.
626+ */
627+ ret = WAYLAND_wl_display_prepare_read (d -> display );
628+ if (ret == -1 ) {
629+ ret = WAYLAND_wl_display_dispatch_pending (d -> display );
630+ if (ret < 0 ) {
631+ goto connection_error ;
636632 }
633+
634+ ret = WAYLAND_wl_display_prepare_read (d -> display );
637635 }
638636
639- // Dispatch any pre-existing pending events or new events we may have read
640- err = WAYLAND_wl_display_dispatch_pending (d -> display );
637+ if ( ret == 0 ) {
638+ ret = WAYLAND_wl_display_flush (d -> display );
641639
642- wl_list_for_each (seat , & d -> seat_list , link ) {
643- if (keyboard_repeat_is_set (& seat -> keyboard .repeat )) {
644- Wayland_SeatSetKeymap (seat );
640+ if (ret == -1 && errno == EAGAIN ) {
641+ // Unable to write to the socket; wait a brief time to see if it becomes writable.
642+ ret = SDL_IOReady (display_fd , SDL_IOR_WRITE , SDL_MS_TO_NS (4 ));
643+ if (ret > 0 ) {
644+ ret = WAYLAND_wl_display_flush (d -> display );
645+ }
646+ }
645647
646- const Uint64 elapsed = SDL_GetTicksNS () - seat -> keyboard .repeat .sdl_press_time_ns ;
647- keyboard_repeat_handle (& seat -> keyboard .repeat , elapsed );
648+ // If the compositor closed the socket, just jump to the error handler.
649+ if (ret < 0 && errno == EPIPE ) {
650+ WAYLAND_wl_display_cancel_read (d -> display );
651+ goto connection_error ;
648652 }
653+
654+ ret = SDL_IOReady (display_fd , SDL_IOR_READ , 0 );
655+ if (ret > 0 ) {
656+ ret = WAYLAND_wl_display_read_events (d -> display );
657+ if (ret == 0 ) {
658+ ret = WAYLAND_wl_display_dispatch_pending (d -> display );
659+ }
660+ } else {
661+ WAYLAND_wl_display_cancel_read (d -> display );
662+ }
663+
664+ } else {
665+ ret = WAYLAND_wl_display_dispatch_pending (d -> display );
649666 }
650667
651- if (err < 0 && !d -> display_disconnected ) {
668+ connection_error :
669+ if (ret < 0 && !d -> display_disconnected ) {
652670 /* Something has failed with the Wayland connection -- for example,
653671 * the compositor may have shut down and closed its end of the socket,
654672 * or there is a library-specific error.
@@ -659,9 +677,7 @@ void Wayland_PumpEvents(SDL_VideoDevice *_this)
659677 d -> display_disconnected = 1 ;
660678 SDL_LogError (SDL_LOG_CATEGORY_VIDEO , "Wayland display connection closed by server (fatal)" );
661679
662- /* Only send a single quit message, as application shutdown might call
663- * SDL_PumpEvents
664- */
680+ // Only send a single quit message, as application shutdown might call SDL_PumpEvents().
665681 SDL_SendQuit ();
666682 }
667683 }
0 commit comments