summaryrefslogtreecommitdiff
path: root/src/event_loop.rs
blob: 89f6f81dfc4e3f64dbd08d19a734a554fa91565b (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
use std::collections::HashMap;
use std::io;
use std::os::fd::RawFd;

use anyhow::Result;
use wayrs_client::Connection;

use crate::state::State;

type Callback = Box<dyn FnMut(EventLoopCtx) -> Result<Action>>;

pub struct EventLoopCtx<'a> {
    pub conn: &'a mut Connection<State>,
    pub state: &'a mut State,
}

/// Simple callback-based event loop. Implemented using `poll`.
pub struct EventLoop {
    cbs: HashMap<RawFd, Callback>,
    on_idle: Vec<Callback>,
}

pub enum Action {
    Keep,
    Unregister,
}

impl EventLoop {
    pub fn new() -> Self {
        Self {
            cbs: HashMap::new(),
            on_idle: Vec::new(),
        }
    }

    pub fn register_with_fd<F>(&mut self, fd: RawFd, cb: F)
    where
        F: FnMut(EventLoopCtx) -> Result<Action> + 'static,
    {
        self.cbs.insert(fd, Box::new(cb));
    }

    pub fn add_on_idle<F>(&mut self, cb: F)
    where
        F: FnMut(EventLoopCtx) -> Result<Action> + 'static,
    {
        self.on_idle.push(Box::new(cb));
    }

    pub fn run(&mut self, conn: &mut Connection<State>, state: &mut State) -> Result<()> {
        let mut pollfds = Vec::new();
        let mut on_idle_scratch = Vec::new();

        while !self.cbs.is_empty() {
            pollfds.clear();
            for &fd in self.cbs.keys() {
                pollfds.push(libc::pollfd {
                    fd,
                    events: libc::POLLIN,
                    revents: 0,
                });
            }

            loop {
                let result = unsafe { libc::poll(pollfds.as_mut_ptr(), pollfds.len() as _, -1) };
                if result == -1 {
                    let err = io::Error::last_os_error();
                    if err.kind() == io::ErrorKind::Interrupted {
                        continue;
                    }
                    return Err(err.into());
                }
                break;
            }

            for fd in &pollfds {
                if fd.revents != 0 {
                    let mut cb = self.cbs.remove(&fd.fd).unwrap();
                    match cb(EventLoopCtx { conn, state })? {
                        Action::Keep => {
                            self.cbs.insert(fd.fd, cb);
                        }
                        Action::Unregister => (),
                    }
                }
            }

            for mut cb in self.on_idle.drain(..) {
                match cb(EventLoopCtx { conn, state })? {
                    Action::Keep => on_idle_scratch.push(cb),
                    Action::Unregister => (),
                }
            }
            self.on_idle.append(&mut on_idle_scratch);
        }
        Ok(())
    }
}