@@ -381,7 +381,18 @@ impl Application {
381
381
let timeout = Duration :: from_millis ( ( 1000.0 / refresh_rate) as u64 ) ;
382
382
let mut last_idle_time = Instant :: now ( ) ;
383
383
loop {
384
+ // Figure out when the next wakeup needs to happen
385
+ let next_timeout = if let Ok ( state) = self . state . try_borrow ( ) {
386
+ state. windows
387
+ . values ( )
388
+ . filter_map ( |w| w. next_timeout ( ) )
389
+ . min ( )
390
+ } else {
391
+ log:: error!( "Getting next timeout, application state already borrowed" ) ;
392
+ None
393
+ } ;
384
394
let next_idle_time = last_idle_time + timeout;
395
+
385
396
self . connection . flush ( ) ?;
386
397
387
398
// Before we poll on the connection's file descriptor, check whether there are any
@@ -390,7 +401,7 @@ impl Application {
390
401
let mut event = self . connection . poll_for_event ( ) ?;
391
402
392
403
if event. is_none ( ) {
393
- poll_with_timeout ( & self . connection , self . idle_read , next_idle_time)
404
+ poll_with_timeout ( & self . connection , self . idle_read , next_timeout , next_idle_time)
394
405
. context ( "Error while waiting for X11 connection" ) ?;
395
406
}
396
407
@@ -409,6 +420,17 @@ impl Application {
409
420
}
410
421
411
422
let now = Instant :: now ( ) ;
423
+ if let Some ( timeout) = next_timeout {
424
+ if timeout <= now {
425
+ if let Ok ( state) = self . state . try_borrow ( ) {
426
+ for w in state. windows . values ( ) {
427
+ w. run_timers ( now) ;
428
+ }
429
+ } else {
430
+ log:: error!( "In timer loop, application state already borrowed" ) ;
431
+ }
432
+ }
433
+ }
412
434
if now >= next_idle_time {
413
435
last_idle_time = now;
414
436
drain_idle_pipe ( self . idle_read ) ?;
@@ -510,11 +532,13 @@ fn drain_idle_pipe(idle_read: RawFd) -> Result<(), Error> {
510
532
/// writing into our idle pipe and the `timeout` has passed.
511
533
// This was taken, with minor modifications, from the xclock_utc example in the x11rb crate.
512
534
// https://github.com/psychon/x11rb/blob/a6bd1453fd8e931394b9b1f2185fad48b7cca5fe/examples/xclock_utc.rs
513
- fn poll_with_timeout ( conn : & Rc < XCBConnection > , idle : RawFd , timeout : Instant ) -> Result < ( ) , Error > {
535
+ fn poll_with_timeout ( conn : & Rc < XCBConnection > , idle : RawFd , timer_timeout : Option < Instant > , idle_timeout : Instant ) -> Result < ( ) , Error > {
514
536
use nix:: poll:: { poll, PollFd , PollFlags } ;
515
537
use std:: os:: raw:: c_int;
516
538
use std:: os:: unix:: io:: AsRawFd ;
517
539
540
+ let mut now = Instant :: now ( ) ;
541
+ let earliest_timeout = idle_timeout. min ( timer_timeout. unwrap_or ( idle_timeout) ) ;
518
542
let fd = conn. as_raw_fd ( ) ;
519
543
let mut both_poll_fds = [
520
544
PollFd :: new ( fd, PollFlags :: POLLIN ) ,
@@ -525,36 +549,60 @@ fn poll_with_timeout(conn: &Rc<XCBConnection>, idle: RawFd, timeout: Instant) ->
525
549
526
550
// We start with no timeout in the poll call. If we get something from the idle handler, we'll
527
551
// start setting one.
528
- let mut poll_timeout = - 1 ;
552
+ let mut honor_idle_timeout = false ;
529
553
loop {
530
554
fn readable ( p : PollFd ) -> bool {
531
555
p. revents ( )
532
556
. unwrap_or_else ( PollFlags :: empty)
533
557
. contains ( PollFlags :: POLLIN )
534
558
} ;
535
559
560
+ // Compute the deadline for when poll() has to wakeup
561
+ let deadline = if honor_idle_timeout {
562
+ Some ( earliest_timeout)
563
+ } else {
564
+ timer_timeout
565
+ } ;
566
+ // ...and convert the deadline into an argument for poll()
567
+ let poll_timeout = if let Some ( deadline) = deadline {
568
+ if deadline <= now {
569
+ break ;
570
+ } else {
571
+ let millis = c_int:: try_from ( deadline. duration_since ( now) . as_millis ( ) )
572
+ . unwrap_or ( c_int:: max_value ( ) - 1 ) ;
573
+ // The above .as_millis() rounds down. This means we would wake up before the
574
+ // deadline is reached. Add one to 'simulate' rounding up instead.
575
+ millis + 1
576
+ }
577
+ } else {
578
+ // No timeout
579
+ -1
580
+ } ;
581
+
536
582
match poll ( poll_fds, poll_timeout) {
537
583
Ok ( _) => {
538
584
if readable ( poll_fds[ 0 ] ) {
539
585
// There is an X11 event ready to be handled.
540
586
break ;
541
587
}
588
+ now = Instant :: now ( ) ;
589
+ if timer_timeout. is_some ( ) && now >= timer_timeout. unwrap ( ) {
590
+ break ;
591
+ }
542
592
if poll_fds. len ( ) == 1 || readable ( poll_fds[ 1 ] ) {
543
593
// Now that we got signalled, stop polling from the idle pipe and use a timeout
544
594
// instead.
545
595
poll_fds = & mut just_connection;
546
-
547
- let now = Instant :: now ( ) ;
548
- if now >= timeout {
596
+ honor_idle_timeout = true ;
597
+ if now >= idle_timeout {
549
598
break ;
550
- } else {
551
- poll_timeout = c_int:: try_from ( timeout. duration_since ( now) . as_millis ( ) )
552
- . unwrap_or ( c_int:: max_value ( ) )
553
599
}
554
600
}
555
601
}
556
602
557
- Err ( nix:: Error :: Sys ( nix:: errno:: Errno :: EINTR ) ) => { }
603
+ Err ( nix:: Error :: Sys ( nix:: errno:: Errno :: EINTR ) ) => {
604
+ now = Instant :: now ( ) ;
605
+ }
558
606
Err ( e) => return Err ( e. into ( ) ) ,
559
607
}
560
608
}
0 commit comments