I'm making a simple console application where I test reading various events from X11 on Linux. I stubbed upon reading mouse button presses. I can read them, but the results are strange.
Here is the code:
#include <iostream>
#include <fstream>
#include <string>
#include <vector>
#include <ctime>
//X11
#include <X11/Xlib.h>
#include <X11/extensions/XI.h>
#include <X11/extensions/XInput.h>
std::vector<XID> findCandidates(Display* display) {
std::vector<XID> result;
int dev_count;
XDeviceInfo* dev_list = XListInputDevices(display, &dev_count);
if( dev_count == 0 ) { //no devices
XFreeDeviceList(dev_list);
return result;
}
for(int i=0; i<dev_count; i) {
XID id = dev_list[i].id;
if( dev_list[i].type == 0 ) continue; //Omit wrong devices
//classes
auto ici = dev_list[i].inputclassinfo;
bool hasButtons = false;
bool hasValuators = false;
for(int cl=dev_list[i].num_classes; cl>0; --cl) {
if( ici->c_class == ButtonClass ) hasButtons = true;
if( ici->c_class == ValuatorClass ) hasValuators = true;
ici = reinterpret_cast<decltype(ici)>(reinterpret_cast<char*>(ici) ici->length);
}
if( hasButtons && hasValuators ) {
//we have found a candidate
result.push_back(id);
}
}
//cleanup
XFreeDeviceList(dev_list);
return result;
}
void traceInputs(std::ofstream &outputFile, Display* display, XDevice* device);
int main (int argc, char **argv)
{
Display* display = XOpenDisplay(nullptr);
if(display == nullptr) {
std::cerr << "Can't connect to the display" << std::endl;
return 1;
}
std::vector<XID> candidates = findCandidates(display);
std::cout << "Candidates: ";
if( candidates.empty() ) {
std::cout << "none" << std::endl;
} else {
bool needComma = false;
for(auto candidate : candidates) {
if( needComma ) std::cout << ", ";
std::cout << candidate;
needComma = true;
}
std::cout << std::endl;
for(auto deviceID : candidates) {
std::cout << "Opening device #" << deviceID << "...";
XDevice* device = XOpenDevice(display, deviceID);
if( device == nullptr ) {
std::cout << "error" << std::endl;
continue;
}
std::cout << "OK" << std::endl;
std::ofstream outputFile{std::string("Dev_") std::to_string(deviceID), std::ios_base::out | std::ios_base::trunc};
traceInputs(outputFile, display, device);
outputFile.close();
XCloseDevice(display, device);
}
}
//cleanup
XCloseDisplay(display);
return 0;
}
void traceInputs(std::ofstream &outputFile, Display* display, XDevice* device) {
int type;
XEventClass event_class;
constexpr int EVENT_COUNT = 2;
constexpr int BTN_PRESS_EVENT = 0;
constexpr int BTN_RELEASE_EVENT = 1;
XEventClass registered_event_classes[EVENT_COUNT];
int registered_event_types[EVENT_COUNT];
//button events
DeviceButtonPress(device, type, event_class);
registered_event_classes[BTN_PRESS_EVENT] = event_class;
registered_event_types[BTN_PRESS_EVENT] = type;
DeviceButtonRelease(device, type, event_class);
registered_event_classes[BTN_RELEASE_EVENT] = event_class;
registered_event_types[BTN_RELEASE_EVENT] = type;
//register events
Window w = XDefaultRootWindow(display);
XSelectExtensionEvent(display, w, registered_event_classes, EVENT_COUNT);
std::time_t start, stop;
start = std::time(nullptr);
do{
XEvent event;
for(int i = 0; i < EVENT_COUNT; i) {
int event_type = registered_event_types[i];
if( XCheckTypedEvent(display, event_type, &event) ) {
outputFile << "Type: " << event.type << ' ';
switch(i) {
case BTN_PRESS_EVENT:
outputFile << "ButtonPress";
outputFile << "\tx=" << event.xbutton.x_root << " y=" << event.xbutton.y_root;
outputFile << " state=" << event.xbutton.state << " button=" << event.xbutton.button;
break;
case BTN_RELEASE_EVENT:
outputFile << "ButtonRelease";
outputFile << "\tx=" << event.xbutton.x_root << " y=" << event.xbutton.y_root;
outputFile << " state=" << event.xbutton.state << " button=" << event.xbutton.button;
break;
}
outputFile << std::endl;
}
}
stop = std::time(nullptr);
}while(stop - start < 3); //less than 3 seconds
}
and that's what it writes to the file corresponding to my mouse device:
Type: 69 ButtonPress x=1221 y=732 state=1221 button=732
Type: 70 ButtonRelease x=1221 y=732 state=1221 button=732
Type: 69 ButtonPress x=1221 y=732 state=1221 button=732
Type: 70 ButtonRelease x=1221 y=732 state=1221 button=732
Type: 69 ButtonPress x=1221 y=732 state=1221 button=732
If you look closely you will spot the x
and state
fields and y
and button
fields shows the same value. This definitely should not be, but I can't identify the source of such phenomena.
[EDIT] I modified the code and the result as I have found the repeated values.
CodePudding user response:
69 and 70 are not X11 ButtonPress
and ButtonRelease
event types. Those are 4 and 5 respectively. (The last X11 event type is 36 as I'm writing this). You are getting extension events, which are different from X11 core events.
You need to check your type against event_type_base
member of XInputClassInfo
, for example button press events will generate XI_DeviceButtonPress event_type_base
event types, and the event structure is XDeviceButtonEvent
and not XButtonEvent
. These are not members of the XEvent
union so rather than event.xbutton
, you need to use ((XDeviceButtonEvent&)event).