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