Search code examples
c++x11xcb

xcb key repeat ignoring not working


I have been trying to figure out how to ignore repeated keys in an xcb event loop and so far have got this:

extern xcb_connection_t *connection;

// looks like there's a leak, but in the finished product there isn't    
bool poll_event(/*my_event_type e*/){
    static xcb_generic_event_t *ev = nullptr;
    static xcb_key_press_event_t *last_key_ev = nullptr;

    if(!(ev = xcb_poll_for_event(connection)))
        return false;

    switch(ev->response_type & ~0x80){
        case XCB_KEY_PRESS:{
            xcb_key_press_event_t *kp = static_cast<decltype(kp)>(ev);                

            if(last_key_ev &&
                    ((last_key_ev->response_type & ~0x80) == XCB_KEY_RELEASE) &&
                    (last_key_ev->detail == kp->detail) &&
                    (last_key_ev->time == kp->time)
                ){
                std::free(last_key_ev);
                last_key_ev = kp;

                // is repeated key, ignore this event

                return false;
            }

            std::free(last_key_ev);
            last_key_ev = kp;
            return true;
        }

        case XCB_KEY_RELEASE:{
            /* same as KEY_PRESS but looking for KEY_PRESS in 'last_key_ev' */
        }

        default:
            std::free(ev);
            return true;
    }
}

This doesn't work because it will only discard the second half of the repeated event pair (XCB_KEY_RELEASE then XCB_KEY_PRESS), so I get a bunch of XCB_KEY_RELEASE events rather than none. but there doesn't seem to be a function in xcb for testing if there is an event in the queue without modifying the queue itself.

I am looking for an XEventsQueued equivalent in xcb so I could test if there is an event queued straight afterward instead of using the last event that occured, but have failed to do so yet.

Have any of you done this already and be willing to pass on your wisdom in the matter?


Solution

  • So, @n.m said that XCB has no utility for peaking into the event queue. So I wrote my own wrapper over the xcb event queue so I could peak into the next event. Here is how I implemented it if anyone is interested:

    First my event queue:

    extern xcb_connection_t *connection;
    
    struct my_event_queue_t{
        xcb_generic_event_t *prev = nullptr;
        xcb_generic_event_t *current = nullptr;
        xcb_generic_event_t *next = nullptr;
    } event_queue;
    
    void update_event_queue(){
        std::free(event_queue.prev);
        event_queue.prev = event_queue.current;
        event_queue.current = event_queue.next;
        event_queue.next = xcb_poll_for_queued_event(connection);
    }
    

    Then the event loop:

    struct my_event_type;
    
    bool poll_event(my_event_type &ret){
        static xcb_generic_event_t *xcb_ev = nullptr;
        update_event_queue();
    
        xcb_ev = event_queue.current;
        if(!xcb_ev) return false;
    
        switch(xcb_ev->response_type & ~0x80){
            case XCB_KEY_RELEASE:{
                static xcb_key_press_event_t *kp = nullptr;
                kp = xcb_ev;
    
                if(event_queue.next &&
                        ((event_queue->response_type & ~0x80) == XCB_KEY_PRESS) &&
                        (reinterpret_cast<decltype(kp)>(xcb_ev)->time == kp->time) &&
                        (reinterpret_cast<decltype(kp)>(xcb_ev)->detail == kp->detail)
                    ){
                    update_event_queue(); // eat repeat event
                    return false;
                }
    
                // update ret
                return true;
            }
    
            case XCB_KEY_PRESS:{
                // handle normally
                // update ret
                return true;
            }
    
            default:
                // signify unknown event
                return true;
        }
    }
    

    This is how I ignore key repeats in my application, which is a toolkit for game/graphical application development. I haven't tested what sort of performance dip this gives, but it should be minimal.