The program below demonstrates the issue. When you build it with clang main.c -o main, run, and press a few keys, you'll see that the program always stops at "Calling poll()..." on Linux. Now if you do the same on an up-to-date WSL (Windows Subsystem for Linux), you will see that it stops at "Calling read()...". In other words, on Linux it's poll() that blocks, on WSL it's read().
The program basically sets the input to non-canonical mode, with VTIME and VMIN also set 0. If I'm reading the two references below right
https://www.gnu.org/software/libc/manual/html_node/Canonical-or-Not.html
https://www.gnu.org/software/libc/manual/html_node/Noncanonical-Input.html
then I think in this case read() should never block and I should never see the program wait after printing "Calling read()...". Yet on WSL this is the case.
Now if you pass -DNONBLOCK when compiling and run, both on Linux and WSL the program always blocks after printing "Calling poll()...", which is the expected behavior.
I'm wondering if this is a bug on WSL, or I'm misreading the non-canonical mode documentation.
Program:
// clang main.c -o main
#include <errno.h>
#include <fcntl.h>
#include <poll.h>
#include <stdbool.h>
#include <stdint.h>
#include <stdio.h>
#include <string.h>
#include <termios.h>
#include <unistd.h>
void main_loop();
bool read_until_empty();
int main()
{
#if defined(NONBLOCK)
int old_stdin_flags = fcntl(STDIN_FILENO, F_GETFL);
fcntl(STDIN_FILENO, F_SETFL, old_stdin_flags | O_NONBLOCK);
printf("Enabled non-blocking mode\n");
#endif
bool fail = false;
int tty = open("/dev/tty", O_RDWR);
if (tty == -1) {
printf("Unable to open /dev/tty\n");
fail = true;
goto cleanup;
}
struct termios old_termios;
if (tcgetattr(STDIN_FILENO, &old_termios) == -1) {
printf("tcgetattr failed\n");
fail = true;
goto cleanup;
}
struct termios new_termios = old_termios;
cfmakeraw(&new_termios);
// These really need to be after cfmakeraw() !!!!
new_termios.c_cc[VMIN] = 0;
new_termios.c_cc[VTIME] = 0;
if (tcsetattr(STDIN_FILENO, TCSAFLUSH, &new_termios) == -1) {
printf("tcsetattr failed\n");
fail = true;
goto cleanup;
}
printf("Type 'q' to quit.\n");
main_loop();
cleanup:
tcsetattr(STDIN_FILENO, TCSAFLUSH, &old_termios);
#if defined(NONBLOCK)
fcntl(STDIN_FILENO, F_SETFL, old_stdin_flags);
printf("Restored stdin flags\n");
#endif
if (fail) {
return 1;
}
}
void main_loop()
{
printf("main_loop\n");
struct pollfd fds[1] = { { .fd = STDIN_FILENO, .events = POLLIN } };
for (;;)
{
printf("Calling poll()...\n");
int poll_ret = poll(fds, 1, -1);
if (poll_ret > 0)
{
printf("stdin ready for reading\n");
if (read_until_empty())
{
return;
}
}
}
}
bool read_until_empty()
{
uint8_t buf[10000];
for (;;)
{
printf("Calling read()...\n");
ssize_t n_read = read(STDIN_FILENO, buf, 10000);
if (n_read == -1)
{
if (errno == EAGAIN || errno == EWOULDBLOCK)
{
printf("EAGAIN or EWOULDBLOCK\n");
return false;
}
else
{
printf("Error %d: %s\n", errno, strerror(errno));
return false;
}
}
else if (n_read == 0)
{
printf("stdin is empty\n");
return false;
}
else
{
printf("Read %zd bytes\n", n_read);
if (buf[0] == 3 || buf[0] == 113)
{
return true;
}
}
}
}
Shortly after asking this I found the bug report in WSL issue tracker: https://github.com/microsoft/WSL/issues/3507
So it seems like this is a bug in WSL.
Related
The following code, when ran, does not respond to terminal input. I have ran this under Debian 10.5.0 and CentOS 7 with the same results. What am I missing?
#include <sys/epoll.h>
#include <stdio.h>
#include <unistd.h>
int main(void) {
char buffer[4096];
int fd = epoll_create1(EPOLL_CLOEXEC);
struct epoll_event event;
int ctl_ret;
event.events = EPOLLIN;
event.data.fd = STDIN_FILENO;
ctl_ret = epoll_ctl(fd, EPOLL_CTL_ADD, STDIN_FILENO, &event);
if (ctl_ret) {
perror("epoll_ctl");
return 1;
}
int nr_event;
for (;;) {
fprintf(stderr, "Starting epoll_wait\n");
nr_event = epoll_wait(fd, &event, 1, -1);
if (nr_event < 0) {
perror("epoll_wait");
return 1;
}
fprintf(stderr, "Reading: %d\n", event.data.fd);
printf("%ld\n", read(0, buffer, sizeof(buffer)));
}
}
As Basile Starynkevitch alluded to, I needed to update the terminal configuration via termios(3).
I have provided the revised code below for reference. For the changes required, please see the code between the // TERMINAL SETUP and // EPOLL SETUP comments.
#include <errno.h>
#include <linux/input.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <sys/epoll.h>
#include <termios.h>
#include <unistd.h>
int main(void) {
// TERMINAL SETUP
struct termios tty_cfg, tty_cfg_cache;
// Check that fd is a TTY
if (!isatty(STDIN_FILENO)) {
fprintf(stderr, "Standard input is not a terminal.\n");
return EXIT_FAILURE;
}
// Save old terminal configuration
if (tcgetattr(STDIN_FILENO, &tty_cfg_cache) == -1 ||
tcgetattr(STDIN_FILENO, &tty_cfg) == -1) {
fprintf(stderr, "Cannot get terminal settings: %s.\n", strerror(errno));
return EXIT_FAILURE;
}
// Set new terminal configuration
tty_cfg.c_iflag &= ~(IGNBRK | BRKINT | PARMRK);
tty_cfg.c_lflag &= ~(ICANON | ISIG | ECHO | IEXTEN | TOSTOP);
tty_cfg.c_cc[VMIN] = 0;
tty_cfg.c_cc[VTIME] = 0;
tty_cfg.c_cc[VSTART] = 0;
tty_cfg.c_cc[VSTOP] = 0;
if (tcsetattr(STDIN_FILENO, TCSANOW, &tty_cfg) == -1) {
const int saved_errno = errno;
tcsetattr(STDIN_FILENO, TCSANOW, &tty_cfg_cache);
fprintf(stderr, "Cannot set terminal settings: %s.\n", strerror(saved_errno));
return EXIT_FAILURE;
}
// EPOLL SETUP
// Create epoll
int epfd = epoll_create1(EPOLL_CLOEXEC);
// Add stdin control interface to epoll
struct epoll_event event;
int ctl_ret;
event.events = EPOLLIN;
event.data.fd = STDIN_FILENO;
ctl_ret = epoll_ctl(epfd, EPOLL_CTL_ADD, STDIN_FILENO, &event);
if (ctl_ret) {
perror("epoll_ctl");
return 1;
}
// LOOP AND MONITOR EPOLL
int nr_event;
unsigned char keys[16];
ssize_t n;
size_t i, done;
done = 0;
while (!done) {
// Start epoll_wait
nr_event = epoll_wait(epfd, &event, 1, -1);
if (nr_event < 0) {
perror("epoll_wait");
return 1;
}
// Read STDIN_FILENO
n = read(STDIN_FILENO, keys, sizeof keys);
if (n > 0) {
for (i = 0; i < n; i++) {
// Quit if 'q' or 'Q' is pressed
if (keys[i] == 'q' || keys[i] == 'Q')
done = 1;
if (keys[i] >= 32 && keys[i] <= 126)
printf("Key '%c' = 0x%02x = %u pressed\n", keys[i], keys[i], keys[i]);
else
if (keys[i])
printf("Key '\\%03o' = 0x%02x = %u pressed\n", keys[i], keys[i], keys[i]);
else
printf("NUL key (0) pressed\n");
}
fflush(stdout);
}
}
// RESTORE TERMINAL SETTINGS
tcsetattr(STDIN_FILENO, TCSAFLUSH, &tty_cfg_cache);
return EXIT_SUCCESS;
}
I have an event handling code that reads Linux's /dev/input/ for my touchpad and prints result on the basis of which button is pressed/released.
Although. as of now my code is waiting on a button press while running on terminal. My next step is to run this event handling thread along with another thread (not event based). If I continue handling event by reading input at terminal, I will not be able to execute other threads as a part of my main() as main() keeps on waiting for the button press:
int main(int argc, char** argv)
{
*Mouse event handling code here*
return 0;
}
Is there a different approach like reading interrupts instead? Or can I still take this approach and make amends in my code to make this work as a part of a thread (like can I make my thread to wait on these inputs as arguments)?
If you make the event device descriptors nonblocking (by opening them with the O_NONBLOCK flag), you can very easily use `poll() to wait until one of them has events you can read.
Consider the following example program, example.c:
#define _POSIX_C_SOURCE 200809L
#define _GNU_SOURCE
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/ioctl.h>
#include <fcntl.h>
#include <linux/input.h>
#include <termios.h>
#include <poll.h>
#include <string.h>
#include <stdio.h>
#include <errno.h>
/* Maximum number of input sources, including the terminal. */
#ifndef MAX_INPUTS
#define MAX_INPUTS 32
#endif
/* Maximum wait for events, in milliseconds (1000 ms = 1 second). */
#ifndef INTERVAL_MS
#define INTERVAL_MS 100
#endif
int main(int argc, char *argv[])
{
unsigned char keys[16];
struct input_event event;
struct termios config, oldconfig;
struct pollfd src[MAX_INPUTS];
size_t srcs, i, done;
ssize_t n;
int arg, nsrcs;
if (!isatty(STDIN_FILENO)) {
fprintf(stderr, "Standard input is not a terminal.\n");
return EXIT_FAILURE;
}
/* Save old terminal configuration. */
if (tcgetattr(STDIN_FILENO, &oldconfig) == -1 ||
tcgetattr(STDIN_FILENO, &config) == -1) {
fprintf(stderr, "Cannot get terminal settings: %s.\n", strerror(errno));
return EXIT_FAILURE;
}
/* Set new terminal configuration. */
config.c_iflag &= ~(IGNBRK | BRKINT | PARMRK);
config.c_lflag &= ~(ICANON | ISIG | ECHO | IEXTEN | TOSTOP);
config.c_cc[VMIN] = 0;
config.c_cc[VTIME] = 0;
config.c_cc[VSTART] = 0;
config.c_cc[VSTOP] = 0;
if (tcsetattr(STDIN_FILENO, TCSANOW, &config) == -1) {
const int saved_errno = errno;
tcsetattr(STDIN_FILENO, TCSANOW, &oldconfig);
fprintf(stderr, "Cannot set terminal settings: %s.\n", strerror(saved_errno));
return EXIT_FAILURE;
}
/* The very first input source is the terminal. */
src[0].fd = STDIN_FILENO;
src[0].events = POLLIN;
src[0].revents = 0;
srcs = 1;
/* Add input devices from command line. */
for (arg = 1; arg < argc; arg++) {
int fd;
fd = open(argv[arg], O_RDONLY | O_NOCTTY | O_NONBLOCK);
if (fd == -1) {
fprintf(stderr, "Skipping input device %s: %s.\n", argv[arg], strerror(errno));
continue;
}
if (srcs >= MAX_INPUTS) {
fprintf(stderr, "Too many event sources.\n");
return EXIT_FAILURE;
}
/* Optional: Grab input device, so only we receive its events. */
ioctl(fd, EVIOCGRAB, 1);
src[srcs].fd = fd;
src[srcs].events = POLLIN;
src[srcs].revents = 0;
srcs++;
}
printf("Ready. Press Q to exit.\n");
fflush(stdout);
done = 0;
while (!done) {
nsrcs = poll(src, srcs, INTERVAL_MS);
if (nsrcs == -1) {
if (errno == EINTR)
continue;
fprintf(stderr, "poll(): %s.\n", strerror(errno));
break;
}
/* Terminal is not an input source. */
if (src[0].revents & POLLIN) {
n = read(src[0].fd, keys, sizeof keys);
if (n > 0) {
for (i = 0; i < n; i++) {
if (keys[i] == 'q' || keys[i] == 'Q')
done = 1;
if (keys[i] >= 32 && keys[i] <= 126)
printf("Key '%c' = 0x%02x = %u pressed\n", keys[i], keys[i], keys[i]);
else
if (keys[i])
printf("Key '\\%03o' = 0x%02x = %u pressed\n", keys[i], keys[i], keys[i]);
else
printf("NUL key (0) pressed\n");
}
fflush(stdout);
}
src[0].revents = 0;
}
/* Check the other input sources. */
for (i = 1; i < srcs; i++) {
if (src[i].revents & POLLIN) {
while (1) {
n = read(src[i].fd, &event, sizeof event);
if (n != sizeof event)
break;
if (event.type == EV_KEY && event.code == BTN_LEFT) {
if (event.value > 0)
printf("Left mouse button pressed\n");
else
printf("Left mouse button released\n");
}
if (event.type == EV_KEY && event.code == BTN_RIGHT) {
if (event.value > 0)
printf("Right mouse button pressed\n");
else
printf("Right mouse button released\n");
}
}
fflush(stdout);
}
src[i].revents = 0;
}
}
/* Close input devices. */
for (i = 1; i < srcs; i++)
close(src[i].fd);
/* Restore terminal settings. */
tcsetattr(src[0].fd, TCSAFLUSH, &oldconfig);
printf("All done.\n");
return EXIT_SUCCESS;
}
Compile it using e.g.
gcc -Wall -O2 example.c -o example
and run it using e.g.
sudo ./example /dev/input/event5
where /dev/input/event5 is a mouse event device. Note that you can read /sys/class/input/event5/device/name to find out the name of the device (as far as the kernel knows it; these are the same names evtest shows when run as root).
If you are not sure, you can always run
for N in /sys/class/input/event*/device/name ; do
DEV="${N%%/device/name}" ; DEV="/dev/${DEV##/sys/class/}" ;
NAME="$(cat "$N" 2>/dev/null)" ;
printf "%s: %s\n" "$DEV" "$NAME" ;
done
in a Bash or Dash or a POSIX shell, to see what event devices you can try.
The example program above must be run from a terminal or console, because it also takes input from the terminal. It sets the terminal into nonblocking non-canonical mode, where it can receive individual keypresses. Do note that some keypresses, like cursor and function keys, are actually several characters long, beginning with an ESC (\033).
It is also common to split that input event loop into a separate thread. It is just a dozen or so lines more, but the "problem" then becomes how the separate thread informs the main (or other) threads that new input events/commands have arrived. The non-blocking poll() approach above is usually easier to implement in a very robust, straightforward manner.
My simple poll. the event routine attempt to get data from the two non blocking fds, one for mouse and one for keyboard. The event routine returns -1 or device busy when not ready, anything error below that is trapped by event. The if statement here tries fmd, mouse, first, then fkd next. A return less than one or zero means data not ready, the thread sleeps.
if( ( ( imd = event(fmd,&ie) ) <=0)&& ( ( ikd = event(fkd,&ie)) <= 0))
{
usleep(TIMEOUT);
continue;
}
I've searched for quite some time now for a solution to my problem.
I'd like to read RFID tags on my Raspberry, but I want to do it in C-code, as the rest of my project is written in C.
I have a few questions to the following C-code I found here on stackoverflow.com:
What .h includes do I need for the code below?
Where is the interface setup, that I'm using? (/dev/ttyAMA0)
Please help me to get this code snippet working right, as it's been holding me up for a few days already.
char read_rfid(char* rfid_num) {
fd_set input_fdset;
ssize_t length;
int done;
for(done=0; done < 14; ) {
FD_ZERO(&input_fdset);
FD_SET(fd,&input_fdset);
if(select(fd+1 ,&input_fdset, NULL,NULL,NULL) == -1) {
if (errno == EAGAIN) continue;
perror("Terminal select() failed");
return -1;
}
if(FD_ISSET(fd,&input_fdset)) {
if((length = read(fd,rfid_num+done,14-done)) == -1) {
if (errno == EAGAIN) continue;
perror("Terminal: read() failed");
return -1;
}
write(STDOUT_FILENO,rfid_num+done,length);
done += length;
}
}
return 0;
}
int setupRS232()
{
struct termios term_attr;
if((fd = open(RFID,O_RDWR)) == -1)
{
perror("Can't open Device");
return(1);
}
if(tcgetattr(fd,&term_attr) != 0)
{
perror("terminal: tcgetattr() failed");
return(1);
}
term_attr.c_cflag = BAUD|CS8|CRTSCTS|CLOCAL|CREAD;
term_attr.c_iflag = 0;
term_attr.c_oflag = 0;
term_attr.c_lflag = 0;
if(tcsetattr(fd,TCSAFLUSH,&term_attr) != 0)
{
perror("terminal: tcsetattr() failed");
return(1);
}
}
int main(int argc, char** argv)
{
char rfid_num[14];
int i;
if(setupRS232() == 1)
return(1);
puts("Waiting for transponder...");
read_rfid(rfid_num);
for(i=0;i<20;i++)
{
printf("%x\n",rfid_num[i]);
}
}
#include <unistd.h> // for read & write functions
#include <sys/select.h> // fd_set functions
#include <stdio.h> // for perror & printf family
#include <sys/types.h> // for open related function
#include <sys/stat.h> // for open related function
#include <fcntl.h> // for open related function
#include <termios.h> // for terminal functions
#include <errno.h> // for error code
#define RFID "path_to_rfid" // FIXME <- you should set this properly
char read_rfid(char* rfid_num, int fd)
{
fd_set input_fdset;
ssize_t length;
int done;
for(done=0; done < 14; ) {
FD_ZERO(&input_fdset);
FD_SET(fd,&input_fdset);
if(select(fd+1 ,&input_fdset, NULL,NULL,NULL) == -1) {
perror("Terminal select() failed");
return -1;
}
if(FD_ISSET(fd,&input_fdset)) {
if((length = read(fd,rfid_num+done,14-done)) == -1) {
perror("Terminal: read() failed");
return -1;
}
write(STDOUT_FILENO,rfid_num+done,length);
done += length;
}
}
return 0;
}
int setupRS232()
{
struct termios term_attr;
int fd = 0;
if((fd = open(RFID,O_RDWR)) == -1) {
perror("Can't open Device");
return(-1);
}
if(tcgetattr(fd,&term_attr) != 0)
{
perror("terminal: tcgetattr() failed");
close(fd);
return(-1);
}
term_attr.c_cflag = CBAUD|CS8|CRTSCTS|CLOCAL|CREAD;
term_attr.c_iflag = 0;
term_attr.c_oflag = 0;
term_attr.c_lflag = 0;
if(tcsetattr(fd,TCSAFLUSH,&term_attr) != 0) {
perror("terminal: tcsetattr() failed");
close(fd);
return(-1);
}
return (fd);
}
int main(int argc, char** argv)
{
char rfid_num[14];
int i;
int fd;
if((fd = setupRS232()) == -1) {
return(-1);
}
puts("Waiting for transponder...");
read_rfid(rfid_num, fd);
for(i=0;i<20;i++) {
printf("%x\n",rfid_num[i]);
}
return 0;
}
Now your code is ready to be compiled, at least in my machine I could compile with no errors.
I am actually trying to write a small program to catch global keyboard inputs from specific USB keyboards under linux.
I am testing with this piece of code :
#include <stdlib.h>
#include <unistd.h>
#include <fcntl.h>
#include <errno.h>
#include <linux/input.h>
#include <string.h>
#include <stdio.h>
static const char *const evval[3] = {
"RELEASED",
"PRESSED ",
"REPEATED"
};
int main(void)
{
const char *dev = "/dev/input/event2";
struct input_event ev;
ssize_t n;
int fd;
char name[256]= "Unknown";
// int codes[2];
// codes[0] = 58; /* M keycap */
// codes[1] = 49; /* assign to N */
fd = open(dev, O_RDONLY);
if (fd == -1) {
fprintf(stderr, "Cannot open %s: %s.\n", dev, strerror(errno));
return EXIT_FAILURE;
}
if(ioctl(fd, EVIOCGNAME(sizeof(name)), name) > 0)
{
printf("The device on %s says its name is '%s'\n", dev, name);
}
/*
int err = ioctl(fd, EVIOCSKEYCODE, codes);
if (err) {
perror("evdev ioctl");
}*/
while (1) {
n = read(fd, &ev, sizeof ev);
if (n == (ssize_t)-1) {
if (errno == EINTR)
continue;
else
break;
} else
if (n != sizeof ev) {
errno = EIO;
break;
}
if (ev.type == EV_KEY && ev.value >= 0 && ev.value <= 2)
printf("%s 0x%04x (%d)\n", evval[ev.value], (int)ev.code, (int)ev.code);
}
fflush(stdout);
fprintf(stderr, "%s.\n", strerror(errno));
return EXIT_FAILURE;
}
Ths point is that I don't know how to change some input key by other. I tried by calling write() on currently red event by changing the event code, sent key was still previous one, and I tried to used ioctl with EVIOCSKEYCODE, but the call failed with an "invalid argument" error (and I'm not sure to call it correctly).
How can I change outputed key correctly ?
Use the EVIOCGRAB ioctl to grab the input device, so that by reading the events you consume them. Normally (not-grabbed) the events are not consumed when you read them. The ioctl takes an additional parameter, (int)1 for grabbing, (int)0 for releasing.
To re-inject any events, just write them to the uinput device. See eg. a mouse example here. (The event structures are the same type, you only need to write a struct uinput_user_dev structure to the uinput device first, to describe your new input device (which provides the mapped events).)
In other words, you don't remap: you consume and forward.
I am in the middle of writing a user level C program, that reads a SPI device and translate the results to a keyboard event.
I am now trying to pass emulate key event to /dev/uinput. A weird thing is happening, file operation is failing.
my_kbd.c
#include <stdio.h>
#include <stdlib.h>
#include <ctype.h>
#include <string.h>
#include <signal.h>
#include <unistd.h>
#include <errno.h>
#include <linux/input.h>
#include <linux/uinput.h>
#include <sys/stat.h>
#include <sys/file.h>
#include <fcntl.h>
#define PROG_NAME "c2h2_spi_kbd"
int fire_key(__u16);
int setup_uinputfd(const char *);
int close_uinputfd();
void write_uinput(__u16, __u16, __s32);
/*fd for uinput, we do need kernel to support uinput */
static int uinputfd = -1;
int main(int argc, char **argv){
puts("Welcome to use SPI keyboard program v0.1!");
uinputfd = setup_uinputfd(PROG_NAME);
if(uinputfd != -1){
fire_key(KEY_A);
}else{
puts("where is uinput ? do you have permission?\n");
}
close_uinputfd();
exit(0);
}
int fire_key(__u16 key){
write_uinput(EV_KEY, key, 1);
return 0;
}
void write_uinput(__u16 type, __u16 code, __s32 value){
struct input_event ie;
sleep(1);
memset(&ie, 0, sizeof(ie));
ie.type = type;
ie.code = code;
ie.value = value;
if(write(uinputfd, &ie, sizeof(ie)) != sizeof(ie)) puts("ERR1");
memset(&ie, 0, sizeof(ie));
ie.type = EV_SYN;
ie.code = SYN_REPORT;
ie.value = 0;
if(write(uinputfd, &ie, sizeof(ie)) != sizeof(ie)) puts("ERR2");
}
int close_uinputfd(){
close(uinputfd);
return 0;
}
int setup_uinputfd(const char *name){
int fd;
int key;
struct uinput_user_dev dev;
fd = open("/dev/input/uinput", O_RDWR);
if (fd == -1) {
fd = open("/dev/uinput", O_RDWR);
if (fd == -1) {
fd = open("/dev/misc/uinput", O_RDWR);
if (fd == -1) {
fprintf(stderr, "could not open %s\n", "uinput");
perror(NULL);
return -1;
}
}
}
memset(&dev, 0, sizeof(dev));
strncpy(dev.name, name, sizeof(dev.name));
dev.name[sizeof(dev.name) - 1] = 0;
if (write(fd, &dev, sizeof(dev)) != sizeof(dev) ||
ioctl(fd, UI_SET_EVBIT, EV_KEY) != 0
|| ioctl(fd, UI_SET_EVBIT, EV_REP) != 0) {
goto setup_error;
}
for (key = KEY_RESERVED; key <= KEY_UNKNOWN; key++) {
if (ioctl(fd, UI_SET_KEYBIT, key) != 0) {
goto setup_error;
}
}
if (ioctl(fd, UI_DEV_CREATE) != 0) {
goto setup_error;
}
return fd;
setup_error:
fprintf(stderr, "could not setup %s\n", "uinput");
perror(NULL);
close(fd);
return -1;
}
gcc -Wall my_kbd.c && sudo ./a.out
without sleep(1); I never have uinput event happen. with sleep(1);, this works perfect. (emulate keys output to focused program)
I do suspect this could be a cache problem, but fsync(fd); did not help. Please help.