use std::time::Duration;
use anyhow::{Context, Result};
use tokio::runtime::Runtime;
use tokio::sync::watch;
use crate::commands::rec::{run_with_stop, Args};
use crate::hotkey::{HotkeyEvent, HotkeyListener, DEFAULT_TOGGLE_ACCELERATOR};
use crate::runtime::load_or_default_config;
use crate::tray::{IndicatorState, RecordingIndicator, TrayCommand};
const POLL_INTERVAL: Duration = Duration::from_millis(20);
pub fn run_record_with_shell(args: Args, runtime: &Runtime) -> Result<()> {
let cfg = load_or_default_config()?;
let accelerator = cfg
.capture
.hotkey
.unwrap_or_else(|| DEFAULT_TOGGLE_ACCELERATOR.to_string());
let indicator =
RecordingIndicator::start(IndicatorState::Idle).context("starting tray indicator")?;
let hotkey = HotkeyListener::start(&accelerator).context("starting global hotkey listener")?;
indicator.set_state(IndicatorState::Recording);
let (stop_tx, stop_rx) = watch::channel(false);
let task = runtime.spawn(run_with_stop(args, stop_rx));
pump_until_finished(&indicator, &hotkey, &stop_tx, &task);
indicator.set_state(IndicatorState::Idle);
runtime
.block_on(task)
.context("joining recording task")?
.context("recording session failed")
}
fn pump_until_finished(
indicator: &RecordingIndicator,
hotkey: &HotkeyListener,
stop_tx: &watch::Sender<bool>,
task: &tokio::task::JoinHandle<Result<()>>,
) {
while !task.is_finished() {
if matches!(indicator.poll(), Some(TrayCommand::Quit)) {
let _ = stop_tx.send(true);
}
if matches!(hotkey.poll(), Some(HotkeyEvent::Toggle)) {
let _ = stop_tx.send(true);
}
pump_platform(POLL_INTERVAL);
}
}
#[cfg(target_os = "macos")]
#[allow(unsafe_code)]
fn pump_platform(interval: Duration) {
use core_foundation::runloop::{kCFRunLoopDefaultMode, CFRunLoopRunInMode};
let seconds = interval.as_secs_f64();
unsafe {
let _ = CFRunLoopRunInMode(kCFRunLoopDefaultMode, seconds, 0);
}
}
#[cfg(not(target_os = "macos"))]
fn pump_platform(interval: Duration) {
std::thread::sleep(interval);
}
#[cfg(test)]
#[allow(clippy::unwrap_used, clippy::expect_used, clippy::panic)]
mod tests {
use super::*;
#[test]
fn test_poll_interval_is_at_most_50_ms_to_keep_ui_responsive() {
assert!(POLL_INTERVAL <= Duration::from_millis(50));
}
}