Reader small image

You're reading from  C++ Game Animation Programming - Second Edition

Product typeBook
Published inDec 2023
Reading LevelN/a
PublisherPackt
ISBN-139781803246529
Edition2nd Edition
Languages
Tools
Concepts
Right arrow
Authors (2):
Michael Dunsky
Michael Dunsky
author image
Michael Dunsky

Michael Dunsky is an educated electronics technician, game developer, and console porting programmer with more than 20 years of programming experience. He started at the age of 14 with BASIC, adding on his way Assembly language, C, C++, Java, Python, VHDL, OpenGL, GLSL, and Vulkan to his portfolio. During his career, he also gained extensive knowledge in virtual machines, server operation, infrastructure automation, and other DevOps topics. Michael holds a Master of Science degree in Computer Science from the FernUniversität in Hagen, focused on computer graphics, parallel programming and software systems.
Read more about Michael Dunsky

Gabor Szauer
Gabor Szauer
author image
Gabor Szauer

Gabor Szauer has been making games since 2010. He graduated from Full Sail University in 2010 with a bachelor's degree in game development. Gabor maintains an active Twitter presence, and maintains a programming-oriented game development blog. Gabor's previously published books are Game Physics Programming Cookbook and Lua Quick Start Guide, both published by Packt.
Read more about Gabor Szauer

View More author details
Right arrow

The mouse and keyboard input for the game window

Adding support for the keys pressed on the keyboard, the buttons on the mouse, or moving the mouse around is a simple copy-and-paste task from the window events – create a member function to be called and add the lambda-encapsulated call to GLFW. The next time you press a key or move the mouse after a successful recompilation, the new callbacks will run.

You can find the enhanced example code in the 05_window_with_input folder.

Let’s start by retrieving the key presses before we add the keyboard callbacks and functions. After this, we will continue to get mouse events and also add the respective functions for them to the code.

Key code, scan code, and modifiers

To get the events for the keys the user presses or releases on their keyboard, GLFW offers another callback. The following callback for a plain key input receives four values:

glfwSetKeyCallback(window, key_callback);
void key_callback(GLFWwindow* window, int key, int scancode, int action, int mods)

These values are listed as follows:

  • The ASCII key code of the key
  • The (platform-specific) scan code of that key
  • The action you carried out (press the key, release it, or hold it until the key repeat starts)
  • The status of the modifier key, such as Shift, Ctrl, or Alt

The key can be compared with internal GLFW values such as GLFW_KEY_A, as they emit the 7-bit ASCII code of the letter you pressed. The function keys, the separate keypad, and the modifier keys return values >256.

The scan code is specific to your system. While it stays the same on your system, the code may differ on another platform. So, hardcoding it into your code is a bad idea.

The action is one of the three values GLFW_PRESS, GLFW_RELEASE, or GLFW_REPEAT, if the key is pressed for longer, but note that the GLFW_REPEAT action is not issued for all keys.

The modifier status is a bitmap to see whether the users pressed keys such as Shift, Ctrl, or Alt. You can also enable the reporting of Caps Lock and Num Lock – this is not enabled in the normal input mode.

For example, we could add a simple keyboard logging to the code. First, add a new function to the Window.h header file:

public:
  void handleKeyEvents(int key, int scancode, int action,
    int mods);

As you can see in the preceding code, we don’t need GLFWwindow in our functions, as we already saved it as a private data member of the class.

Next, add the callback to the GLFW function using a lambda:

  glfwSetKeyCallback(mWindow, [](GLFWwindow *win, int key,
    int scancode, int action, int mods) {
    auto thisWindow = static_cast<Window*>(
      glfwGetWindowUserPointer(win));
    thisWindow->handleKeyEvents(key, scancode, action,
      mods);
    }
  );

This is the same as it was for the window event – get the this pointer of the current instance of the Window class from the user pointer set by glfwSetWindowUserPointer() and call the new member functions of the class.

For now, the member function for the keys can be simple:

void Window::handleKeyEvents(int key, int scancode, int action, int mods) {
  std::string actionName;
  switch (action) {
    case GLFW_PRESS:
      actionName = "pressed";
      break;
    case GLFW_RELEASE:
      actionName = "released";
      break;
    case GLFW_REPEAT:
      actionName = "repeated";
      break;
    default:
      actionName = "invalid";
      break;
  }
  const char *keyName = glfwGetKeyName(key, 0);
  Logger::log(1, "%s: key %s (key %i, scancode %i) %s\n",
    __FUNCTION__, keyName, key, scancode,
    actionName.c_str());
}

Here, we use a switch() statement to set a string depending on the action that has occurred and also call glfwGetKeyName() to get a human-readable name of the key. If no name has been set, it prints out (null). You will also see the key code, which is the ASCII code for letters and numbers, as mentioned earlier in this section, and the platform-specific scan code of the key. As a last field, it will print out if the key was pressed, released, or held until the key repeat from the OS started. The default option is used for completeness here; it should never be called in the current GLFW version as it would indicate a bug.

Different styles of mouse movement

GLFW knows two types of mouse movement: the movement adjusted by the OS and a raw movement.

The first one returns the value with all the optional settings you might have defined, such as mouse acceleration, which speeds up the cursor if you need to move the cursor across the screen.

The following is a callback function, which gets informed if the mouse position changes:

