Related
I have developped a C program for my embedded Board. This program make the green LED lights on when I push and release the BUTTON.
The green LED is defined under "/sys/class/leds" and the BUTTON is under "/dev/input/event0".
This is the code :
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <string.h>
#include <sys/select.h>
#include <sys/time.h>
#include <errno.h>
#include <linux/input.h>
#define BTN_FILE_PATH "/dev/input/event0"
#define LED_PATH "/sys/class/leds"
#define green "green"
void change_led_state(char *led_path, int led_value)
{
char lpath[64];
FILE *led_fd;
strncpy(lpath, led_path, sizeof(lpath) - 1);
lpath[sizeof(lpath) - 1] = '\0';
led_fd = fopen(lpath, "w");
if (led_fd == NULL) {
fprintf(stderr, "simplekey: unable to access led\n");
return;
}
fprintf(led_fd, "%d\n", led_value);
fclose(led_fd);
}
void reset_leds(void)
{
change_led_state(LED_PATH "/" green "/brightness", 0);
}
int configure_leds(void)
{
FILE *l_fd;
FILE *r_fd;
char *none_str = "none";
/* Configure leds for hand control */
r_fd = fopen(LED_PATH "/" green "/trigger", "w");
fprintf(r_fd, "%s\n", none_str);
fclose(r_fd);
/* Switch off leds */
reset_leds();
return 0;
}
void eval_keycode(int code)
{
static int green_state = 0;
switch (code) {
case 260:
printf("BTN pressed\n");
/* figure out green state */
green_state = green_state ? 0 : 1;
change_led_state(LED_PATH "/" green "/brightness", green_state);
break;
}
}
int main(void)
{
int file;
/* how many bytes were read */
size_t rb;
int ret;
int yalv;
/* the events (up to 64 at once) */
struct input_event ev[64];
char *str = BTN_FILE_PATH;
printf("Starting simplekey app\n");
ret = configure_leds();
if (ret < 0)
exit(1);
printf("File Path: %s\n", str);
if((file = open(str, O_RDONLY)) < 0) {
perror("simplekey: File can not open");
exit(1);
}
for (;;) {
/* Blocking read */
rb= read(file, &ev, sizeof(ev));
for (yalv = 0;
yalv < (int) (rb / sizeof(struct input_event));
yalv++) {
if (ev[yalv].type == EV_KEY) {
/* Change state on button pressed */
if (ev[yalv].value == 0)
eval_keycode(ev[yalv].code);
}
}
}
close(file);
reset_leds();
exit(0);
}
The compilation is going well.
When I execute the code it shows me this :
Starting simplekey app
File Path: /dev/input/event0
When I push the BUTTON nothing happens and when I release it the LED changes state and it shows me this in the terminal :
BTN pressed
The problem is that the code continue executing until I press CTRL+C to exit.
I just want it to wait until the event ( pressing BUTTON ) happens then change LED state and finally exit automatically.
My question is how to modify the program for this purpose ? I thought about using Threads and Signals but I don't have any idea about them. Thank you!
I presume you are running all this under a system complying either SVr4, or 4.3BSD ot POSIX.1-2001 (or later versions).
You are missing the the check on the read() return value, which is not size_t but rather ssize_t (that is it is signed).
Your code could then be changed like this:
/* ... */
ssize_t rb; /* !!! */
/* ... */
for (;;) {
/* Blocking read */
rb= read(file, &ev, sizeof(ev));
if (rb <= 0) /* Check for the EOF */
break;
for (yalv = 0;
yalv < (int) (rb / sizeof(struct input_event));
yalv++) {
if (ev[yalv].type == EV_KEY) {
/* Change state on button pressed */
if (ev[yalv].value == 0)
eval_keycode(ev[yalv].code);
}
}
}
Please, refer to the friendly man pages.
I think I may have a threading problem in c, but I'm not sure.
My goal is to execute two separate functions inside a while(1) loop as such:
One of these functions is kbget() to retrieve the key pressed in a terminal in non-canonical mode.
The second one is to constantly get the terminal window size with the ioctl(1, TIOCGWINSZ,...) function.
It normally doesn't work because the while(1) loop stops to get a keypress from the user before executing the second function to reevaluate the terminal window size. If the terminal window is resized before a key is pressed, the function to evaluate its size isn't executed unless a random key is pressed again.
In other words, resizing the terminal window doesn't update the size values in the Window struct below unless a key is pressed.
I want the program to update the y_size & x_size values 'live' as the terminal is resized.
Here's the issue in code without POSIX threads:
Executing with :
gcc -Wall scr.h main.c -o main && ./main
(scr.h below has kbget() to change terminal mode):
main.c:
#include "scr.h"
#include <sys/ioctl.h>
#define gotoyx(y, x) printf("\033[%d;%dH", (y), (x)) // equivalent to move(y, x) in ncurses
#define del_from_cursor(x) printf("\033[%dX", (x)) // delete x characters from cursor position
typedef struct {
int y_size;
int x_size;
} Window;
int main(void)
{
printf("\033[?1049h\033[2J\033[H"); // remember position & clear screen
gotoyx(1, 10);
printf("Press <ESC> to stop program.");
gotoyx(2, 10);
printf("Resizing the terminal window does not 'automatically' update the size shown on screen");
Window w;
struct winsize w_s;
while (1) {
// evaluate terminal size
if (!ioctl(1, TIOCGWINSZ, &w_s)) {
w.y_size = w_s.ws_row;
w.x_size = w_s.ws_col;
}
// print terminal size and center it
gotoyx(w.y_size / 2, w.x_size / 2);
del_from_cursor(5);
printf("w.y_size: %d", w.y_size);
gotoyx((w.y_size / 2) + 1, w.x_size / 2);
del_from_cursor(5);
printf("w.x_size: %d", w.x_size);
// get key pressed by user in terminal & exit if <ESC> is pressed
if (kbget() == 0x001b) { break; }
}
printf("\033[2J\033[H\033[?1049l"); // clear screen & restore
return 0;
}
I have tried solving this using threads but I was unsuccessful so far.
I have modified the main.c file above by adding 2 functions (get_window_size & get_key):
(scr.h has the kbget() function in get_key() to change the terminal to canonical mode)
main.c:
#include "scr.h"
#include <sys/ioctl.h>
#include <pthread.h>
#define gotoyx(y, x) printf("\033[%d;%dH", (y), (x))
#define del_from_cursor(x) printf("\033[%dX", (x))
typedef struct {
int y_size;
int x_size;
} Window;
void *get_window_size(void *arg)
{
Window *w = (Window *)arg;
struct winsize w_s;
if (!ioctl(1, TIOCGWINSZ, &w_s)) {
w->y_size = w_s.ws_row;
w->x_size = w_s.ws_col;
}
pthread_exit(0);
}
void *get_key(void *arg)
{
int *key = (int *)arg;
free(arg);
*key = kbget();
int *entered_key = malloc(sizeof(*key));
*entered_key = *key;
pthread_exit(entered_key);
}
int main(void)
{
printf("\033[?1049h\033[2J\033[H");
Window w;
pthread_t tid[3];
pthread_attr_t attr;
pthread_attr_init(&attr);
pthread_create(&tid[0], &attr, get_window_size, &w);
int *c = malloc(sizeof(*c));
int *key_pressed;
while (1) {
// for initial size
pthread_join(tid[0], NULL);
// printing size to screen
gotoyx(w.y_size / 2, w.x_size / 2);
del_from_cursor(5);
printf("w.y_size: %d", w.y_size);
gotoyx((w.y_size / 2) + 1, w.x_size / 2);
del_from_cursor(5);
printf("w.x_size: %d", w.x_size);
// get window size
pthread_attr_t attr1;
pthread_attr_init(&attr1);
pthread_create(&tid[1], &attr1, get_window_size, &w);
// get key entered by user
pthread_attr_t attr2;
pthread_attr_init(&attr2);
pthread_create(&tid[2], &attr2, get_key, c);
pthread_join(tid[1], NULL);
pthread_join(tid[2], (void **)&key_pressed);
if (*key_pressed == 0x001b) {
break;
} else {
free(key_pressed);
}
}
if (key_pressed != NULL) {
free(key_pressed);
}
printf("\033[2J\033[H\033[?1049l");
return 0;
}
The scr.h file changes the terminal mode to non-canonical (the kbget() function above is called from here):
I don't think there's any problems in scr.h as it is taken from here (Move the cursor in a C program).
scr.h:
#ifndef SCR_H
#define SCR_H
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <termios.h>
#include <unistd.h>
struct termios term, oterm;
int getch(void)
{
int c = 0;
tcgetattr(STDIN_FILENO, &oterm);
memcpy(&term, &oterm, sizeof(term));
term.c_lflag &= ~(ICANON | ECHO);
term.c_cc[VMIN] = 1;
term.c_cc[VTIME] = 0;
tcsetattr(STDIN_FILENO, TCSANOW, &term);
c = getchar();
tcsetattr(STDIN_FILENO, TCSANOW, &oterm);
return c;
}
int kbhit(void)
{
int c = 0;
tcgetattr(STDIN_FILENO, &oterm);
memcpy(&term, &oterm, sizeof(term));
term.c_lflag &= ~(ICANON | ECHO);
term.c_cc[VMIN] = 0;
term.c_cc[VTIME] = 1;
tcsetattr(STDIN_FILENO, TCSANOW, &term);
c = getchar();
tcsetattr(STDIN_FILENO, TCSANOW, &oterm);
if (c != -1) { ungetc(c, stdin); }
return c != -1 ? 1 : 0;
}
int kbesc(void)
{
int c = 0;
if (!kbhit()) { return 0x001b; } // 0x001b is the <ESC> key
c = getch();
if (c == 0) { while (kbhit()) { getch(); } }
return c;
}
int kbget(void)
{
int c = getch();
return c == 0x001b ? kbesc() : c; // 0x001b is the <ESC> key
}
#endif // SCR_H
I also get errors Invalid write of size 4 in the code above with pthread while executing with valgrind:
gcc -Wall scr.h main.c -pthread -o main
valgrind -v --leak-check=yes ./main
I am aware of the existence of ncurses and pdcurses. I am only doing this as an exercise for myself.
UPDATE
I have changed my code to the following, unfortunately the ret variable never changes to -1:
#include "scr.h"
#include <errno.h>
#include <sys/ioctl.h>
#define gotoyx(y, x) printf("\033[%d;%dH", (y), (x))
#define del_from_cursor(x) printf("\033[%dX", (x))
typedef struct {
int y_size;
int x_size;
} Window;
static int sigwinch_arrived = 0;
void sigwinch_handler(int signum)
{ sigwinch_arrived = 1; }
void on_window_size_change(Window *w)
{
struct winsize w_s;
// evaluate terminal size
if (!ioctl(1, TIOCGWINSZ, &w_s)) {
w->y_size = w_s.ws_row;
w->x_size = w_s.ws_col;
}
// print terminal size in its center
gotoyx(w->y_size / 2, w->x_size / 2);
del_from_cursor(15);
printf("w.y_size: %d", w->y_size);
gotoyx((w->y_size / 2) + 1, w->x_size / 2);
del_from_cursor(15);
printf("w.x_size: %d", w->x_size);
}
int main(void)
{
printf("\033[?1049h\033[2J\033[H");
gotoyx(1, 10);
printf("Press <ESC> to stop program.");
gotoyx(2, 10);
printf("Resizing the terminal window does not 'automatically' update the size shown on screen");
Window w;
int ret;
while (1) {
// get key pressed by user in terminal & exit if <ESC> is pressed
ret = kbget();
gotoyx(10, 10);
del_from_cursor(8);
printf("ret: %d", ret);
if (ret == -1) {
if (errno == EAGAIN) {
if (sigwinch_arrived) {
sigwinch_arrived = 0;
on_window_size_change(&w);
}
}
} else if (ret == 0x001b) {
break;
}
}
printf("\033[2J\033[H\033[?1049l");
return 0;
}
Extension: as per this answer, if your ncurses was compiled with the --enable-sigwinch flag, it does the solution below automatically (if you did not override SIGWINCH before ncurses_init() yet). In this case, getch() (wgetch()) will simply return KEY_RESIZE if a resize event is happened.
If the size of your controlling character terminal changes, your process should get a SIGWINCH signal (window size change, signal 28 on Linux).
It can be sent by the kernel (if there is mode switch on a character desktop), or by the virtual terminal software (xterm, gnome-terminal, screen, etc).
If your process gets a signal, its blocking kernel calls, including getch(), stop with the -EAGAIN error number. It means, that the blocking call stopped before time due to an arrived signal.
Note, from a signal handler, you can't do too much (for example: no malloc()), and the best to do if you make the least possible. Typical signal handlers change a static, global variable, whose value is checked by the main program.
Untested example code:
static int sigwinch_arrived = 0;
// this is called from the signal handler - nothing complex is allowed here
void sigwinch_handler(int signum) {
sigwinch_arrived = 1;
}
// callback if there is a window size change
void on_window_size_change() {
...
}
// main program
...
while (1) { // your main event handler loop
int ret = getch();
if (ret == ERR) {
if (errno == EAGAIN) {
if (sigwinch_arrived) {
sigwinch_arrived = 0;
on_window_size_change();
}
}
}
...
}
I am trying to read mouse events from the /dev/input/mice file. I am able to parse the 3 byte mouse input for getting the three button states and the increments in X and Y coordinates. However, the mouse input when I scroll up is identical to that when I scroll down. How do I distinguish a scroll up event from a scroll down event? Are there any ioctls that can do any required configuration so that I get different inputs from the mouse on these two events?
The following is a simple program to see the input from a mouse when a mouse event occurs. Scroll up and scroll down events cause the same output to be printed by this program (namely, 8 0 0).
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stdbool.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <errno.h>
int main(void) {
int mouse_fd = open("/dev/input/mice", O_RDONLY | O_NONBLOCK);
signed char input[4];
ssize_t rd_cnt;
if(mouse_fd < 0)
{
perror("Could not open /dev/input/mice");
exit(EXIT_FAILURE);
}
while(true)
{
errno = 0;
rd_cnt = read(mouse_fd, input, 4);
if(rd_cnt <= 0 && errno != EAGAIN)
{
perror("Mouse read error:");
exit(EXIT_FAILURE);
}
else
{
for(int i = 0; i < rd_cnt; i++)
{
printf("%d", input[i]);
if(i == rd_cnt - 1)
{
printf("\n");
}
else
{
printf("\t");
}
}
}
}
return 0;
}
An alternative would be to use SDL2.
I've managed to mash together an example of reading mouse inputs with SDL, so take what you like from it.
#include <SDL2/SDL.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stdbool.h>
char* itoa(int i, char b[]){
char const digit[] = "0123456789";
char* p = b;
if(i<0){
*p++ = '-';
i *= -1;
}
int shifter = i;
do{ //Move to where representation ends
++p;
shifter = shifter/10;
}while(shifter);
*p = '\0';
do{ //Move back, inserting digits as u go
*--p = digit[i%10];
i = i/10;
}while(i);
return b;
}
int main()
{
//initialize the window
bool quit = false;
SDL_Init(SDL_INIT_VIDEO);
SDL_Window* window = SDL_CreateWindow("Mouse Events", SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED, 640, 480, 0);
SDL_Renderer* renderer = SDL_CreateRenderer(window, -1, 0);
// the event that will occur when a mouse event happens
SDL_Event event;
while(!quit)
{
while (SDL_PollEvent(&event)) //returns one is there is a pending event (if an event happens)
{
switch(event.type)
{
case SDL_QUIT: //if the window is exited
quit = true;
break;
case SDL_MOUSEBUTTONDOWN:
switch (event.button.button)
{
case SDL_BUTTON_LEFT:
SDL_ShowSimpleMessageBox(0, "Click", "Left button was pressed!", window);
break;
case SDL_BUTTON_RIGHT:
SDL_ShowSimpleMessageBox(0, "Click", "Right button was pressed!", window);
break;
}
break;
case SDL_MOUSEWHEEL:
if(event.wheel.y == -1) //negative means the scroll wheel has gone away from the user
{
SDL_ShowSimpleMessageBox(0, "Wheel Event", "You rolled away from yourself!", window);
} else if (event.wheel.y == 1) //vice-versa
{
SDL_ShowSimpleMessageBox(0, "Wheel Event", "You rolled towards yourself!", window);
}
}
}
//do some SDL cleanup
SDL_Rect dstrect = { 288, 208, 64, 64 };
SDL_RenderClear(renderer);
SDL_RenderPresent(renderer);
}
}
The event.wheel type can be found here: https://wiki.libsdl.org/SDL_MouseWheelEvent
Hope this is of some use to you!
If you don't want to use SDL2, it may be worth have a look in the source of the library to see what it's doing.
I am writing a program in C that uses ncurses to check if a key is being pressed. The problem is that there is a key repeat delay.
If I for example hold the key 'a' while in the terminal there is a short delay before 'a' gets repeatedly entered. I want to be able to know if it is being pressed from the point where it is actually pressed.
How do temporarily change this delay to be equal to 0 while in the terminal? I'm currently using Mac OSX 10.10.5.
With ncurses (any curses implementation), you would use getch rather than getchar. The latter is a C standard I/O input function.
Someone suggested Create a function to check for key press in unix using ncurses, which contains an answer worth mentioning. It uses nodelay to eliminate the time normally spent in getch for successive bytes of an escape sequence. In curses, you always have a tradeoff between waiting or not, since an escape sequence may not arrive all in one read operation. The example shown there reports cases when no character is available, and pauses (sleeps) for a short time in that case.
If you only want to see the characters which are read, you could eliminate that pause (but making your program use a lot of CPU time):
#include <ncurses.h>
int kbhit(void)
{
int ch = getch();
if (ch != ERR) {
ungetch(ch);
return 1;
} else {
return 0;
}
}
int main(void)
{
initscr();
cbreak();
noecho();
nodelay(stdscr, TRUE);
scrollok(stdscr, TRUE);
while (1) {
if (kbhit()) {
printw("Key pressed! It was: %d\n", getch());
}
}
}
or (recognizing that there is a tradeoff), use napms to pause a short amount of time, but lessening the CPU time used:
#include <ncurses.h>
int kbhit(void)
{
int ch = getch();
if (ch != ERR) {
ungetch(ch);
return 1;
} else {
return 0;
}
}
int main(void)
{
initscr();
cbreak();
noecho();
nodelay(stdscr, TRUE);
scrollok(stdscr, TRUE);
while (1) {
if (kbhit()) {
printw("Key pressed! It was: %d\n", getch());
} else {
napms(20);
}
}
}
Try this to ignore buffered key repeats:
int key;
if ((key = getch()) != ERR) {
while (getch() == key);
}
In context:
//example.c
#include <ncurses.h>
#include <unistd.h>
int main(int argc, char *argv[]) {
int pos_x = 0;
int max_x = 0, max_y = 0;
int key = 0;
int on = 1;
initscr();
noecho();
cbreak();
curs_set(FALSE);
keypad(stdscr, TRUE);
nodelay(stdscr, TRUE);
getmaxyx(stdscr,max_y,max_x);
while(1) {
clear();
mvprintw(0, 0, "Press, hold and release L-R arrow keys. Press UP/DOWN to toggle function.");
mvprintw(1, 0, "Skip buffered repeats: %s", (on ? "ON" : "OFF"));
mvprintw(2, pos_x, "#");
refresh();
usleep(50000);
getmaxyx(stdscr,max_y,max_x);
key = getch();
// skip buffered repeats
if (on) {
if (key != ERR) {
while (getch() == key);
}
}
//
switch (key) {
case KEY_LEFT:
pos_x += (pos_x > 0 ? -1 : 0); break;
case KEY_RIGHT:
pos_x += (pos_x < max_x - 1 ? 1 : 0); break;
case KEY_UP:
on = 1; break;
case KEY_DOWN:
on = 0; break;
}
}
endwin();
}
Compile and run with gcc example.c -lncurses -oexample && ./example
I want to log my mouse click positions. I have tried this;
#include <stdio.h>
#include <stddef.h>
#include <X11/Xlib.h>
#include <assert.h>
#include <unistd.h>
#include <signal.h>
int working = 1;
void signal_callback_handler(int signum) {
working = 0;
}
int main () {
signal(SIGINT, signal_callback_handler);
signal(SIGTSTP, signal_callback_handler);
signal(SIGTERM, signal_callback_handler);
Display *d = XOpenDisplay(NULL);
assert(d);
XSelectInput(d, DefaultRootWindow(d), ButtonPressMask);
while(working) {
XEvent e;
XNextEvent(d,&e);
if (e.type == ButtonPress) {
printf("%dx%d",e.xbutton.x,e.xbutton.y);
}
}
return 0;
}
But I am seeing this error:
X Error of failed request: BadAccess (attempt to access private resource denied)
Major opcode of failed request: 2 (X_ChangeWindowAttributes)
Serial number of failed request: 7
Current serial number in output stream: 7
What is wrong with my code, and how can I fix it?
Update
I have researched this a little bit more, and got some help from the folks in #xorg-dev. It seems like it is impossible to do with regular Xlib, because only one client can register for button press on a window. In this case, my WM already registered, therefore I get bad access. It seems like this can be done using X input extensions and by listening XI_RawButtonPress Event, which I am still trying to figure out how to do. Here is what I have so far;
#include <stdio.h>
#include <X11/Xlib.h>
#include <X11/extensions/XInput2.h>
#include <signal.h>
#include <assert.h>
int working = 1;
void signal_callback_handler(int signum) {
working = 0;
}
int main() {
signal(SIGINT, signal_callback_handler);
signal(SIGTSTP, signal_callback_handler);
signal(SIGTERM, signal_callback_handler);
/* Connect to the X server */
Display *dpy = XOpenDisplay(NULL);
assert(dpy);
/* XInput Extension available? */
int opcode, event, error;
if (!XQueryExtension(dpy, "XInputExtension", &opcode, &event, &error)) {
printf("X Input extension not available.\n");
return -1;
}
/* Which version of XI2? We support 2.0 */
int major = 2, minor = 0;
if (XIQueryVersion(dpy, &major, &minor) == BadRequest) {
printf("XI2 not available. Server supports %d.%d\n", major, minor);
return -1;
}
XIEventMask eventmask;
unsigned char mask[1] = { 0 }; /* the actual mask */
eventmask.deviceid = 2;
eventmask.mask_len = sizeof(mask); /* always in bytes */
eventmask.mask = mask;
/* now set the mask */
XISetMask(mask, XI_RawButtonPress);
/* select on the window */
XISelectEvents(dpy, DefaultRootWindow(dpy), &eventmask, 1);
while(working) {
XEvent ev;
XNextEvent(dpy, &ev);
if (ev.xcookie.type == GenericEvent &&
ev.xcookie.extension == opcode &&
XGetEventData(dpy, &ev.xcookie))
{
switch(ev.xcookie.evtype)
{
case XI_RawButtonPress:
printf("RawButtonPress");
break;
}
}
XFreeEventData(dpy, &ev.xcookie);
}
}
However, I get this error;
X Error of failed request: XI_BadDevice (invalid Device parameter)
Major opcode of failed request: 131 (XInputExtension)
Minor opcode of failed request: 46 ()
Device id in failed request: 0xad
Serial number of failed request: 15
Current serial number in output stream: 15
Update 2
I have tried to do this with ButtonRelaseEvent, but I am not getting any event. XNextEvent blocks forever, no matter where I click/relase button. Here are the codes;
#include <stdio.h>
#include <stddef.h>
#include <X11/Xlib.h>
#include <assert.h>
#include <unistd.h>
#include <signal.h>
int working = 1;
void signal_callback_handler(int signum) {
working = 0;
}
int main () {
signal(SIGINT, signal_callback_handler);
signal(SIGTSTP, signal_callback_handler);
signal(SIGTERM, signal_callback_handler);
Display *d = XOpenDisplay(NULL);
assert(d);
XSelectInput(d,DefaultRootWindow(d), ButtonReleaseMask);
while(working) {
XEvent e;
XNextEvent(d, &e);
printf("Something Occured");
if (e.type == ButtonRelease) {
printf("%dx%d",e.xbutton.x,e.xbutton.y);
}
}
return 0;
}
Try XWindowEvent instead of XNextEvent.
For example to grab mouse you can do this:
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <X11/Xlib.h>
#include <X11/Xutil.h>
#include <X11/cursorfont.h>
int main(){
Display* display;
int screen_num;
Screen *screen;
Window root_win;
XEvent report;
XButtonEvent *xb = (XButtonEvent *)&report;
int i;
Cursor cursor;
display = XOpenDisplay(0);
if (display == NULL){
perror("Cannot connect to X server");
exit (-1);
}
screen_num = DefaultScreen(display);
screen = XScreenOfDisplay(display, screen_num);
root_win = RootWindow(display, XScreenNumberOfScreen(screen));
cursor = XCreateFontCursor(display, XC_crosshair);
i = XGrabPointer(display, root_win, False,
ButtonReleaseMask | ButtonPressMask|Button1MotionMask, GrabModeSync,
GrabModeAsync, root_win, cursor, CurrentTime);
if(i != GrabSuccess){
perror("Can't grab the mouse");
exit(-1);
}
for(i = 0; i < 10; i++){
XAllowEvents(display, SyncPointer, CurrentTime);
XWindowEvent(display, root_win, ButtonPressMask | ButtonReleaseMask, &report);
switch(report.type){
case ButtonPress:
printf("Press # (%d, %d)\n", xb->x_root, xb->y_root);
break;
case ButtonRelease:
printf("Release # (%d, %d)\n", xb->x_root, xb->y_root);
break;
}
}
XFlush(display);
XUngrabServer(display);
XCloseDisplay( display );
return 0;
}
Yes, from x11 protocol spec:
Multiple clients can select input on the same window; their
event-masks are disjoint. When an event is generated, it will be
reported to all interested clients. However, only one client at a time
can select for SubstructureRedirect, only one client at a time can
select for ResizeRedirect, and only one client at a time can select
for ButtonPress. An attempt to violate these restrictions results in
an Access error.
However, it is allowed for multiple clients to select ButtonRelease event - I just checked with two clients and both receive events.