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.