Rust winapi GetLastInputInfo not detecting correctly - winapi

I am VERY new to rust, and really programming in general. I am writing a rust program that will detect idle and print out the time since the last input. I am using the winapi crate. The code below returns between 0ns and 31ms, regardless of how long it's been since I pressed a key or moved the mouse. It never gets higher than 31ms.
use winapi::um::{
winuser::{
LASTINPUTINFO,
PLASTINPUTINFO,
GetLastInputInfo
},
};
fn sleep(milliseconds: u64){
let mills = std::time::Duration::from_millis(milliseconds);
std::thread::sleep(mills);
}
fn main() {
loop {
let now = unsafe { winapi::um::sysinfoapi::GetTickCount() };
let mut last_input_info = LASTINPUTINFO {
cbSize: std::mem::size_of::<LASTINPUTINFO>() as u32,
dwTime: 0
};
let p_last_input_info: PLASTINPUTINFO = &mut last_input_info as *mut LASTINPUTINFO;
let ok = unsafe { GetLastInputInfo(p_last_input_info) } != 0;
let logvar = match ok {
true => {
let millis = now - last_input_info.dwTime;
Ok(std::time::Duration::from_millis(millis as u64))
},
false => Err(format!("GetLastInputInfo failed"))
}.unwrap();
println!("{:?}", logvar);
sleep(1000);
};
}
I considered that it might be some program that is keeping the PC from going idle, so using powercfg -requests, I found some audio streams open (still don't know how to fix that). I just don't know if that could be what's happening here. Community expertise requested!

It might be good for inspecting problem to execute cpp code below. It works well on my PC.
If it work well --> Your Rust code or Rust winapi problem
If it does not work well --> Your PC problem
#include <windows.h>
int main() {
LASTINPUTINFO lii;
lii.cbSize = sizeof(LASTINPUTINFO);
for (;;) {
int ret = GetLastInputInfo(&lii);
printf("ret=%d diff=%d\n", ret, GetTickCount() - lii.dwTime);
Sleep(1000);
}
}
output in my PC with mouse move and stop
ret=1 diff=578
ret=1 diff=1578
ret=1 diff=2594
ret=1 diff=0
ret=1 diff=422
ret=1 diff=1422
ret=1 diff=2422
ret=1 diff=3438
ret=1 diff=4438
ret=1 diff=5438
ret=1 diff=79
ret=1 diff=16

I have found the culprit. It was not the rust code at all, but my Game Controller Driver that was registering as input. For some reason, whenever I have a game controller connected, it does that. It took me forever to figure it out, and even so happen to just stumble onto the answer after weeks of googling. Consider this question closed.

Related

Not able to receive 'Continued' status from waitpid using the nix crate

I'm experimenting with the nix crate in order to debug child processes. I would like to receive events when the child process stops, continues and exits. Stoppages and exits are reported using the waitpid function. I'm having trouble receiving the continued status though.
I tried to convert my code to a minimum reproduceable example and hoping someone is able to spot the problem.
fn main() {
// fork here
let res = unsafe { fork().unwrap() };
if res.is_child() {
traceme().unwrap();
unsafe {
breakpoint();
}
} else {
let status = waitpid(Pid::from_raw(-1), Some(WaitPidFlag::WCONTINUED)).unwrap();
let pid = status.pid().unwrap();
println!("{:?}", status);
println!("Calling continue....");
cont(pid, None).unwrap();
println!("{:?}", waitpid(Pid::from_raw(-1), None).unwrap());
}
}
is producing the following output:
Stopped(Pid(28411), SIGTRAP)
Calling continue....
Exited(Pid(28411), 0)

How to read one single char in Rust? [duplicate]

I want to run an executable that blocks on stdin and when a key is pressed that same character is printed immediately without Enter having to be pressed.
How can I read one character from stdin without having to hit Enter? I started with this example:
fn main() {
println!("Type something!");
let mut line = String::new();
let input = std::io::stdin().read_line(&mut line).expect("Failed to read line");
println!("{}", input);
}
I looked through the API and tried replacing read_line() with bytes(), but everything I try requires me to hit Enter before read occurs.
This question was asked for C/C++, but there seems to be no standard way to do it: Capture characters from standard input without waiting for enter to be pressed
It might not be doable in Rust considering it's not simple in C/C++.
While #Jon's solution using ncurses works, ncurses clears the screen by design. I came up with this solution that uses the termios crate for my little project to learn Rust. The idea is to modify ECHO and ICANON flags by accessing tcsetattr through termios bindings.
extern crate termios;
use std::io;
use std::io::Read;
use std::io::Write;
use termios::{Termios, TCSANOW, ECHO, ICANON, tcsetattr};
fn main() {
let stdin = 0; // couldn't get std::os::unix::io::FromRawFd to work
// on /dev/stdin or /dev/tty
let termios = Termios::from_fd(stdin).unwrap();
let mut new_termios = termios.clone(); // make a mutable copy of termios
// that we will modify
new_termios.c_lflag &= !(ICANON | ECHO); // no echo and canonical mode
tcsetattr(stdin, TCSANOW, &mut new_termios).unwrap();
let stdout = io::stdout();
let mut reader = io::stdin();
let mut buffer = [0;1]; // read exactly one byte
print!("Hit a key! ");
stdout.lock().flush().unwrap();
reader.read_exact(&mut buffer).unwrap();
println!("You have hit: {:?}", buffer);
tcsetattr(stdin, TCSANOW, & termios).unwrap(); // reset the stdin to
// original termios data
}
One advantage of reading a single byte is capturing arrow keys, ctrl etc. Extended F-keys are not captured (although ncurses can capture these).
This solution is intended for UNIX-like platforms. I have no experience with Windows, but according to this forum perhaps something similar can be achieved using SetConsoleMode in Windows.
Use one of the 'ncurses' libraries now available, for instance this one.
Add the dependency in Cargo
[dependencies]
ncurses = "5.86.0"
and include in main.rs:
extern crate ncurses;
use ncurses::*; // watch for globs
Follow the examples in the library to initialize ncurses and wait for single character input like this:
initscr();
/* Print to the back buffer. */
printw("Hello, world!");
/* Update the screen. */
refresh();
/* Wait for a key press. */
getch();
/* Terminate ncurses. */
endwin();
You can also use termion, but you will have to enable the raw TTY mode which changes the behavior of stdout as well. See the example below (tested with Rust 1.34.0). Note that internally, it also wraps the termios UNIX API.
Cargo.toml
[dependencies]
termion = "1.5.2"
main.rs
use std::io;
use std::io::Write;
use std::thread;
use std::time;
use termion;
use termion::input::TermRead;
use termion::raw::IntoRawMode;
fn main() {
// Set terminal to raw mode to allow reading stdin one key at a time
let mut stdout = io::stdout().into_raw_mode().unwrap();
// Use asynchronous stdin
let mut stdin = termion::async_stdin().keys();
loop {
// Read input (if any)
let input = stdin.next();
// If a key was pressed
if let Some(Ok(key)) = input {
match key {
// Exit if 'q' is pressed
termion::event::Key::Char('q') => break,
// Else print the pressed key
_ => {
write!(
stdout,
"{}{}Key pressed: {:?}",
termion::clear::All,
termion::cursor::Goto(1, 1),
key
)
.unwrap();
stdout.lock().flush().unwrap();
}
}
}
thread::sleep(time::Duration::from_millis(50));
}
}
Here's a lightweight solution only using the libc crate based some code from the console crate:
fn setup_raw_terminal() -> io::Result<()> {
unsafe {
let tty;
let fd = if libc::isatty(libc::STDIN_FILENO) == 1 {
libc::STDIN_FILENO
} else {
tty = fs::File::open("/dev/tty")?;
tty.as_raw_fd()
};
let mut ptr = core::mem::MaybeUninit::uninit();
if libc::tcgetattr(fd, ptr.as_mut_ptr()) == 0 {
let mut termios = ptr.assume_init();
let c_oflag = termios.c_oflag;
libc::cfmakeraw(&mut termios);
termios.c_oflag = c_oflag;
if libc::tcsetattr(fd, libc::TCSADRAIN, &termios) == 0 {
return Ok(());
}
}
}
Err(io::Error::last_os_error())
}
It needs to be called before reading stdin:
let mut buf = [0u8; 1024];
let mut stdin = io::stdin();
setup_raw_terminal()?;
loop {
let size = stdin.read(&mut buf)?;
let data = &buf[0..size];
println!("stdin data: {}", data);
}

How do I enable editable user input for a Rust terminal application?

I am writing a minimal Lisp with a classic terminal-based REPL environment project in Rust.
How do I read in user input from the arrow keys, allowing them to move back and forth on their current line of input at least before they have pressed enter? Ideally, I will be able to extend functionality to include moving "back" to retrieve old inputs like you would in any terminal or any REPL. Here's an image of the behavior for clarity:
I have played around with the standard library's io module and the termion crate but have not figured this functionality out.
Here is my current working code. It effectively takes input and immediately prints it back to the user in addition to quitting as expected with quit().
use std::io::prelude::*;
use std::io;
fn main() {
println!("Rispy Version 0.0.1");
println!("Enter `quit()` to Exit");
let mut input: String;
// continuous input til ctrl-c or quit()
loop {
print!("rispy>> ");
io::stdout().flush().unwrap();
input = String::new();
io::stdin().read_line(&mut input)
.expect("Error reading line");
print!("input: {}", input);
match input.as_ref() {
"quit()\n" => {
println!("\nGoodbye");
break;
},
_ => continue,
}
}
}
It's funny how some basic/fundamental crates are not suggested elsewhere more readily for problems like this but thankfully #kazemakase answered the question by suggesting a crate I had not found up to this point: rustyline
A slight edit to the example code on the readme yields the results I want, with history, the ability to navigate left/right with the arrow keys, and even the use of key strokes like ctrl-d, ctrl-c, home, etcetera. Here it is to couple with the question as asked:
extern crate rustyline;
use rustyline::Editor;
use rustyline::error::ReadlineError;
fn main() {
println!("Rispy Version 0.0.1");
println!("Enter `quit()` to Exit");
let mut reader = Editor::<()>::new();
if let Err(_) = reader.load_history("rispy_history.txt") {
println!("No previous history.");
}
// continuous input
loop {
let readline = reader.readline("rispy>> ");
match readline {
Ok(line) => {
reader.add_history_entry(&line);
println!("input: {}", line);
},
Err(ReadlineError::Interrupted) => {
println!("CTRL-C");
println!("Goodbye");
break
}
Err(ReadlineError::Eof) => {
println!("CTRL-D");
println!("Goodbye");
break
},
Err(err) => {
println!("Error: {:?}", err);
break
}
}
}
reader.save_history("rispy_history.txt").unwrap();
}
As an out-of-the-box answer, I occasionally use rlwrap, a small wrapper around any command line program that adds basic readline capabilities.
Running rlwrap cargo run, your original program now has editing like you asked, as well as command history and history searching, and probably a lot of other things.

Pause process to allow time for debugger to attach

I'd like to attach gdb to a process where I can't easily control the startup of the process because it is run from inetd and where the process completes too fast to be able to attach to it once it starts.
What I'd like to do is insert a bit of code at the particular point that I want to start debugging. That code would ideally wait for the debugger to attach and then continue. I've tried with a sleep but it is then tricky to choose a delay long enough that I have time to catch it but short enough not to be a nuisance waiting for it to elapse after gdb is attached.
Is there any better choices of code to insert or to call for this purpose?
What I'd like to do is insert a bit of code at the particular point that I want to start debugging.
I usually do it like this:
volatile int done = 0;
while (!done) sleep(1);
Attach GDB (you'll be inside sleep). Do finish, then set var done = 1, and enjoy the rest of your debugging session ;-)
using namespace std;
using namespace std::this_thread;
using namespace chrono;
void waitForDebugger()
{
steady_clock::time_point tp1, tp2, tp3;
microseconds d1 = microseconds(10000);
microseconds d2 = microseconds(20000);
bool looped = false;
while (true)
{
sleep_for(d1);
tp1 = steady_clock::now();
if (looped && duration_cast<microseconds>(tp1 - tp3) > d2)
{
break;
}
sleep_for(d1);
tp2 = steady_clock::now();
if (looped && duration_cast<microseconds>(tp2 - tp1) > d2)
{
break;
}
sleep_for(d1);
tp3 = steady_clock::now();
if (looped && duration_cast<microseconds>(tp3 - tp2) > d2)
{
break;
}
looped = true;
}
}

Help needed with F_NOCACHE in mac

I am Srinivasa Raghavan and new to this group.
I am facing problem with non caching the file.
the code looks like the below:
main()
{
int fd;
char buf[512] = {'\0'};
fd = fopen("Sample.bin",O_RDONLY);
fcntl(fd, F_NOCACHE, 1);
fcntl(fd, F_RDAHEAD, 1);
read(fd, buf, sizeof(buf));
close(fd);
if(buf[0] == 'x' )
print("non-cached\n");
else
printf("cached\n")
}
the problem was, the F_NOCACHE doesn't not work properly, and all the time I get the message cached only. The firware will always update the value 'x' in sample.bin.
the above code works if I put the entire stuff (open, fcntl, read and close) in an indefinite loop (take long time to come out) like the below.
main()
{
while(1)
{
open...
fcntl(.., F_NOCACHE)
read(....
close..
if(buf[0] == 'x')
break;
}
}
I am really stuck with this for a week time, I want to know the exact behaviour of F_NOCACHE, and any information will be highly appreciated.
Thanks in advance,
Srinivasa Raghavan
That's not what it's for. F_NOCACHE tells the system that you don't expect to read that data* off the disk again any time soon, so it shouldn't bother caching it.
*Yes, "data" is plural, I know.

Resources