c++openglgraphicsglfw

What is common practice for storing a bool to determine if a key is held?


I am new to OpenGL and using GLFW for input. I recently have been developing a simple program to create shapes when clicking the left mouse button. If the left mouse button is held down, the shape will follow the mouse until the left mouse button is released. I currently have a global bool variable keeping track of when the key is being held or has been released. A mouse click callback changes the bool from true to false, respectively. I know it's bad practice to use global variables, thus, what is a better way to go about doing this? I have read that a separate buffer object is commonly used to keep track of this, but what kind of buffer would I want to use? Or is there a better way to go about this that I am unaware of?

// Globals
// -------
bool keyHeld = false;

int main() {


   while (!glfwWindowShouldClose(window)) {
      if (keyHeld) {
      
         // Get screen coordinates
         // Get Cursor Position
         // Convert position to clip. coords. 
         // Create transformation matrix

      }
   }
      // Send matrix to uniform in shader
      // Render shape

   return 0;
}

void mouse_callback(GLFWwindow* window, int button, int action, int mods) {

   if (button == GLFW_MOUSE_BUTTON_LEFT && action == GLFW_PRESS) 
      keyHeld = true;

   if (button == GLFW_MOUSE_BUTTON_LEFT && action == GLFW_RELEASE)
      keyHeld = false;

}

Solution

  • A good way to organize states such as input states of keys and mouse buttons is to put them into a common namespace where you store the booleans for each input. This namespace should have one update function that accesses the input API (in this case GLFW), and updates the state of each key and button. You can then allow access to the buttons through functions that will return the state of a specific input.

    Here is an example of what this input namespace could look like:

    namespace Input {
        bool _mouseDown = false;
    
        void Update(GLFWwindow *window) {
            int state = glfwGetMouseButton(window, GLFW_MOUSE_BUTTON_LEFT);
            if(state == GLFW_PRESS) {
                _mouseDown = true;
            } else if(state == GLFW_RELEASE) {
                _mouseDown = false;
            }
        }
    
        bool MouseDown() {
            return _mouseDown;
        }
    }
    

    This exact example of getting the state of a mouse button is in the glfw api documentation here below the callback example: GLFW Mouse Button Input

    This can also be done for key inputs an example of which can be found here also below the callback example: GLFW Key Input

    Make sure you add glfwPollEvents(); into your main loop before you update your Input namespace.

    For more documentation on input event processing look here

    Imagine clicking your mouse is ordering a pizza and the pizza is the state of the mouse. A callback would be like getting the pizza delivered, every time the API detects a pizza order, it will 'deliver it' or run the callback. The way I did it manually is like picking up the pizza. I have to go and get the 'pizza' or state of the mouse button. You also have to make sure the pizza business is getting the pizza orders, think of glfwPollEvents(); as sending the pizza orders (input states) to the pizza company (api) to get picked up by you, or delivered later.

    The advantage of picking up the pizza is more control, abstraction, and you can combine all of your input updating into one Update function instead of being spread out in multiple callbacks.

    Your updated main function would look like this:

    int main() {
    
       while (!glfwWindowShouldClose(window)) {
    
          glfwPollEvents(); // makes GLFW check for changes in input state
    
          Input::Update(window); // updates our input booleans stored in our Input namespace
    
          if (Input::MouseDown()) { // returns current state of left mouse button
          
             // Get screen coordinates
             // Get Cursor Position
             // Convert position to clip. coords. 
             // Create transformation matrix
    
          }
       }
          // Send matrix to uniform in shader
          // Render shape
    
       return 0;
    }
    

    Make sure that if you store this Input namespace in a header file you include it at the top of your rendering loop file.

    The benefits of this way of abstraction are:

    1. Readability: with a function name of MouseDown(), or whatever you want to call it, other people or even yourself can easily tell what is happening in the code.

    2. Future proofing: if you change the underlying input API all you have to do is change the functions in the Input namespace, instead of having to go through and find every spot where you check for mouse input. Therefore you only have to change the code in one spot.

    3. Organization: all these boolean variables storing the states of input buttons and keys are organized into a common namespace instead of having to keep track of a bunch of global variables.

    If you want extra safety you could put this namespace into a class and keep the member variables as private so they aren't accidently accessed.

    This is my first answer on Stack Overflow so I hope I did a decent job!