How to use linux/input.h input_event struct in rust

Hello, I wanted to adapt this C code into Rust code but including Linux C code header linux/input.h input_event struct

#include <stdio.h>
#include <fcntl.h>
#include <stdlib.h>
#include <linux/input.h>
#include <unistd.h>

int main(int argc, char *argv[]) {
        if (argc != 2) {
                printf("Usage: %s <event-file-path>\n", argv[0]);
                exit(-1);
        }

        printf("Keylogger active ...\n");

        int fd = open(argv[1], O_RDONLY, 0);
        printf("Opened file descriptor: %d\n", fd);

        struct input_event ie;

        while (0) {
                read(fd, &ie, sizeof(ie));

                printf("Key pressed: %d\n", ie.code);

                if (ie.code == 16) {
                        printf("A key pressed\n");
                }

                if (ie.code == 17) {
                        printf("ZZZZZZZZZZZZZZ\n");
                }
        }
}

how can I use this struct into Rust code without external library?

(My Rust code is here):

use std::{env, fs::File, io, os::fd::AsRawFd, process::exit};

fn main() -> io::Result<()> {
    let args: Vec<String> = env::args().collect();

    let Some(_unused) = std::env::args().nth(1) else { // Solution found on: https://users.rust-lang.org/t/how-to-stop-thread-main-panicked-at-if-no-value-is-give-a-start/117632
        eprint!("Usage: {} <event-file-path>\n", &args[0]);
        exit(-1);
    };

    print!("Loggy alive!\n");

    let fd = File::open(&args[1])?; // Equivalent of "int fd"
    print!("Opened file descriptor: {}\n", fd.as_raw_fd()); // play if the file was successfully opened

    #[repr(C)]
    struct input_event {

    };

    loop {
        // print!("Key pressed: {}\n", ie.code)
    }
}

Thank you in advance for your help :slight_smile:

Use libc crate. input_event

1 Like

Yeah but the thing is that I'd like to do it without relying on any library, is not any other method to do it?

You could write your own definition of the struct by e.g. copy-pasting from libc. But that will be less portable and you'll have to take care to e.g. get the repr correct for your platform.

And you'll be depending on std anyway, and libc is an official crate, so I also recommend just using libc.

I also suggest wrapping up the FFI types in a new type. Use that to put a safe API around any required unsafe. Here's one way using std to do the reading instead of libc.

    #[derive(Copy, Clone, Debug)]
    // The `transparent` is important
    #[repr(transparent)]
    pub struct InputEvent(input_event);

    impl Default for InputEvent { ... }

    impl InputEvent {
        pub fn read_from<R: Read>(mut reader: R) -> Result<Self, io::Error> {
            let mut buffer = [0; size_of::<Self>()];
            reader.read_exact(&mut buffer)?;
            // SAFETY: We consist of all plain integer data or padding, so any
            // bit pattern of correct size and alignment is a valid value.
            let this = unsafe { std::mem::transmute::<[u8; _], Self>(buffer) };
            Ok(this)
        }

        pub fn code(&self) -> c_ushort {
            self.0.code
        }
        
        // Todo: Add more methods as needed...
    }
    let file = File::open(&path)?;
    println!("Opened file descriptor: {}", file.as_raw_fd());

    loop {
        let ie = InputEvent::read_from(&file)?;
        let code = ie.code();
1 Like

I looked to the libc crate and it seems to be what I need (because I searched for the most "official" method) but can you help me use the libc method please, because I don't know to use the struct and all the functions to make it work like in the C code?

Here's what I came up with on the playground. (Untested beyond that.)

Thank you so much for your solution, it helped me a lot to get unstucked!