Home > database >  Strange results while reading mouse button press in X11 (repeated values)
Strange results while reading mouse button press in X11 (repeated values)

Time:06-22

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).

  • Related