Reputation: 13758
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;
}
Upvotes: 2
Views: 4790
Reputation: 25466
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.
Upvotes: 1
Reputation: 872
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;
}
Upvotes: 1