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
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
use crate::{child_process::Nanny, config::Config, models::FocusBehaviour};
use crate::{CommandPipe, DisplayServer, Manager, Mode, StateSocket, Window, Workspace};
use std::path::{Path, PathBuf};
use std::sync::{atomic::Ordering, Once};

impl<C: Config, SERVER: DisplayServer> Manager<C, SERVER> {
    pub async fn event_loop(mut self) {
        let socket_file = place_runtime_file("current_state.sock")
            .expect("ERROR: couldn't create current_state.sock");
        let mut state_socket = StateSocket::default();
        state_socket
            .listen(socket_file)
            .await
            .expect("ERROR: couldn't connect to current_state.sock");

        let pipe_file =
            place_runtime_file("commands.pipe").expect("ERROR: couldn't create commands.pipe");
        let mut command_pipe = CommandPipe::new(pipe_file)
            .await
            .expect("ERROR: couldn't connect to commands.pipe");

        //start the current theme
        let after_first_loop: Once = Once::new();

        //main event loop
        let mut event_buffer = vec![];
        loop {
            if self.state.mode == Mode::Normal {
                state_socket.write_manager_state(&self).await.ok();
            }
            self.display_server.flush();

            let mut needs_update = false;
            tokio::select! {
                _ = self.display_server.wait_readable(), if event_buffer.is_empty() => {
                    event_buffer.append(&mut self.display_server.get_next_events());
                    continue;
                }
                //Once in a blue moon we miss the focus event,
                //This is to double check that we know which window is currently focused
                _ = timeout(100), if event_buffer.is_empty() && self.state.focus_manager.behaviour == FocusBehaviour::Sloppy => {
                    let mut focus_event = self.display_server.verify_focused_window();
                    event_buffer.append(&mut focus_event);
                    continue;
                }
                Some(cmd) = command_pipe.read_command(), if event_buffer.is_empty() => {
                    needs_update = self.command_handler(&cmd) || needs_update;
                    self.display_server.update_theme_settings(&self.state.config);
                }
                else => {
                    event_buffer.drain(..).for_each(|event| needs_update = self.display_event_handler(event) || needs_update);
                }
            }

            //if we need to update the displayed state
            if needs_update {
                self.update_windows();

                match self.state.mode {
                    Mode::Normal => {
                        let windows: Vec<&Window> = self.state.windows.iter().collect();
                        let focused = self.focused_window();
                        self.display_server.update_windows(windows, focused, &self);
                        let workspaces: Vec<&Workspace> = self.state.workspaces.iter().collect();
                        let focused = self.focused_workspace();
                        self.display_server.update_workspaces(workspaces, focused);
                    }
                    //when (resizing / moving) only deal with the single window
                    Mode::ResizingWindow(h) | Mode::MovingWindow(h) => {
                        let focused = self.focused_window();
                        let windows: Vec<&Window> = (&self.state.windows)
                            .iter()
                            .filter(|w| w.handle == h)
                            .collect();
                        self.display_server.update_windows(windows, focused, &self);
                    }
                }
            }

            //preform any actions requested by the handler
            while !self.state.actions.is_empty() {
                if let Some(act) = self.state.actions.pop_front() {
                    if let Some(event) = self.display_server.execute_action(act) {
                        event_buffer.push(event);
                    }
                }
            }

            //after the very first loop run the 'up' scripts (global and theme). we need the unix
            //socket to already exist.
            after_first_loop.call_once(|| {
                match Nanny::run_global_up_script() {
                    Ok(child) => {
                        child.map(|child| self.children.insert(child));
                    }
                    Err(err) => log::error!("Global up script faild: {}", err),
                }
                match Nanny::boot_current_theme() {
                    Ok(child) => {
                        child.map(|child| self.children.insert(child));
                    }
                    Err(err) => log::error!("Theme loading failed: {}", err),
                }

                C::load_state(&mut self);
            });

            if self.reap_requested.swap(false, Ordering::SeqCst) {
                self.children.reap();
            }

            if self.reload_requested {
                state_socket.shutdown().await;
                break;
            }
        }
    }
}

fn place_runtime_file<P>(path: P) -> std::io::Result<PathBuf>
where
    P: AsRef<Path>,
{
    xdg::BaseDirectories::with_prefix("leftwm")?.place_runtime_file(path)
}

async fn timeout(mills: u64) {
    use tokio::time::{sleep, Duration};
    sleep(Duration::from_millis(mills)).await;
}