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.
Related
I'm trying to get the terminal window size, even when I resize the window, I'm using termcaps for this, the problem is when I resize the window, the values of lines and columns stays the same instead of updating, I also tried using ncurses's LINES and COLS globals, but the same thing happens.
Here is a minimal reproductible example:
#include <ncurses.h>
#include <term.h>
#include <unistd.h>
#include <stdlib.h>
#include <stdio.h>
int main(void)
{
char * term_type = getenv("TERM");
int ret;
int li_cap;
int co_cap;
if (!term_type)
{
write(2, "TERM env must be set\n", 21);
return (-1);
}
if ((ret = tgetent(NULL, term_type)) == -1)
{
write(2, "Could not access to the termcap database\n", 41);
return (-1);
}
if (!ret)
{
write(2, "This terminal is not supported by termcaps\n", 43);
return (-1);
}
while (1)
{
sleep(1);
li_cap = tgetnum("li");
co_cap = tgetnum("co");
printf("%d %d\n", li_cap, co_cap);
}
return (0);
}
So when I resize the window inside the loop, the values stay the same, I want to get the lines and colums in real time, how could I do this with termcaps?
The termcap data and the environment variables COLUMNS and LINES are unreliable and aren't updated upon terminal resizing, especially during program execution. There is another solution for POSIX systems where you can:
retrieve the size of the terminal window with ioctl(0, TIOCGWINSZ, &ws)
register a signal handler to get notified of terminal size changes.
Here is a demonstration program:
#include <stdio.h>
#include <signal.h>
#include <termios.h>
#include <sys/ioctl.h>
#include <unistd.h>
static volatile unsigned char term_size_updated;
static void term_resize() {
term_size_updated = 1;
}
static void term_get_size(int *cols, int *rows) {
struct winsize ws;
/* get screen dimensions from (pseudo) tty ioctl */
if (ioctl(0, TIOCGWINSZ, &ws) == 0) {
*cols = ws.ws_col;
*rows = ws.ws_row;
} else {
*cols = *rows = -1;
}
}
int main() {
struct sigaction sig;
int cols, rows;
/* set up terminal resize callback */
sig.sa_handler = term_resize;
sigemptyset(&sig.sa_mask);
sig.sa_flags = 0;
sigaction(SIGWINCH, &sig, NULL);
term_size_updated = 1;
for (;;) {
if (term_size_updated) {
term_size_updated = 0;
term_get_size(&cols, &rows);
fprintf(stderr, "term_resize: cols=%d, rows=%d\n", cols, rows);
}
sleep(1);
}
return 0;
}
First of all, I don't know if I can explain well my problem or you can get it in the appropriate way. But I will try to make it clear for you.
In fact, I have two different C programs.
The first one is a simple loop print of a message on the console :
#include <stdlib.h>
#include <stdio.h>
#include <unistd.h>
int main ()
{
while(1)
{
printf("WAITING\n");
sleep(1);
}
}
The second one is a blocking program that waits for an event ( press button ) to turn on led in my embedded board.
#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 left pressed\n");
/* figure out red 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));
if (rb < (int) sizeof(struct input_event)) {
perror("simplekey: short read");
exit(1);
}
for (yalv = 0;
yalv < (int) (rb / sizeof(struct input_event));
yalv++) {
if (ev[yalv].type == EV_KEY) {
printf("%ld.%06ld ",
ev[yalv].time.tv_sec,
ev[yalv].time.tv_usec);
printf("type %d code %d value %d\n",
ev[yalv].type,
ev[yalv].code, ev[yalv].value);
/* Change state on button pressed */
if (ev[yalv].value == 0)
eval_keycode(ev[yalv].code);
}
}
}
close(file);
reset_leds();
exit(0);
}
When I execute the second code, the program starts waiting for the event to switch on/off the led.
My question is :
How can I make interaction between the two programs ? I want to execute the firs one --> It starts printing for me " WAITING " until I press the BUTTON --> the LED turn on --> and then it goes back to the first program and re-start printing " WAITING " on the console.
I don't know if I explained well the issue but I hope that you can help me! Thank you.
You need a communication mechanism between your two programs. This is also known als inter-process communication.
Generally, you have several options to achieve this (depending on the operating system you are using, not all of them may be available):
Shared memory / shared files
Message passing (e.g. via sockets)
Pipes
Signals
A helpful introduction can be found here.
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 have ncurses program where I need instant response to user input and term resize and 1 sec delay between redraws.
By using sleep(1) I got instant redraw on startup and term resize but 1 sec delay on user input.
By using timeout(1 * 1000) and getch() I get instant response for input but 1 sec redraw delay on startup and resize.
Here is example program to demonstrate the problem:
#include <stdlib.h>
#include <stdio.h>
#include <signal.h>
#include <ncurses.h>
static sig_atomic_t resize;
void sighandler(int sig) {
if (sig == SIGWINCH)
resize = 1;
}
int main(int argc, char *argv[]) {
double delay = 1.0;
char key = ERR;
WINDOW *testwin;
if (argc > 1)
delay = strtod(argv[1], NULL);
signal(SIGWINCH, sighandler);
initscr();
timeout(delay * 1000);
testwin = newwin(LINES, COLS, 0, 0);
while (key != 'q') {
if (key != ERR)
resize = 1;
if (resize) {
endwin();
refresh();
clear();
werase(testwin);
wresize(testwin, LINES, COLS);
resize = 0;
}
box(testwin, 0, 0);
wnoutrefresh(testwin);
doupdate();
key = getch();
}
delwin(testwin);
endwin();
return EXIT_SUCCESS;
}
I'm thinking about select and/or threads: one thread could monitor the resize while the other one will wait for user input.
You can synchronize threads with a select, which can wait on multiple file descriptors: for example you can write on a pipe when you detect an event to wake-up the main process. Cool thing is you can also set a timeout to be woken-up even if no event happened (then you can put a timeout of one second to trigger the redraw).
I managed to solve the resize delay by removing clear();
#include <stdlib.h>
#include <stdio.h>
#include <signal.h>
#include <ncurses.h>
static sig_atomic_t resize;
void sighandler(int sig) {
if (sig == SIGWINCH)
resize = 1;
}
int main(int argc, char *argv[]) {
double delay = 1.0;
char key = ERR;
WINDOW *testwin;
if (argc > 1)
delay = strtod(argv[1], NULL);
signal(SIGWINCH, sighandler);
initscr();
timeout(delay * 1000);
testwin = newwin(LINES, COLS, 0, 0);
while (key != 'q') {
key = getch();
if (key != ERR)
resize = 1;
if (resize) {
endwin();
refresh();
werase(testwin);
wresize(testwin, LINES, COLS);
resize = 0;
}
box(testwin, 0, 0);
wnoutrefresh(testwin);
doupdate();
}
delwin(testwin);
endwin();
return EXIT_SUCCESS;
}
I am an embedded programmer and using a multithreaded application which is going to receive pixel data over serial line and display it in a window. I am using openCV's cvSetData() method to copy the data received over serial line and populate it to an openCV array. Also using the cvShowImage() function I am displaying the continuously updating pixel data( concept of displaying a video).
Here is a snippet from my code:
//-------------------------------------Start of code------------------------------------//
#include <stdio.h>
#include <stdlib.h>
#include <fcntl.h>
#include <unistd.h>
#include <termios.h>
#include <sys/select.h>
#include "serial_comm_defines.h"
#include <opencv2/highgui/highgui.hpp>
#include <signal.h>
#include <time.h>
#include <sys/time.h>
#include <pthread.h>
extern unsigned char array[COUNT_LIM];
IplImage *newimage;
img_disp_method(void)
{
cvSetData((CvArr*)newimage, (void*)array, 1556);
cvNamedWindow("Mywindow",CV_WINDOW_FREERATIO);
cvResizeWindow("Mywindow", 1556, 360);
cvShowImage("Mywindow",(CvArr*)newimage);
cvWaitKey(1);
}
void *serial_thread_method(void* my_fd)
{
clock_t start = 0, end = 0;
double time_taken = 0;
if ((int)my_fd<0)
printf("\nError opening device file\n");
else
{
printf("\nDevice file opened successfully\n");
if ( serial_config((int)my_fd) < 0)
printf("\nUnable to configure serial port\n");
else
{
printf("\nSerial port configured successfully\n");
for(;;)
{
start = clock();
serial_read((int)my_fd);
end = clock();
printf("\nTime taken:%f seconds\n", (double)((end-start)/CLOCKS_PER_SEC));
}
}
}
close ((int)my_fd);
return NULL;
}
int main(int argc, char **argv)
{
pthread_t serial_read_thread;
int my_fd=0, i=0, temp=0, serial_thread_ret=0;
newimage = cvCreateImageHeader(cvSize(HEIGHT, WIDTH), IPL_DEPTH_8U, 0x01);
struct timeval my_value={0,10000};
struct timeval my_interval={0,10000};
struct itimerval my_timer={my_interval,my_value};
setitimer(ITIMER_REAL, &my_timer, 0);
signal(SIGALRM, img_disp_method);
my_fd = open_device_file((char**)argv);
if ( (serial_thread_ret = pthread_create(&serial_read_thread, NULL, serial_thread_method, (void*)my_fd) == 0))
fprintf(stdout, "\nSerial read thread created successfully\n");
else
perror("\nError creating serial read thread\n");
pthread_join(&serial_read_thread, NULL);
cvReleaseImageHeader(&newimage);
return NULL;
}
//----------------------------------------End of code--------------------------------------//
The code is compiling fine. But when I execute the binary it throws the following error. I also observed that if change the value of timer (value of my_value and my_interval) to anywhere greater than 30ms (30000) the code works just fine. Please explain what is happening.
Try using a virtual timer instead of a real timer.
Something like this
setitimer(SIGVTALRM, &my_timer, 0);
signal(SIGVTALRM, img_disp_method);