top

Input Events in C++

This tutorial performs a step by step decomposition of NaCl input events demo application in C++.

Introduction

This article will discuss the structure of the most basic NaCl application written in the C programming language. The source code can be found in Tizen Studio or you can click here. The downloaded zip file can be imported into Tizen Studio or extracted and compiled using a separately downloaded NaCl toolchain. Build instructions are available in the demo package.

Important

Please note that you need at least pepper_42 toolchain version to compile this sample application.

We recommend to go through this tutorial along with analyzing the downloaded source code. This decomposition will only focus on the native part of the NaCl application, the web part (.html and .js code) will be omitted because it’s trivial and almost identical for every demo presented on this website. If you want to learn more about NaCl or how to implement a basic NaCl application from scratch, read Getting Started with NaCl and How to Create a NaCl App. We also suggest to see Hello World in C++.

There is also a cheat sheet for this demo available in Tizen Studio that explains the application functionality by highlighting most important code fragments. Cheat sheets are available from the Help menu.



The application is a simple input handling application. It waits for input events from NaCl plugin embedded on the webpage and upon receiving one interprets it and logs event data on the webpage.

Code Analysis

The first thing that needs to be done in order to implement input events handling is enabling those events in the main application instance. It happens in the constructor of InputEventsInstance (which inherits from pp::Instance) by calling RequestInputEvents() and RequestFilteringInputEvents() functions.

  • RequestInputEvents() function will enable the mouse and mouse wheel input events.
  • RequestFilteringInputEvents() function will enable keyboard input events.
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);
  }

Additional thing that is done in the constructor is the initialization of the static Logger class. This class is a wrapper for the PostMessage() function that adds a prefix for every logged message. It’s source code can be found in the demo folder. It is used to manage log messages.

Now, for every input event that we requested for, the HandleInputEvent method will be called. In order to be able to handle these input events we need to overload that method. This is shown below:

/**
   * This method is called when input events are captured in nacl_module instance on webpage.
   * @see https://developer.chrome.com/native-client/devguide/coding/view-focus-input-events
   */
  virtual bool HandleInputEvent(const pp::InputEvent& event) {
    switch (event.GetType()) {
      case PP_INPUTEVENT_TYPE_UNDEFINED: break;
      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;
      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;
      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;
      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;
      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;
      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;
  }

Please note that HandleInputEvent takes const reference to pp::InputEvent as an argument. We use this argument to extract the event type by calling GetType() on it. This returns an enumeration which is used as an argument for the switch expression. You can handle many different input event types.

For a complete list of input events please refer to Google’s input_event.h documentation.

In this example we handled following events:

  • PP_INPUTEVENT_TYPE_MOUSEDOWN - emitted when any of the mouse buttons is pressed while it’s hovering above the plugin embed element on the webpage.
  • PP_INPUTEVENT_TYPE_MOUSEUP - emitted when any of the mouse button is released while plugin embed element has focus.
  • PP_INPUTEVENT_TYPE_MOUSEMOVE - emitted when mouse is hovering above the plugin embed element on the webpage or while plugin embed element has focus and the left mouse button is pressed down.
  • PP_INPUTEVENT_TYPE_WHEEL - emitted when mouse wheel is rotated while mouse pointer is hovering above the plugin embed element on the webpage.
  • PP_INPUTEVENT_TYPE_KEYDOWN - emitted when any keyboard key is pressed down while plugin embed element has focus.
  • PP_INPUTEVENT_TYPE_CHAR - emitted when keyboard key representing printable character is pressed down while plugin embed element has focus.
    In each separate case statement pp::InputEvent object is casted to the class that corresponds to its type, e.g. when the event is PP_INPUTEVENT_TYPE_KEYDOWN the pp::InputEvent object is casted to pp::KeyboardInputEvent. It allows calling event specific methods that provide information about mouse position, clicked key code etc. For each event, data is interpreted and a reply message is created that describes the data of the event in a readable way. That message is then sent to the browser via Logger class and then is handled by a dedicated JavaScript function. Below is the code sample:
/**
 * This function is called when a message from NaCl arrives.
 */
function handleNaclMessage(message_event) {
  var message = message_event.data;
  if (printIfLog(message)) { 
    return;   // this was a log or error message, so we can finish this handling
  }
}

/**
 * Print message in the logs area if it has a specific prefix (defined in 
 * kLogPrefix or kErrorPrefix).
 * @param message - a string message
 * @returns true - if this message was a log or error message. 
 */
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;
}

The printfIfLog() function checks whether a message starts with a predefined prefix. If it does, it is appended to the HTML object on the web page that is responsible for displaying log messages.

Note

Please note that in order to allow the NaCl plugin to grab the keyboard input events the user must click the plugin area to give it focus. This implies that the NaCl embed object must have non zero dimensions and be visible for the user. Mouse and mouse wheel events are handled only when the mouse pointer hovers over the NaCl plugin object, even when the plugin doesn’t have focus.

Summary

This application can be compiled using Tizen Studio or manually with the make program. The application compiled manually can be run in Google Chrome. To run it on Smart TV Emulator you have to build it with Tizen Studio.

After a successful load you should be presented with a webpage that looks like the one below and displays events when interacting with space inside of the blue rectangle.