top

Input Events in C++

This topic describes the "Input Events in C++" sample application implementation.

This tutorial describes how to implement a simple input-handling Native Client (NaCl) application in C++. The NaCl module waits for input events in the space inside the blue rectangle. When it receives mouse or keyboard events, they are interpreted and logged in the "NaCl messages:" field.

Note

To allow the NaCl plugin to capture keyboard input events, the user must focus the plugin area by clicking it. Therefore, the NaCl embed object must have non-zero dimensions and be visible to the user.
Mouse and mouse wheel events can be handled whenever the mouse pointer hovers over the NaCl plugin object, even when the plugin does not have focus.

Figure 1. Input events in C++ application

Figure 1. Input events in C++ application

For information on how to access the sample application cheat sheet and run the application, see Sample-based Tutorials.

To implement input event handling:

  1. In the main instance constructor, enable the events you want to capture:

    • The RequestInputEvents() function enables the mouse and mouse wheel input events.
    • The RequestFilteringInputEvents() function enables keyboard input events.
  2. To add a prefix to the logged messages, initialize the static Logger class, which is a wrapper for the PostMessage() function. The Logger class is implemented in the "logger.cc" file.

    explicit InputEventsInstance(PP_Instance instance)
        : pp::Instance(instance) {
      RequestInputEvents(PP_INPUTEVENT_CLASS_MOUSE | PP_INPUTEVENT_CLASS_WHEEL);
      RequestFilteringInputEvents(PP_INPUTEVENT_CLASS_KEYBOARD);
      Logger::InitializeInstance(this);
    }
    
  3. To react to specific input events, overload the HandleInputEvent() function.
    The HandleInputEvent() function takes a const reference to pp::InputEvent as a parameter. To extract the event type, use the GetType() function on it. This returns an enumeration value that can be used as a parameter for the switch expression. You can handle as many input event types as you want.
    In each case statement, the pp::InputEvent object is casted to the class that corresponds to its type. For example, for the PP_INPUTEVENT_TYPE_KEYDOWN event, the pp::InputEvent object is casted to pp::KeyboardInputEvent. This allows you to call event-specific functions that can provide additional information, such as the mouse position or the clicked key code.
    For each event, interpret the input data, create a reply message describing the event, and send the message to the browser using the Logger class:

      virtual bool HandleInputEvent(const pp::InputEvent& event) {
        switch (event.GetType()) {
          case PP_INPUTEVENT_TYPE_UNDEFINED: break;
          // Mouse button pressed while hovering over the embed element
          case PP_INPUTEVENT_TYPE_MOUSEDOWN: {
            pp::MouseInputEvent mouse_event(event);
            switch (mouse_event.GetButton()) {
              case PP_INPUTEVENT_MOUSEBUTTON_LEFT:
                Logger::Log("Left mouse button down");
                break;
              case PP_INPUTEVENT_MOUSEBUTTON_MIDDLE:
                Logger::Log("Middle mouse button down");
                break;
              case PP_INPUTEVENT_MOUSEBUTTON_RIGHT:
                Logger::Log("Right mouse button down");
                break;
              default:
                break;
            }
          } break;
          // Mouse button released while embed element has focus 
          case PP_INPUTEVENT_TYPE_MOUSEUP:  {
            pp::MouseInputEvent mouse_event(event);
            switch (mouse_event.GetButton()) {
              case PP_INPUTEVENT_MOUSEBUTTON_LEFT:
                Logger::Log("Left mouse button up");
                break;
              case PP_INPUTEVENT_MOUSEBUTTON_MIDDLE:
                Logger::Log("Middle mouse button up");
                break;
              case PP_INPUTEVENT_MOUSEBUTTON_RIGHT:
                Logger::Log("Right mouse button up");
                break;
              default:
                break;
            }
          } break;
          // Left mouse button is pressed while hovering over the embed element, or while the embed element has focus 
          case PP_INPUTEVENT_TYPE_MOUSEMOVE: {
            pp::MouseInputEvent mouse_event(event);
            pp::Point pos = mouse_event.GetPosition();
            Logger::Log("Mouse moved to position: (%d, %d)", pos.x(), pos.y());
            } break;
          case PP_INPUTEVENT_TYPE_MOUSEENTER: break;
          case PP_INPUTEVENT_TYPE_MOUSELEAVE: break;
          case PP_INPUTEVENT_TYPE_CONTEXTMENU: break;
          // Mouse wheel rotated while hovering over the embed element
          case PP_INPUTEVENT_TYPE_WHEEL: {
            pp::WheelInputEvent wheel_event(event);
            pp::FloatPoint delta = wheel_event.GetDelta();
            Logger::Log("Wheel move delta: (%f, %f)", delta.x(), delta.y());
            } break;
          case PP_INPUTEVENT_TYPE_RAWKEYDOWN: break;
          // Keyboard key is pressed while embed element has focus
          case PP_INPUTEVENT_TYPE_KEYDOWN: {
            pp::KeyboardInputEvent key_event(event);
            Logger::Log("Keyboard stroke code: %u", key_event.GetKeyCode());
            } break;
          case PP_INPUTEVENT_TYPE_KEYUP: break;
          // Keyboard key representing a printable character is pressed while embed element has focus
          case PP_INPUTEVENT_TYPE_CHAR: {
            pp::KeyboardInputEvent key_event(event);
            pp::Var key_name = key_event.GetCharacterText();
            Logger::Log("Keyboard stroke char: \"%s\"", key_name.AsString().c_str());
            } break;
          default: break;
        }
        return true;
      }
    
  4. To show the log messages on the Web page, in the JavaScript application component, receive the NaCl messages:

    function handleNaclMessage(message_event) {
      var message = message_event.data;
      if (printIfLog(message)) { 
        return;   // This was a log or error message; finish handling
      }
    }
    
  5. Check whether the message starts with any of the predefined prefixes. If it does, append it to the "NaCl messages" HTML object on the Web page:

    function printIfLog(message) {
      if ((typeof message == "string") && (uses_logging == true) && 
      (startsWith(message, kLogPrefix) || startsWith(message, kErrorPrefix)
      || startsWith(message, kDebugPrefix))) {
        logs.value += message;
        logs.scrollTop = logs.scrollHeight;
        return true;
      }
      return false;
    }