I'm using Rust for my ncurses app.
When the user presses F5, line numbers window should be toggled. When the user presses the F5 key for the first time, the window appears as it is supposed to. However, on second key press, the window does not go away, it's still there, as if the delwin call does not succeed. I tried refreshing the screen after it, but have had no success.
Here's a minimal example:
use ncurses::*;
struct LineNumbers {
window: WINDOW,
shown: bool
}
impl LineNumbers {
fn new() -> LineNumbers {
LineNumbers {
window: newwin(LINES(), 5, 0, 0),
shown: false
}
}
fn toggle(&mut self) {
if self.shown == true {
self.hide();
} else {
self.show();
}
}
fn show(&mut self) {
self.shown = true;
wbkgd(self.window, COLOR_PAIR(1));
wrefresh(self.window);
}
fn hide(&mut self) {
self.shown = false;
delwin(self.window);
refresh();
}
}
fn main() {
setlocale(LcCategory::all, "");
initscr();
keypad(stdscr(), true);
start_color();
init_pair(1, COLOR_RED, COLOR_RED);
let mut ln = LineNumbers::new();
loop {
let user_input = get_wch();
match user_input.unwrap() {
WchResult::Char(ch) => {
match ch {
27 => break,
_ => {}
}
},
WchResult::KeyCode(code) => {
match code {
KEY_F5 => {
ln.toggle();
},
_ => {}
}
}
}
}
endwin();
}
What could be the issue?
You could touch the main window before doing a refresh.
Deleting a window won't do that for you (man delwin):
Calling delwin deletes the named window, freeing all memory associated
with it (it does not actually erase the window's screen image). Sub-
windows must be deleted before the main window can be deleted.
It seems that ncurses-rs has no documentation, but is a "thin layer" (a binding). Use the ncurses manpages.
Related
I wanted to know if it's possible to loop{} and call a function inside the loop that takes a parameter that is not a reference without copying it.
I am using the crate posix_mq, and I want to open an existing queue, if it not exists wait 1 second and try to open it again.
Here is my code:
pub fn open(&mut self) -> Result<(), posix_mq::error::Error> {
let mut attempt = self.tools_.get_max_attempt(); //30
let queue_name: Name = Name::new(&self.queue_name_)?;
loop {
match Queue::open(queue_name) {
Ok(q) => {
self.my_queue_ = Some(q);
Ok::<(),posix_mq::error::Error>(());
}
Err(e) => match e {
posix_mq::error::Error::QueueNotFound() => {
let waiting_print = "Waiting for creation of the queue.".to_string();
self.tools_.update_printing_elements(waiting_print, false);
attempt -= 1;
if attempt <= 1 {
return Err(e);
}
thread::sleep(time::Duration::from_secs(1));
},
_ => {Err::<(), posix_mq::error::Error>(e);},
}
}
}
}
I want that Queue::open() borrows queue_name without creating a copy of it instead of taking ownership of it.
I have two async functions: get_message and get_event. I'd like to perform an action whenever a message arrives or an event comes and do that forever in an infinite loop.
The simplified setup looks like this:
use futures::{future::select, future::Either, pin_mut};
impl MsgReceiver {
async fn get_message(&mut self) -> Message { /* ... */ }
}
impl EventListener {
async fn get_event(&mut self) -> Event { /* ... */ }
}
async fn eternal_task(receiver: MsgReceiver, listener: EventListener) -> ! {
let get_msg_fut = receiver.get_message();
pin_mut!(get_msg_fut);
loop {
let get_event_fut = listener.get_event();
pin_mut!(get_event_fut);
match select(get_event_fut, get_msg_fut).await {
Either::Left((ev, r_get_msg_fut)) => {
/* react to the event */
// r_get_msg_fut is not done, how to reuse it in the next iteration?
}
Either::Right((msg, r_get_event_fut)) => {
/* react to the message */
// it's fine to drop get_event_fut here
// the following line causes a double-mut-borrow error on receiver,
// despite receiver isn't borrowed anymore (the old future is completed and dropped)
let new_future = receiver.get_message();
}
};
}
}
I have three major questions here:
When an event comes first, how to tell rust that I want to reuse the incomplete get_message future on the next loop iteration?
When a message comes first, how to construct a new future without a borrow error?
When (2) is solved, how to put the new future into the same pinned memory location and use it on the next loop iteration?
I had success using this, but could not get rid of the Box::pin
use futures::{future::select, future::Either, pin_mut};
use std::sync::Mutex;
#[derive(Debug)]
struct MsgReceiver;
#[derive(Debug)]
struct EventListener;
#[derive(Debug)]
struct Message;
#[derive(Debug)]
struct Event;
impl MsgReceiver {
async fn get_message(&mut self) -> Message {
Message
}
}
impl EventListener {
async fn get_event(&mut self) -> Event {
Event
}
}
async fn eternal_task(receiver: MsgReceiver, mut listener: EventListener) -> ! {
let receiver = Mutex::new(receiver);
let mut f = None;
loop {
let get_msg_fut = match f.take() {
None => {
let mut l = receiver.lock();
Box::pin(async move {
l.get_message().await
})
}
Some(f) => f,
};
let get_event_fut = listener.get_event();
pin_mut!(get_event_fut);
match select(get_event_fut, get_msg_fut).await {
Either::Left((ev, r_get_msg_fut)) => {
/* react to the event */
// store the future for next iteration
f = Some(r_get_msg_fut);
}
Either::Right((msg, r_get_event_fut)) => {
/* react to the message */
}
};
}
}
#[tokio::main]
async fn main() {
eternal_task(MsgReceiver, EventListener).await;
}
I think this is tricky to get right, even with unsafe which would probably be needed to accomplish this. Persisting and reusing the same variables isn't too hard, its actually #2 that's the hardest (at least with the current borrow checker).
I found a solution that totally circumvents the problem by using the async-stream crate to provide an intermediary:
async fn eternal_task(mut receiver: MsgReceiver, mut listener: EventListener) -> ! {
let combined = futures::stream::select(
stream! { loop { yield Either::Left(receiver.get_message().await); } },
stream! { loop { yield Either::Right(listener.get_event().await); } },
);
pin_mut!(combined);
while let Some(msg_or_evt) = combined.next().await {
match msg_or_evt {
Either::Left(msg) => {
// do something with msg
}
Either::Right(evt) => {
// do something with evt
}
};
}
unreachable!()
}
It uses the stream! macro to generate a type that continuously calls and yields values from .get_message() and .get_event(). It then uses futures::stream::select and Either to combine them. And then its just a matter of looping over the results. It works in #![no_std].
I am attempting to show a MessageDialog upon keypress. So far, it doesn't seem to do anything. The code does compile, but does not function. The code verifies that the keypress works, but the dialog simply wont display. I have tried taking the returned IAsyncOperation and using 'get()' but that seems to freeze the application completely. What am I missing?
//#![windows_subsystem = "windows"]
use winit::{
event::{Event, WindowEvent},
event_loop::{ControlFlow, EventLoop},
window::WindowBuilder,
};
use winrt::*;
import!(
dependencies
os
modules
"windows.data.xml.dom"
"windows.foundation"
"windows.ui"
"windows.ui.popups"
);
fn main() {
let event_loop = EventLoop::new();
let window = WindowBuilder::new().build(&event_loop).unwrap();
event_loop.run(move |event, _, control_flow| {
*control_flow = ControlFlow::Wait;
match event {
Event::WindowEvent {
event: WindowEvent::CloseRequested,
window_id,
} if window_id == window.id() => *control_flow = ControlFlow::Exit,
Event::WindowEvent {
event: WindowEvent::Resized (_size),
..
} => (),
Event::WindowEvent {
event: WindowEvent::KeyboardInput {input,..},
..
} if input.state == winit::event::ElementState::Pressed => {
use windows::ui::popups::MessageDialog;
let mymsg = MessageDialog::create("Test").unwrap().show_async();
println!("KeyState-{}",input.scancode);
},
_ => (),
}
});
}
This highlights one of the differences in using some WinRT APIs in a Win32 application. In a UWP application, your app has a CoreWindow associated with its main thread. Normally, dialogs query for this window and display themselves as modal to it. However, in a Win32 application the system can't make an assumption about what window you want to use. In these instances, you need to QI for the IInitializeWithWindow interface and call the initialize function with your window handle.
Since the IInitializeWithWindow interface is a pure COM interface and not a WinRT one, winrt-rs doesn't have a projection for it. Instead you'll need to define it yourself (make sure you get the GUID right!):
#[repr(C)]
pub struct abi_IInitializeWithWindow {
__base: [usize; 3],
initialize: extern "system" fn(
winrt::NonNullRawComPtr<InitializeWithWindowInterop>,
*mut c_void,
) -> winrt::ErrorCode,
}
unsafe impl winrt::ComInterface for InitializeWithWindowInterop {
type VTable = abi_IInitializeWithWindow;
fn iid() -> winrt::Guid {
winrt::Guid::from_values(1047057597, 28981, 19728, [128, 24, 159, 182, 217, 243, 63, 161])
}
}
#[repr(transparent)]
#[derive(Default, Clone)]
pub struct InitializeWithWindowInterop {
ptr: winrt::ComPtr<InitializeWithWindowInterop>,
}
impl InitializeWithWindowInterop {
pub fn initialize(
&self,
window: *mut c_void,
) -> winrt::Result<()> {
match self.ptr.abi() {
None => panic!("The `this` pointer was null when calling method"),
Some(this) => unsafe {
(this.vtable().initialize)(
this,
window,
)
.ok()?;
Ok(())
},
}
}
}
To get the window handle from your winit Window, you'll need the raw-window-handle crate. From there you can create a helper trait for anything that implements HasRawWindowHandle:
trait InitializeWithWindow {
fn initialize_with_window<O: RuntimeType + ComInterface>(&self, object: &O) -> winrt::Result<()>;
}
impl<T> InitializeWithWindow for T
where
T: HasRawWindowHandle,
{
fn initialize_with_window<O: RuntimeType + ComInterface>(
&self,
object: &O,
) -> winrt::Result<()> {
// Get the window handle
let window_handle = self.raw_window_handle();
let window_handle = match window_handle {
raw_window_handle::RawWindowHandle::Windows(window_handle) => window_handle.hwnd,
_ => panic!("Unsupported platform!"),
};
let init: InitializeWithWindowInterop = object.try_into()?;
init.initialize(window_handle)?;
Ok(())
}
}
Now in your event loop you can call it using the following:
let dialog = MessageDialog::create("Test").unwrap();
window.initialize_with_window(&dialog).unwrap();
dialog.show_async().unwrap();
println!("KeyState-{}",input.scancode);
Note that I'm not waiting on the result of the IAsyncOperation that is retunred by show_async. The reason is because only synchronous waiting it supported by the projection right now, and that would tie up your message pump and cause the window to hang. This means that the print statement will run before the dialog returns. This should improve once broader async support is up and running in the projection.
You can now use your initialize_with_window method on dialogs and pickers (e.g. FileSavePicker, GraphicsCapturePicker).
Context
I am working on a pomodoro command line app written in rust, most of it worked well but now I want to edit the text of a pomodoro item in the database. All of the actions in the app are triggered by keystrokes, pausing/resuming, quitting etc. and as well editing the text.
Now I want to read the text from stdin but the key-events are as sourced as well from stdin, but on a different thread. I came up with using a stdin.lock() - which works almost fine.
The problem
How can I read a line from stdin in the main thread, without dropping the first letter, due to the event listener being triggered in its thread, before the lock in the main thread is acquired.
expected behaviour:
press t => print Reading from stdin!
type abc<enter> => print You typed: Some("abc")
actual behaviour:
press t => print Reading from stdin!
type abc<enter> => print You typed: Some("bc")
Minimal non-working example
Here is an example that shows the described behaviour:
use failure;
use std::io::{stdin, stdout};
use std::sync::mpsc;
use std::thread;
use termion::event::Key;
use termion::input::TermRead;
use termion::raw::IntoRawMode;
use tui::backend::TermionBackend;
use tui::Terminal;
pub enum Event {
Input(Key),
}
#[allow(dead_code)]
pub struct Events {
rx: mpsc::Receiver<Event>,
input_handle: thread::JoinHandle<()>,
}
impl Events {
pub fn new() -> Events {
let (tx, rx) = mpsc::channel();
let input_handle = {
let tx = tx.clone();
thread::spawn(move || {
let stdin = stdin();
for evt in stdin.keys() {
match evt {
Ok(key) => {
if let Err(_) = tx.send(Event::Input(key)) {
return;
}
}
Err(_) => {}
}
}
})
};
Events {
rx,
input_handle,
}
}
pub fn next(&self) -> Result<Event, mpsc::RecvError> {
self.rx.recv()
}
}
pub fn key_handler(key: Key) -> bool {
match key {
Key::Char('t') => {
println!("Reading from stdin!");
let stdin = stdin();
let mut handle = stdin.lock();
let input = handle.read_line().unwrap();
println!("You typed: {:?}", input);
}
_ =>{
println!("no thing!");
}
};
key == Key::Char('q')
}
fn main() -> Result<(), failure::Error> {
let stdout = stdout().into_raw_mode()?;
let backend = TermionBackend::new(stdout);
let mut terminal = Terminal::new(backend)?;
terminal.clear()?;
terminal.hide_cursor()?;
let events = Events::new();
loop {
match events.next()? {
Event::Input(key) => {
if key_handler(key) {
break;
}
}
}
}
terminal.clear()?;
terminal.show_cursor()?;
Ok(())
}
Update
Cargo.toml
[package]
name = "mnwe"
version = "1.1.0"
edition = "2018"
autotests = false
[[bin]]
bench = false
path = "app/main.rs"
name = "mnwe"
[dependencies]
failure = "0.1"
termion = "1.5.3"
tui = "0.7"
The problem, as correctly identified, is the race on stdin().lock() between stdin.keys() in the events thread and stdin.lock() in key_handler (which the events thread tends to win, and eat one key).
For the sake of the bounty, I see four possible approaches:
At easiest, you can avoid having threads at all, and instead regularly poll for new input with termion::async_std. (It's what I ended up doing for my own tui application. In the end, you're likely to be polling the event receiver anyway, so why not poll stdin directly.)
If your problem allows it, you could do the stdin reading directly on the event thread. Instead of sending key events over the channel, you would send something you could call "user commands" over the channel:
// Channel data:
pub enum Command {
StdinInput(String),
Quit,
// Add more commands
Unknown(Key),
}
// Send side
thread::spawn(move || {
for evt in stdin().keys() {
match evt {
Ok(key) => {
let cmd = match key {
Key::Char('t') => {
println!("Reading from stdin!");
let input = stdin().lock().read_line().unwrap();
Command::StdinInput(input.unwrap_or(String::new()))
}
Key::Char('q') => Command::Quit,
_ => Command::Unknown(key),
};
if let Err(_) = tx.send(cmd) {
return;
}
}
Err(_) => {}
}
}
})
// Receive side
loop {
match events.next()? {
Command::StdinInput(input) => println!("You typed: {}", input),
Command::Quit => break,
Command::Unknown(k) => println!("no thing: {:?}", k),
}
}
If you must absolutely have access stdin from to threads, I would not recommend using a CondVar, but passing the sender of another channel through the event channel. Why? Because it's much harder to get wrong. Any channel will do, but I think oneshot::channel() is most suitable here:
// Channel data
pub enum Event {
Input(Key, oneshot::Sender<()>),
}
// Send side
for evt in stdin.keys() {
let (shot_send, shot_recv) = oneshot::channel();
match evt {
Ok(key) => {
if let Err(_) = tx.send(Event::Input(key, shot_send)) {
return;
}
shot_recv.recv().ok();
}
Err(_) => {}
}
}
// Receive side
loop {
match events.next()? {
Event::Input(key, done) => {
match key {
Key::Char('t') => {
println!("Reading from stdin!");
let stdin = stdin();
let mut handle = stdin.lock();
let input = handle.read_line().unwrap();
println!("You typed: {:?}", input);
// It doesn't really matter whether we send anything
// but using the channel here avoids mean surprises about when it gets dropped
done.send(()).ok();
}
Key::Char('q') => break,
_ => println!("no thing!"),
};
}
}
}
You could also not do the stdin().lock().read_line() at all, but reassemble the user input line from the key stroke events. I wouldn't do that.
My Gtk-rs application crashes whenever I try calling the show_all() function.
In a simple application window, I have added a headerbar and a label. If I compile without adding the headerbar, the window works and shows the label as intended. However if I add the headerbar the window crashes.
use gio::prelude::*;
use gtk::{
prelude::*,
HeaderBarExt,
GtkWindowExt
};
fn gen_header_bar(subtitle: Option<String>) -> gtk::HeaderBar {
let header_bar = gtk::HeaderBar::new();
header_bar.set_title(Some(crate::consts::APP_NAME));
header_bar.set_show_close_button(true);
match subtitle {
Some(subtitle) => {
header_bar.set_subtitle(Some(&subtitle));
},
_ => {
}
}
header_bar
}
pub fn build_application_window() -> Result<(), Box<dyn std::error::Error>> {
let application = gtk::Application::new(
Some(crate::consts::APP_ID),
gio::ApplicationFlags::FLAGS_NONE,
)?;
application.connect_activate(move |app| {
let window = gtk::ApplicationWindow::new(app);
window.set_title(crate::consts::APP_NAME);
window.set_default_size(32 * 10, 200); // golden ratio
window.set_position(gtk::WindowPosition::Center);
let header_bar = gen_header_bar(None);
window.set_titlebar(Some(&header_bar));
window.add(&{
let label = gtk::Label::new(Some("Welcome!"));
label
});
window.show_all(); // crashes here
});
application.run(&std::env::args().collect::<Vec<_>>());
Ok(())
}
What is causing this?