Search code examples
c++ubuntukeyboardx11

How to know keypresses using X11 in C++?


My Goal:

I'm making C++ programs but something very important is missing,
the program needs to know which key is pressed but I don't know any way to do it.

Problems:

After some researches, the most native way of getting key presses seems to be using X11.
Everything about keyboard input the Xlib manual were using events,
but I don't know how to handle events!

Is there any way to know key presses without using events?
Or how to use X11 events?

If any clarification is needed, please add a comment or suggest an edit


Solution

  • There are two (and more) ways to achieve it:

    The Unrecommended Hard Way

    It does the required features but is not recommended as it is slower and uses more lines of code.

    uint32_t PressedKeys[8];
    
    bool RefreshPressedKeys() {
      while(XPending(X11Display)) {
        XEvent KeyEvent;
        XNextEvent(X11Display,&KeyEvent);
        if(KeyEvent.type==KeyPress) {
          uint32_t KeyEventCode=KeyEvent.xkey.keycode;
          for(uint8_t i=0;i<8;i++) {
            if(PressedKeys[i]==0) {
              PressedKeys[i]=KeyEventCode;
              break;
            }
          }
        }else if(KeyEvent.type==KeyRelease) {
          uint32_t KeyEventCode=KeyEvent.xkey.keycode;
          for(uint8_t i=0;i<8;i++) {
            if(PressedKeys[i]==KeyEventCode) {
              PressedKeys[i]=0;
              break;
            }
          }
        }
      }
      return true;
    }
    
    uint32_t GetAPressedKey() { //Get a pressed key, won't always the last pressed key
      for(uint8_t i=0;i<8;i++) {
        if(PressedKeys[i]!=0) return PressedKeys[i]; //Returns the first pressed key found
      }
      return 0; //Or 0 when no key found
    }
    
    bool IsKeyPressed(uint32_t KeyFilter) { //Is Key Pressed?
      for(uint8_t i=0;i<8;i++) {
        if(PressedKeys[i]==KeyFilter) return true; //Returns true if the key is pressed
      }
      return false; //Else false
    }
    
    int main() {
      uint8_t Key;
      XSelectInput(X11Display,X11Window,KeyPressMask|KeyReleaseMask); //Enables keyboard input
      while(1) {
        Key = GetPressedKey(); //Gets the pressed key's number
        std::cout << Key << '\n'; //Displays the number
        /* Some code using the Key var */
      }
      return 0;
    }
    
    

    The Recommended Easy Way

    The program might be more complex to understand at first but it's better since it can theoretically handle an "infinite" number of keys pressed at once.

    int main() {
      XSelectInput(Pixel.GetX11Display(),Pixel.GetX11Window(),KeyPressMask|KeyReleaseMask);
      while(1) {
        while(XPending(Pixel.GetX11Display())) { //Repeats until all events are computed
          XEvent KeyEvent;
          XNextEvent(Pixel.GetX11Display(),&KeyEvent); //Gets exactly one event
          if(KeyEvent.type==KeyPress) {
            uint32_t KeyEventCode=KeyEvent.xkey.keycode; //Gets the key code, NOT HIS CHAR EQUIVALENT
            std::cout << KeyEventCode << '\n'; //Displays the key code
    
            /* Code handling a Keypress event */
    
          } else if(KeyEvent.type==KeyRelease) {
             uint32_t KeyEventCode=KeyEvent.xkey.keycode;
             std::cout << KeyEventCode << '\n'; //Displays the key code
    
             /* Code handling a KeyRelease event */
    
          }
        }
    
        /* General code */
    
      }
    }
    

    These two programs were handwritten by me; I may have made an error since I haven't tested them, but it should be something similar.

    These are copyleft and you don't need to credit me :)

    Please comment or suggest an edit for any error found or for code clarification.