Home | History | Annotate | Download | only in input_event
      1 // Copyright (c) 2012 The Chromium Authors. All rights reserved.
      2 // Use of this source code is governed by a BSD-style license that can be
      3 // found in the LICENSE file.
      4 
      5 // C headers
      6 #include <cassert>
      7 #include <cstdio>
      8 
      9 // C++ headers
     10 #include <sstream>
     11 #include <string>
     12 
     13 // PPAPI headers
     14 #include "ppapi/cpp/completion_callback.h"
     15 #include "ppapi/cpp/input_event.h"
     16 #include "ppapi/cpp/instance.h"
     17 #include "ppapi/cpp/module.h"
     18 #include "ppapi/cpp/point.h"
     19 #include "ppapi/cpp/var.h"
     20 #include "ppapi/utility/completion_callback_factory.h"
     21 
     22 #include "custom_events.h"
     23 #include "shared_queue.h"
     24 
     25 #ifdef PostMessage
     26 #undef PostMessage
     27 #endif
     28 
     29 const char* const kDidChangeView = "DidChangeView\n";
     30 const char* const kHandleInputEvent = "DidHandleInputEvent\n";
     31 const char* const kDidChangeFocus = "DidChangeFocus\n";
     32 const char* const kHaveFocus = "HaveFocus\n";
     33 const char* const kDontHaveFocus = "DontHaveFocus\n";
     34 const char* const kCancelMessage = "CANCEL";
     35 
     36 // Convert a pepper inputevent modifier value into a
     37 // custom event modifier.
     38 unsigned int ConvertEventModifier(uint32_t pp_modifier) {
     39   unsigned int custom_modifier = 0;
     40   if (pp_modifier & PP_INPUTEVENT_MODIFIER_SHIFTKEY) {
     41     custom_modifier |= kShiftKeyModifier;
     42   }
     43   if (pp_modifier & PP_INPUTEVENT_MODIFIER_CONTROLKEY) {
     44     custom_modifier |= kControlKeyModifier;
     45   }
     46   if (pp_modifier & PP_INPUTEVENT_MODIFIER_ALTKEY) {
     47     custom_modifier |= kAltKeyModifier;
     48   }
     49   if (pp_modifier & PP_INPUTEVENT_MODIFIER_METAKEY) {
     50     custom_modifier |= kMetaKeyModifer;
     51   }
     52   if (pp_modifier & PP_INPUTEVENT_MODIFIER_ISKEYPAD) {
     53     custom_modifier |= kKeyPadModifier;
     54   }
     55   if (pp_modifier & PP_INPUTEVENT_MODIFIER_ISAUTOREPEAT) {
     56     custom_modifier |= kAutoRepeatModifier;
     57   }
     58   if (pp_modifier & PP_INPUTEVENT_MODIFIER_LEFTBUTTONDOWN) {
     59     custom_modifier |= kLeftButtonModifier;
     60   }
     61   if (pp_modifier & PP_INPUTEVENT_MODIFIER_MIDDLEBUTTONDOWN) {
     62     custom_modifier |= kMiddleButtonModifier;
     63   }
     64   if (pp_modifier & PP_INPUTEVENT_MODIFIER_RIGHTBUTTONDOWN) {
     65     custom_modifier |= kRightButtonModifier;
     66   }
     67   if (pp_modifier & PP_INPUTEVENT_MODIFIER_CAPSLOCKKEY) {
     68     custom_modifier |= kCapsLockModifier;
     69   }
     70   if (pp_modifier & PP_INPUTEVENT_MODIFIER_NUMLOCKKEY) {
     71     custom_modifier |= kNumLockModifier;
     72   }
     73   return custom_modifier;
     74 }
     75 
     76 class InputEventInstance : public pp::Instance {
     77  public:
     78   explicit InputEventInstance(PP_Instance instance)
     79       : pp::Instance(instance), event_thread_(NULL), callback_factory_(this) {
     80     RequestInputEvents(PP_INPUTEVENT_CLASS_MOUSE | PP_INPUTEVENT_CLASS_WHEEL |
     81                        PP_INPUTEVENT_CLASS_TOUCH);
     82     RequestFilteringInputEvents(PP_INPUTEVENT_CLASS_KEYBOARD);
     83   }
     84 
     85   // Not guaranteed to be called in Pepper, but a good idea to cancel the
     86   // queue and signal to workers to die if it is called.
     87   virtual ~InputEventInstance() { CancelQueueAndWaitForWorker(); }
     88 
     89   // Create the 'worker thread'.
     90   bool Init(uint32_t argc, const char* argn[], const char* argv[]) {
     91     event_thread_ = new pthread_t;
     92     pthread_create(event_thread_, NULL, ProcessEventOnWorkerThread, this);
     93     return true;
     94   }
     95 
     96   /// Clicking outside of the instance's bounding box
     97   /// will create a DidChangeFocus event (the NaCl instance is
     98   /// out of focus). Clicking back inside the instance's
     99   /// bounding box will create another DidChangeFocus event
    100   /// (the NaCl instance is back in focus). The default is
    101   /// that the instance is out of focus.
    102   void DidChangeFocus(bool focus) {
    103     PostMessage(pp::Var(kDidChangeFocus));
    104     if (focus == true) {
    105       PostMessage(pp::Var(kHaveFocus));
    106     } else {
    107       PostMessage(pp::Var(kDontHaveFocus));
    108     }
    109   }
    110 
    111   /// Scrolling the mouse wheel causes a DidChangeView event.
    112   void DidChangeView(const pp::View& view) {
    113     PostMessage(pp::Var(kDidChangeView));
    114   }
    115 
    116   virtual void HandleMessage(const pp::Var& var_message) {
    117     std::string message = var_message.AsString();
    118     if (kCancelMessage == message) {
    119       std::string reply =
    120           "Received cancel : only Focus events will be "
    121           "displayed. Worker thread for mouse/wheel/keyboard will exit.";
    122       PostMessage(pp::Var(reply));
    123       printf("Calling cancel queue\n");
    124       CancelQueueAndWaitForWorker();
    125     }
    126   }
    127 
    128   // HandleInputEvent operates on the main Pepper thread.  Here we
    129   // illustrate copying the Pepper input event to our own custom event type.
    130   // Since we need to use Pepper API calls to convert it, we must do the
    131   // conversion on the main thread.  Once we have converted it to our own
    132   // event type, we push that into a thread-safe queue and quickly return.
    133   // The worker thread can process the custom event and do whatever
    134   // (possibly slow) things it wants to do without making the browser
    135   // become unresponsive.
    136   // We dynamically allocate a sub-class of our custom event (Event)
    137   // so that the queue can contain an Event*.
    138   virtual bool HandleInputEvent(const pp::InputEvent& event) {
    139     Event* event_ptr = NULL;
    140     switch (event.GetType()) {
    141       case PP_INPUTEVENT_TYPE_IME_COMPOSITION_START:
    142       case PP_INPUTEVENT_TYPE_IME_COMPOSITION_UPDATE:
    143       case PP_INPUTEVENT_TYPE_IME_COMPOSITION_END:
    144       case PP_INPUTEVENT_TYPE_IME_TEXT:
    145       // these cases are not handled...fall through below...
    146       case PP_INPUTEVENT_TYPE_UNDEFINED:
    147         break;
    148       case PP_INPUTEVENT_TYPE_MOUSEDOWN:
    149       case PP_INPUTEVENT_TYPE_MOUSEUP:
    150       case PP_INPUTEVENT_TYPE_MOUSEMOVE:
    151       case PP_INPUTEVENT_TYPE_MOUSEENTER:
    152       case PP_INPUTEVENT_TYPE_MOUSELEAVE:
    153       case PP_INPUTEVENT_TYPE_CONTEXTMENU: {
    154         pp::MouseInputEvent mouse_event(event);
    155         PP_InputEvent_MouseButton pp_button = mouse_event.GetButton();
    156         MouseEvent::MouseButton mouse_button = MouseEvent::kNone;
    157         switch (pp_button) {
    158           case PP_INPUTEVENT_MOUSEBUTTON_NONE:
    159             mouse_button = MouseEvent::kNone;
    160             break;
    161           case PP_INPUTEVENT_MOUSEBUTTON_LEFT:
    162             mouse_button = MouseEvent::kLeft;
    163             break;
    164           case PP_INPUTEVENT_MOUSEBUTTON_MIDDLE:
    165             mouse_button = MouseEvent::kMiddle;
    166             break;
    167           case PP_INPUTEVENT_MOUSEBUTTON_RIGHT:
    168             mouse_button = MouseEvent::kRight;
    169             break;
    170         }
    171         event_ptr =
    172             new MouseEvent(ConvertEventModifier(mouse_event.GetModifiers()),
    173                            mouse_button,
    174                            mouse_event.GetPosition().x(),
    175                            mouse_event.GetPosition().y(),
    176                            mouse_event.GetClickCount(),
    177                            mouse_event.GetTimeStamp(),
    178                            event.GetType() == PP_INPUTEVENT_TYPE_CONTEXTMENU);
    179       } break;
    180       case PP_INPUTEVENT_TYPE_WHEEL: {
    181         pp::WheelInputEvent wheel_event(event);
    182         event_ptr =
    183             new WheelEvent(ConvertEventModifier(wheel_event.GetModifiers()),
    184                            wheel_event.GetDelta().x(),
    185                            wheel_event.GetDelta().y(),
    186                            wheel_event.GetTicks().x(),
    187                            wheel_event.GetTicks().y(),
    188                            wheel_event.GetScrollByPage(),
    189                            wheel_event.GetTimeStamp());
    190       } break;
    191       case PP_INPUTEVENT_TYPE_RAWKEYDOWN:
    192       case PP_INPUTEVENT_TYPE_KEYDOWN:
    193       case PP_INPUTEVENT_TYPE_KEYUP:
    194       case PP_INPUTEVENT_TYPE_CHAR: {
    195         pp::KeyboardInputEvent key_event(event);
    196         event_ptr = new KeyEvent(ConvertEventModifier(key_event.GetModifiers()),
    197                                  key_event.GetKeyCode(),
    198                                  key_event.GetTimeStamp(),
    199                                  key_event.GetCharacterText().DebugString());
    200       } break;
    201       case PP_INPUTEVENT_TYPE_TOUCHSTART:
    202       case PP_INPUTEVENT_TYPE_TOUCHMOVE:
    203       case PP_INPUTEVENT_TYPE_TOUCHEND:
    204       case PP_INPUTEVENT_TYPE_TOUCHCANCEL: {
    205         pp::TouchInputEvent touch_event(event);
    206 
    207         TouchEvent::Kind touch_kind = TouchEvent::kNone;
    208         if (event.GetType() == PP_INPUTEVENT_TYPE_TOUCHSTART)
    209           touch_kind = TouchEvent::kStart;
    210         else if (event.GetType() == PP_INPUTEVENT_TYPE_TOUCHMOVE)
    211           touch_kind = TouchEvent::kMove;
    212         else if (event.GetType() == PP_INPUTEVENT_TYPE_TOUCHEND)
    213           touch_kind = TouchEvent::kEnd;
    214         else if (event.GetType() == PP_INPUTEVENT_TYPE_TOUCHCANCEL)
    215           touch_kind = TouchEvent::kCancel;
    216 
    217         TouchEvent* touch_event_ptr =
    218             new TouchEvent(ConvertEventModifier(touch_event.GetModifiers()),
    219                            touch_kind,
    220                            touch_event.GetTimeStamp());
    221         event_ptr = touch_event_ptr;
    222 
    223         uint32_t touch_count =
    224             touch_event.GetTouchCount(PP_TOUCHLIST_TYPE_CHANGEDTOUCHES);
    225         for (uint32_t i = 0; i < touch_count; ++i) {
    226           pp::TouchPoint point =
    227               touch_event.GetTouchByIndex(PP_TOUCHLIST_TYPE_CHANGEDTOUCHES, i);
    228           touch_event_ptr->AddTouch(point.id(),
    229                                     point.position().x(),
    230                                     point.position().y(),
    231                                     point.radii().x(),
    232                                     point.radii().y(),
    233                                     point.rotation_angle(),
    234                                     point.pressure());
    235         }
    236       } break;
    237       default: {
    238         // For any unhandled events, send a message to the browser
    239         // so that the user is aware of these and can investigate.
    240         std::stringstream oss;
    241         oss << "Default (unhandled) event, type=" << event.GetType();
    242         PostMessage(oss.str());
    243       } break;
    244     }
    245     event_queue_.Push(event_ptr);
    246     return true;
    247   }
    248 
    249   // Return an event from the thread-safe queue, waiting for a new event
    250   // to occur if the queue is empty.  Set |was_queue_cancelled| to indicate
    251   // whether the queue was cancelled.  If it was cancelled, then the
    252   // Event* will be NULL.
    253   const Event* GetEventFromQueue(bool* was_queue_cancelled) {
    254     Event* event = NULL;
    255     QueueGetResult result = event_queue_.GetItem(&event, kWait);
    256     if (result == kQueueWasCancelled) {
    257       *was_queue_cancelled = true;
    258       return NULL;
    259     }
    260     *was_queue_cancelled = false;
    261     return event;
    262   }
    263 
    264   // This method is called from the worker thread using CallOnMainThread.
    265   // It is not static, and allows PostMessage to be called.
    266   void* PostStringToBrowser(int32_t result, std::string data_to_send) {
    267     PostMessage(pp::Var(data_to_send));
    268     return 0;
    269   }
    270 
    271   // |ProcessEventOnWorkerThread| is a static method that is run
    272   // by a thread.  It pulls events from the queue, converts
    273   // them to a string, and calls CallOnMainThread so that
    274   // PostStringToBrowser will be called, which will call PostMessage
    275   // to send the converted event back to the browser.
    276   static void* ProcessEventOnWorkerThread(void* param) {
    277     InputEventInstance* event_instance =
    278         static_cast<InputEventInstance*>(param);
    279     while (1) {
    280       // Grab a generic Event* so that down below we can call
    281       // event->ToString(), which will use the correct virtual method
    282       // to convert the event to a string.  This 'conversion' is
    283       // the 'work' being done on the worker thread.  In an application
    284       // the work might involve changing application state based on
    285       // the event that was processed.
    286       bool queue_cancelled;
    287       const Event* event = event_instance->GetEventFromQueue(&queue_cancelled);
    288       if (queue_cancelled) {
    289         printf("Queue was cancelled, worker thread exiting\n");
    290         pthread_exit(NULL);
    291       }
    292       std::string event_string = event->ToString();
    293       delete event;
    294       // Need to invoke callback on main thread.
    295       pp::Module::Get()->core()->CallOnMainThread(
    296           0,
    297           event_instance->callback_factory().NewCallback(
    298               &InputEventInstance::PostStringToBrowser, event_string));
    299     }  // end of while loop.
    300     return 0;
    301   }
    302 
    303   // Return the callback factory.
    304   // Allows the static method (ProcessEventOnWorkerThread) to use
    305   // the |event_instance| pointer to get the factory.
    306   pp::CompletionCallbackFactory<InputEventInstance>& callback_factory() {
    307     return callback_factory_;
    308   }
    309 
    310  private:
    311   // Cancels the queue (which will cause the thread to exit).
    312   // Wait for the thread.  Set |event_thread_| to NULL so we only
    313   // execute the body once.
    314   void CancelQueueAndWaitForWorker() {
    315     if (event_thread_) {
    316       event_queue_.CancelQueue();
    317       pthread_join(*event_thread_, NULL);
    318       delete event_thread_;
    319       event_thread_ = NULL;
    320     }
    321   }
    322   pthread_t* event_thread_;
    323   LockingQueue<Event*> event_queue_;
    324   pp::CompletionCallbackFactory<InputEventInstance> callback_factory_;
    325 };
    326 
    327 class InputEventModule : public pp::Module {
    328  public:
    329   InputEventModule() : pp::Module() {}
    330   virtual ~InputEventModule() {}
    331 
    332   virtual pp::Instance* CreateInstance(PP_Instance instance) {
    333     return new InputEventInstance(instance);
    334   }
    335 };
    336 
    337 namespace pp {
    338 Module* CreateModule() { return new InputEventModule(); }
    339 }
    340