glfwSetCursorPosCallback(window, cursor_position_callback);
void cursor_position_callback(GLFWwindow* window,
  double xpos, double ypos)

Alternatively, you can poll the current mouse position in your code manually:

double xpos, ypos;
glfwGetCursorPos(window, &xpos, &ypos);

The raw mode excludes these settings and provides you with the precise level of movement on your desk or mouse mat. To enable raw mode, first, you have to disable the mouse cursor in the window (not only hide it), and then you can try to activate it:

glfwSetInputMode(window, GLFW_CURSOR,
  GLFW_CURSOR_DISABLED);
if (glfwRawMouseMotionSupported()) {
    glfwSetInputMode(window, GLFW_RAW_MOUSE_MOTION,
      GLFW_TRUE);
}

To exit raw mode, go back to the normal mouse mode:

glfwSetInputMode(window, GLFW_CURSOR, GLFW_CURSOR_NORMAL);

Keeping both movement styles apart will be interesting for the kind of application we are creating. If we want to adjust the settings using an onscreen menu, having the mouse pointer react like it would in other applications on your computer is perfect. But once we need to rotate or move the model, or change the view in the virtual world, any acceleration could lead to unexpected results. For this kind of mouse movement, we should use the raw mode instead.

To add a mouse button callback, add the function call to Window.h:

private:
  void handleMouseButtonEvents(int button, int action,
    int mods);

And in Window.cpp, add the callback handling and the function itself:

  glfwSetMouseButtonCallback(mWindow, [](GLFWwindow *win,
    int button, int action, int mods) {
    auto thisWindow = static_cast<Window*>(
      glfwGetWindowUserPointer(win));
    thisWindow->handleMouseButtonEvents(button, action,
      mods);
    }
  );

This is similar to the keyboard callback discussed earlier in this chapter; we get back the pressed button, the action (GLFW_PRESS or GLFW_RELEASE), and also any pressed modifiers such as the Shift or Alt keys.

The handler itself is pretty basic in the first version. The first switch() block is similar to the keyboard function, as it checks whether the button has been pressed or released:

void Window::handleMouseButtonEvents(int button,
  int action, int mods) {
  std::string actionName;
  switch (action) {
    case GLFW_PRESS:
      actionName = "pressed";
      break;
    case GLFW_RELEASE:
      actionName = "released";
      break;
    default:
      actionName = "invalid";
      break;
  }

The second switch() block checks which mouse button was pressed, and it prints out the names of the left, right, or middle buttons. GLFW supports up to eight buttons on the mouse, and more than the basic three are printed out as "other":

  std::string mouseButtonName;
  switch(button) {
    case GLFW_MOUSE_BUTTON_LEFT:
      mouseButtonName = "left";
      break;
    case GLFW_MOUSE_BUTTON_MIDDLE:
      mouseButtonName = "middle";
      break;
    case GLFW_MOUSE_BUTTON_RIGHT:
      mouseButtonName = "right";
      break;
    default:
      mouseButtonName = "other";
      break;
  }
  Logger::log(1, "%s: %s mouse button (%i) %s\n",
    __FUNCTION__, mouseButtonName.c_str(), button,
    actionName.c_str());
}

When running the code, you should see messages like this:

init: Window successfully initialized
handleWindowMoveEvents: Window has been moved to 0/248
handleMouseButtonEvents: left mouse button (0) pressed
handleMouseButtonEvents: left mouse button (0) released
handleMouseButtonEvents: middle mouse button (2) pressed
handleMouseButtonEvents: middle mouse button (2) released
handleWindowCloseEvents: Window got close event... bye!
cleanup: Terminating Window

You could add more handlers. The example code also uses the callbacks for mouse movement, which gives you the current mouse position inside the window, and the callback for entering and leaving the window.

Previous PageNext Page
You have been reading a chapter from
C++ Game Animation Programming - Second Edition
Published in: Dec 2023Publisher: PacktISBN-13: 9781803246529
Register for a free Packt account to unlock a world of extra content!
A free Packt account unlocks extra newsletters, articles, discounted offers, and much more. Start advancing your knowledge today.
undefined
Unlock this book and the full library FREE for 7 days
Get unlimited access to 7000+ expert-authored eBooks and videos courses covering every tech area you can think of
Renews at AU $19.99/month. Cancel anytime

Authors (2)

author image
Michael Dunsky

Michael Dunsky is an educated electronics technician, game developer, and console porting programmer with more than 20 years of programming experience. He started at the age of 14 with BASIC, adding on his way Assembly language, C, C++, Java, Python, VHDL, OpenGL, GLSL, and Vulkan to his portfolio. During his career, he also gained extensive knowledge in virtual machines, server operation, infrastructure automation, and other DevOps topics. Michael holds a Master of Science degree in Computer Science from the FernUniversität in Hagen, focused on computer graphics, parallel programming and software systems.
Read more about Michael Dunsky

author image
Gabor Szauer

Gabor Szauer has been making games since 2010. He graduated from Full Sail University in 2010 with a bachelor's degree in game development. Gabor maintains an active Twitter presence, and maintains a programming-oriented game development blog. Gabor's previously published books are Game Physics Programming Cookbook and Lua Quick Start Guide, both published by Packt.
Read more about Gabor Szauer