Crossterm poll not work

I run event reading using crossterm::event::poll inside separate tokio-rs task, but seems like no events captured. This is part where I read for events:

async fn handle_ui_input(&mut self) -> JoinHandle<()> {
	let key_event_income = self._message_duplex.lock().await.key_event_income.clone();

	tokio::spawn(async move {
		let mut ui_input = UIInput::new(key_event_income);

		loop {
			ui_input.handle();
		}
	})
}

pub struct UIInput {
    _key_event_income: KeyEventIncome,
}

impl UIInput {
    pub fn new(key_event_income: KeyEventIncome) -> Self {
        Self {
            _key_event_income: key_event_income,
        }
    }

    pub fn handle(&mut self) {
        if event::poll(Duration::from_millis(UI_INPUT_TICK_RATE)).unwrap() {
            if let Event::Key(key) = event::read().unwrap() {
                self._key_event_income.send_key_event(key);
            }
        }
    }
}

if condition never true here. So, this is my task where I render UI:

async fn handle_ui(&mut self) -> JoinHandle<()> {
	let message_duplex = Arc::clone(&self._message_duplex);

	tokio::spawn(async move {
		let mut ui = UI::new(CrosstermBackend::new(std::io::stdout()));

		loop {
			ui.render(UIOptions {
				message: message_duplex.lock().await.get_income(),
			});

			sleep(Duration::from_millis(WRITE_TIMEOUT)).await;
		}
	})
}

pub struct UI<'a, B: Backend> {
    _terminal: Terminal<B>,
    _state_flags: UIStateFlags,
    _debug_panel: DebugPanel<'a>,
    // ... rest components
}

impl<'a, B: Backend> UI<'a, B> {
    pub fn new(backend: B) -> Self {
        enable_raw_mode().unwrap();
        execute!(std::io::stdout(), EnterAlternateScreen, EnableMouseCapture).unwrap();

        let mut _terminal = Terminal::new(backend).unwrap();
        _terminal.clear().unwrap();
        _terminal.hide_cursor().unwrap();

        Self {
            _terminal,

            _debug_panel: DebugPanel::new(),
            // ... rest components
        }
    }
}

in case it will be useful, this is code of MessageDuplex:

pub struct MessageDuplex {
    pub dialog_income: DialogIncome,
    pub dialog_outcome: DialogOutcome,
    pub message_income: MessageIncome,
    pub key_event_income: KeyEventIncome,

    _income_receiver: Receiver<IncomeMessageType>,
    _outcome_receiver: Receiver<OutcomeMessageType>,
}

impl MessageDuplex {
    pub fn new() -> Self {
        let (input_tx, input_rx) = mpsc::channel::<IncomeMessageType>();
        let (output_tx, output_rx) = mpsc::channel::<OutcomeMessageType>();

        Self {
            // from client to UI
            dialog_income: DialogIncome::new(input_tx.clone()),
            // from UI (dialog/modal/popup) to client
            dialog_outcome: DialogOutcome::new(output_tx.clone()),
            // from client to UI
            message_income: MessageIncome::new(input_tx.clone()),
            // from client to UI
            key_event_income: KeyEventIncome::new(input_tx.clone()),

            _income_receiver: input_rx,
            _outcome_receiver: output_rx,
        }
    }

    // messages from client to UI
    pub fn get_income(&mut self) -> IncomeMessageType {
        self._income_receiver.recv().unwrap()
    }

    // messages from UI to client
    pub fn get_outcome(&mut self) -> OutcomeMessageType {
        self._outcome_receiver.recv().unwrap()
    }
}

Could somebody help me to find the reason why crossterm::event::poll not work ? Or what possible reasons can be ? I can't capture key events for now.

In case it will be useful, the full project is here: https://github.com/idewave/idewave-cli/tree/IC-36/src.

MessageDuplex
UI
handle_ui_input
handle_ui

Without trying anything this looks to be blocking code so should be in at least be in block_in_place if not a dedicated thread.

do you mean when I wrap this part in tokio::spawn this will not work even considering it use event::poll which should be unblocking according to docs ?

ui_input.handle is next:

pub fn handle(&mut self) {
        if event::poll(Duration::from_millis(UI_INPUT_TICK_RATE)).unwrap() {
            if let Event::Key(key) = event::read().unwrap() {
                self._key_event_income.send_key_event(key);
            }
        }
    }

The loop itself is blocking, since it spins over and over and over again without ever yielding to the executor. You probably want to somehow await on the presence of the event, right? When you have only the poll-based approach, the best bet might be to use yield_now when there's no events to poll - this will still spin a busy loop, but this loop will at least be broken when there's nothing to do immediately.

seems like the issue is not about Rust, but about shell. I've run this within git bash (win10) and no events worked there at all. But after I did cargo build and run directly compiled app from target dir I got events.

But there another issue for now. I got ONLY mouse events. No keyboard events captured at all. App started with enable_raw_mode().

Well, finally this code works correctly:

match self._event_stream.next().fuse().await {
	Some(Ok(event)) => {
		if let Event::Key(key) = event {
			self._key_event_income.send_key_event(key);
		}
	},
	_ => {},
}

but only when run as app, not as cargo run from git bash

This topic was automatically closed 90 days after the last reply. We invite you to open a new topic if you have further questions or comments.