diff options
author | binji@chromium.org <binji@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2012-12-19 18:26:40 +0000 |
---|---|---|
committer | binji@chromium.org <binji@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2012-12-19 18:26:40 +0000 |
commit | cee673111dd5ee539290521bf1d869c62c2e2189 (patch) | |
tree | ccb5d584ed0823b4ac3a2eb0b421780a7c38d7ac /native_client_sdk | |
parent | 0795982840095652cb7e0e64b227dfff861ebb77 (diff) | |
download | chromium_src-cee673111dd5ee539290521bf1d869c62c2e2189.zip chromium_src-cee673111dd5ee539290521bf1d869c62c2e2189.tar.gz chromium_src-cee673111dd5ee539290521bf1d869c62c2e2189.tar.bz2 |
[NaCl SDK] Make input_events do what mt_input_events was doing.
mt_input_events is still useful -- and it is not significantly more difficult
than input_events. But it is a lot of duplicated code. So let's just keep
mt_input_events and rename it input_events.
BUG=none
R=noelallen@chromium.org
NOTRY=true
Review URL: https://chromiumcodereview.appspot.com/11617010
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@173955 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'native_client_sdk')
11 files changed, 278 insertions, 643 deletions
diff --git a/native_client_sdk/src/examples/multithreaded_input_events/custom_events.cc b/native_client_sdk/src/examples/input_events/custom_events.cc index 9e22808..9e22808 100644 --- a/native_client_sdk/src/examples/multithreaded_input_events/custom_events.cc +++ b/native_client_sdk/src/examples/input_events/custom_events.cc diff --git a/native_client_sdk/src/examples/multithreaded_input_events/custom_events.h b/native_client_sdk/src/examples/input_events/custom_events.h index 2f7b10c..2f7b10c 100644 --- a/native_client_sdk/src/examples/multithreaded_input_events/custom_events.h +++ b/native_client_sdk/src/examples/input_events/custom_events.h diff --git a/native_client_sdk/src/examples/input_events/example.dsc b/native_client_sdk/src/examples/input_events/example.dsc index 69b3800..759d9e6a 100644 --- a/native_client_sdk/src/examples/input_events/example.dsc +++ b/native_client_sdk/src/examples/input_events/example.dsc @@ -1,10 +1,15 @@ { - 'TOOLS': ['newlib', 'glibc', 'pnacl', 'win', 'linux'], + 'TOOLS': ['newlib', 'glibc', 'pnacl'], 'TARGETS': [ { 'NAME' : 'input_events', 'TYPE' : 'main', - 'SOURCES' : ['input_events.cc'], + 'SOURCES' : [ + 'custom_events.cc', + 'custom_events.h', + 'input_events.cc', + 'shared_queue.h', + ], 'LIBS': ['ppapi_cpp', 'ppapi', 'pthread'] } ], @@ -12,12 +17,13 @@ 'DEST': 'examples', 'NAME': 'input_events', 'TITLE': 'Input Events', - 'DESC': """ -The Input Events example demonstrates how to handle events triggered by -the user. This example allows a user to interact with a square representing a -module instance. Events are displayed on the screen as the user clicks, -scrolls, types, inside or outside of the square.""", - 'FOCUS': 'Keyboard and mouse input, view change, and focus events.', + 'DESC': """The Input Events example shows how to handle input events in a +multi-threaded application. The main thread converts input events to +non-pepper events and puts them on a queue. The worker thread pulls them off of +the queue, converts them to a string, and then uses CallOnMainThread so that +PostMessage can be send the result of the worker thread to the browser.""", + 'FOCUS': """Multi-threading, keyboard and mouse input, view change, and focus +events.""", 'GROUP': 'API', } diff --git a/native_client_sdk/src/examples/input_events/example.js b/native_client_sdk/src/examples/input_events/example.js index 6342a72..eb335f0 100644 --- a/native_client_sdk/src/examples/input_events/example.js +++ b/native_client_sdk/src/examples/input_events/example.js @@ -11,6 +11,11 @@ function moduleDidLoad() { } // Called by the common.js module. +function attachListeners() { + document.getElementById('killButton').addEventListener('click', cancelQueue); +} + +// Called by the common.js module. function handleMessage(message) { // Show last |kMaxArraySize| events in html. messageArray.push(message.data); @@ -22,3 +27,11 @@ function handleMessage(message) { // Print event to console. console.log(message.data); } + +function cancelQueue() { + if (common.naclModule == null) { + console.log('Module is not loaded.'); + return; + } + common.naclModule.postMessage('CANCEL'); +} diff --git a/native_client_sdk/src/examples/input_events/index.html b/native_client_sdk/src/examples/input_events/index.html index 17077f7..e0fa20c 100644 --- a/native_client_sdk/src/examples/input_events/index.html +++ b/native_client_sdk/src/examples/input_events/index.html @@ -6,6 +6,7 @@ Use of this source code is governed by a BSD-style license that can be found in the LICENSE file. --> <head> + <title><TITLE></title> <meta http-equiv="Pragma" content="no-cache"> <meta http-equiv="Expires" content="-1"> <title><TITLE></title> @@ -15,12 +16,21 @@ found in the LICENSE file. <body data-name="<NAME>" data-tc="<tc>" data-path="<path>"> <h1><TITLE></h1> <h2>Status: <code id="statusField">NO-STATUS</code></h2> + <button id="killButton">Kill worker thread and queue</button> <p>This example demonstrates handling of input events in PPAPI.</p> - <p>Each time an input event happens in the context of the gray box, the - embedded NaCl module posts a message describing the event back to JavaScript, - which prints a message to the JavaScript console in Chrome and to a string on - the page.</p> + <p>Each time an input event happens in the context of the gray box, the main + thread in the embedded NaCl module converts it from a Pepper input event to a + non-Pepper event and puts this custom event onto a shared queue. A worker + thread in the embedded NaCl module reads events from the queue, and converts + each event to a string and then uses CallOnMainThread to post a message + describing the event back to JavaScript, which prints a message to the + JavaScript console in Chrome and to a string on the page.</p> + <p>If you press the 'Kill worker thread and queue' button, then the main + thread (which puts events on the queue) will call CancelQueue, indicating + that the main thread will no longer put events on the queue. When the worker + sees that the shared queue has been cancelled, the worker thread will + terminate.</p> <!-- The NaCl plugin will be embedded inside the element with id "listener". See common.js.--> diff --git a/native_client_sdk/src/examples/input_events/input_events.cc b/native_client_sdk/src/examples/input_events/input_events.cc index a84822b..5ff49ba 100644 --- a/native_client_sdk/src/examples/input_events/input_events.cc +++ b/native_client_sdk/src/examples/input_events/input_events.cc @@ -10,92 +10,88 @@ #include <sstream> #include <string> -// NaCl +// PPAPI headers +#include "ppapi/cpp/completion_callback.h" #include "ppapi/cpp/input_event.h" #include "ppapi/cpp/instance.h" #include "ppapi/cpp/module.h" #include "ppapi/cpp/point.h" #include "ppapi/cpp/var.h" +#include "ppapi/utility/completion_callback_factory.h" -namespace { +#include "custom_events.h" +#include "shared_queue.h" + +namespace event_queue { const char* const kDidChangeView = "DidChangeView"; const char* const kHandleInputEvent = "DidHandleInputEvent"; const char* const kDidChangeFocus = "DidChangeFocus"; const char* const kHaveFocus = "HaveFocus"; const char* const kDontHaveFocus = "DontHaveFocus"; +const char* const kCancelMessage = "CANCEL"; -// Convert a given modifier to a descriptive string. Note that the actual -// declared type of modifier in each of the event classes is uint32_t, but it is -// expected to be interpreted as a bitfield of 'or'ed PP_InputEvent_Modifier -// values. -std::string ModifierToString(uint32_t modifier) { - std::string s; - if (modifier & PP_INPUTEVENT_MODIFIER_SHIFTKEY) { - s += "shift "; - } - if (modifier & PP_INPUTEVENT_MODIFIER_CONTROLKEY) { - s += "ctrl "; +// Convert a pepper inputevent modifier value into a +// custom event modifier. +unsigned int ConvertEventModifier(uint32_t pp_modifier) { + unsigned int custom_modifier = 0; + if (pp_modifier & PP_INPUTEVENT_MODIFIER_SHIFTKEY) { + custom_modifier |= kShiftKeyModifier; } - if (modifier & PP_INPUTEVENT_MODIFIER_ALTKEY) { - s += "alt "; + if (pp_modifier & PP_INPUTEVENT_MODIFIER_CONTROLKEY) { + custom_modifier |= kControlKeyModifier; } - if (modifier & PP_INPUTEVENT_MODIFIER_METAKEY) { - s += "meta "; + if (pp_modifier & PP_INPUTEVENT_MODIFIER_ALTKEY) { + custom_modifier |= kAltKeyModifier; } - if (modifier & PP_INPUTEVENT_MODIFIER_ISKEYPAD) { - s += "keypad "; + if (pp_modifier & PP_INPUTEVENT_MODIFIER_METAKEY) { + custom_modifier |= kMetaKeyModifer; } - if (modifier & PP_INPUTEVENT_MODIFIER_ISAUTOREPEAT) { - s += "autorepeat "; + if (pp_modifier & PP_INPUTEVENT_MODIFIER_ISKEYPAD) { + custom_modifier |= kKeyPadModifier; } - if (modifier & PP_INPUTEVENT_MODIFIER_LEFTBUTTONDOWN) { - s += "left-button-down "; + if (pp_modifier & PP_INPUTEVENT_MODIFIER_ISAUTOREPEAT) { + custom_modifier |= kAutoRepeatModifier; } - if (modifier & PP_INPUTEVENT_MODIFIER_MIDDLEBUTTONDOWN) { - s += "middle-button-down "; + if (pp_modifier & PP_INPUTEVENT_MODIFIER_LEFTBUTTONDOWN) { + custom_modifier |= kLeftButtonModifier; } - if (modifier & PP_INPUTEVENT_MODIFIER_RIGHTBUTTONDOWN) { - s += "right-button-down "; + if (pp_modifier & PP_INPUTEVENT_MODIFIER_MIDDLEBUTTONDOWN) { + custom_modifier |= kMiddleButtonModifier; } - if (modifier & PP_INPUTEVENT_MODIFIER_CAPSLOCKKEY) { - s += "caps-lock "; + if (pp_modifier & PP_INPUTEVENT_MODIFIER_RIGHTBUTTONDOWN) { + custom_modifier |= kRightButtonModifier; } - if (modifier & PP_INPUTEVENT_MODIFIER_NUMLOCKKEY) { - s += "num-lock "; + if (pp_modifier & PP_INPUTEVENT_MODIFIER_CAPSLOCKKEY) { + custom_modifier |= kCapsLockModifier; } - return s; -} - -std::string MouseButtonToString(PP_InputEvent_MouseButton button) { - switch (button) { - case PP_INPUTEVENT_MOUSEBUTTON_NONE: - return "None"; - case PP_INPUTEVENT_MOUSEBUTTON_LEFT: - return "Left"; - case PP_INPUTEVENT_MOUSEBUTTON_MIDDLE: - return "Middle"; - case PP_INPUTEVENT_MOUSEBUTTON_RIGHT: - return "Right"; - default: - std::ostringstream stream; - stream << "Unrecognized (" - << static_cast<int32_t>(button) - << ")"; - return stream.str(); + if (pp_modifier & PP_INPUTEVENT_MODIFIER_NUMLOCKKEY) { + custom_modifier |= kNumLockModifier; } + return custom_modifier; } -} // namespace - class EventInstance : public pp::Instance { public: explicit EventInstance(PP_Instance instance) - : pp::Instance(instance) { + : pp::Instance(instance), + event_thread_(NULL), + callback_factory_(this) { RequestInputEvents(PP_INPUTEVENT_CLASS_MOUSE | PP_INPUTEVENT_CLASS_WHEEL | PP_INPUTEVENT_CLASS_TOUCH); RequestFilteringInputEvents(PP_INPUTEVENT_CLASS_KEYBOARD); } - virtual ~EventInstance() {} + + // Not guaranteed to be called in Pepper, but a good idea to cancel the + // queue and signal to workers to die if it is called. + virtual ~EventInstance() { + CancelQueueAndWaitForWorker(); + } + + // Create the 'worker thread'. + bool Init(uint32_t argc, const char* argn[], const char* argv[]) { + pthread_create(&event_thread_, NULL, ProcessEventOnWorkerThread, this); + return true; + } /// Clicking outside of the instance's bounding box /// will create a DidChangeFocus event (the NaCl instance is @@ -117,148 +113,219 @@ class EventInstance : public pp::Instance { PostMessage(pp::Var(kDidChangeView)); } - void GotKeyEvent(const pp::KeyboardInputEvent& key_event, - const std::string& kind) { - std::ostringstream stream; - stream << pp_instance() << ":" - << " Key event:" << kind - << " modifier:" << ModifierToString(key_event.GetModifiers()) - << " key_code:" << key_event.GetKeyCode() - << " time:" << key_event.GetTimeStamp() - << " text:" << key_event.GetCharacterText().DebugString() - << "\n"; - PostMessage(stream.str()); - } - - void GotMouseEvent(const pp::MouseInputEvent& mouse_event, - const std::string& kind) { - std::ostringstream stream; - stream << pp_instance() << ":" - << " Mouse event:" << kind - << " modifier:" << ModifierToString(mouse_event.GetModifiers()) - << " button:" << MouseButtonToString(mouse_event.GetButton()) - << " x:" << mouse_event.GetPosition().x() - << " y:" << mouse_event.GetPosition().y() - << " click_count:" << mouse_event.GetClickCount() - << " time:" << mouse_event.GetTimeStamp() - << "\n"; - PostMessage(stream.str()); - } - - void GotWheelEvent(const pp::WheelInputEvent& wheel_event) { - std::ostringstream stream; - stream << pp_instance() << ": Wheel event." - << " modifier:" << ModifierToString(wheel_event.GetModifiers()) - << " deltax:" << wheel_event.GetDelta().x() - << " deltay:" << wheel_event.GetDelta().y() - << " wheel_ticks_x:" << wheel_event.GetTicks().x() - << " wheel_ticks_y:"<< wheel_event.GetTicks().y() - << " scroll_by_page: " - << (wheel_event.GetScrollByPage() ? "true" : "false") - << "\n"; - PostMessage(stream.str()); - } - - void GotTouchEvent(const pp::TouchInputEvent& touch_event, - const std::string& kind) { - std::ostringstream stream; - stream << pp_instance() << ":" - << " Touch event:" << kind - << " modifier:" << ModifierToString(touch_event.GetModifiers()); - uint32_t touch_count = - touch_event.GetTouchCount(PP_TOUCHLIST_TYPE_CHANGEDTOUCHES); - for (uint32_t i = 0; i < touch_count; ++i) { - pp::TouchPoint point = - touch_event.GetTouchByIndex(PP_TOUCHLIST_TYPE_CHANGEDTOUCHES, i); - stream << " x[" << point.id() << "]:" << point.position().x(); - stream << " y[" << point.id() << "]:" << point.position().y(); - stream << " radius_x[" << point.id() << "]:" << point.radii().x(); - stream << " radius_y[" << point.id() << "]:" << point.radii().y(); - stream << " angle[" << point.id() << "]:" << point.rotation_angle(); - stream << " pressure[" << point.id() << "]:" << point.pressure(); + /// Called by the browser to handle the postMessage() call in Javascript. + /// Detects which method is being called from the message contents, and + /// calls the appropriate function. Posts the result back to the browser + /// asynchronously. + /// @param[in] var_message The message posted by the browser. The only + /// supported message is |kCancelMessage|. If we receive this, we + /// cancel the shared queue. + virtual void HandleMessage(const pp::Var& var_message) { + std::string message = var_message.AsString(); + if (kCancelMessage == message) { + std::string reply = "Received cancel : only Focus events will be " + "displayed. Worker thread for mouse/wheel/keyboard will exit."; + PostMessage(pp::Var(reply)); + printf("Calling cancel queue\n"); + CancelQueueAndWaitForWorker(); } - PostMessage(stream.str()); } - // Handle an incoming input event by switching on type and dispatching - // to the appropriate subtype handler. - // - // HandleInputEvent operates on the main Pepper thread. In large - // real-world applications, you'll want to create a separate thread - // that puts events in a queue and handles them independant of the main - // thread so as not to slow down the browser. There is an additional - // version of this example in the examples directory that demonstrates - // this best practice. + // HandleInputEvent operates on the main Pepper thread. Here we + // illustrate copying the Pepper input event to our own custom event type. + // Since we need to use Pepper API calls to convert it, we must do the + // conversion on the main thread. Once we have converted it to our own + // event type, we push that into a thread-safe queue and quickly return. + // The worker thread can process the custom event and do whatever + // (possibly slow) things it wants to do without making the browser + // become unresponsive. + // We dynamically allocate a sub-class of our custom event (Event) + // so that the queue can contain an Event*. virtual bool HandleInputEvent(const pp::InputEvent& event) { - PostMessage(pp::Var(kHandleInputEvent)); + Event* event_ptr = NULL; switch (event.GetType()) { + case PP_INPUTEVENT_TYPE_IME_COMPOSITION_START: + case PP_INPUTEVENT_TYPE_IME_COMPOSITION_UPDATE: + case PP_INPUTEVENT_TYPE_IME_COMPOSITION_END: + case PP_INPUTEVENT_TYPE_IME_TEXT: + // these cases are not handled...fall through below... case PP_INPUTEVENT_TYPE_UNDEFINED: break; case PP_INPUTEVENT_TYPE_MOUSEDOWN: - GotMouseEvent(pp::MouseInputEvent(event), "Down"); - break; case PP_INPUTEVENT_TYPE_MOUSEUP: - GotMouseEvent(pp::MouseInputEvent(event), "Up"); - break; case PP_INPUTEVENT_TYPE_MOUSEMOVE: - GotMouseEvent(pp::MouseInputEvent(event), "Move"); - break; case PP_INPUTEVENT_TYPE_MOUSEENTER: - GotMouseEvent(pp::MouseInputEvent(event), "Enter"); - break; case PP_INPUTEVENT_TYPE_MOUSELEAVE: - GotMouseEvent(pp::MouseInputEvent(event), "Leave"); + { + pp::MouseInputEvent mouse_event(event); + PP_InputEvent_MouseButton pp_button = mouse_event.GetButton(); + MouseEvent::MouseButton mouse_button = MouseEvent::kNone; + switch (pp_button) { + case PP_INPUTEVENT_MOUSEBUTTON_NONE: + mouse_button = MouseEvent::kNone; + break; + case PP_INPUTEVENT_MOUSEBUTTON_LEFT: + mouse_button = MouseEvent::kLeft; + break; + case PP_INPUTEVENT_MOUSEBUTTON_MIDDLE: + mouse_button = MouseEvent::kMiddle; + break; + case PP_INPUTEVENT_MOUSEBUTTON_RIGHT: + mouse_button = MouseEvent::kRight; + break; + } + event_ptr = new MouseEvent( + ConvertEventModifier(mouse_event.GetModifiers()), + mouse_button, mouse_event.GetPosition().x(), + mouse_event.GetPosition().y(), mouse_event.GetClickCount(), + mouse_event.GetTimeStamp()); + } break; case PP_INPUTEVENT_TYPE_WHEEL: - GotWheelEvent(pp::WheelInputEvent(event)); + { + pp::WheelInputEvent wheel_event(event); + event_ptr = new WheelEvent( + ConvertEventModifier(wheel_event.GetModifiers()), + wheel_event.GetDelta().x(), wheel_event.GetDelta().y(), + wheel_event.GetTicks().x(), wheel_event.GetTicks().y(), + wheel_event.GetScrollByPage(), wheel_event.GetTimeStamp()); + } break; case PP_INPUTEVENT_TYPE_RAWKEYDOWN: - GotKeyEvent(pp::KeyboardInputEvent(event), "RawKeyDown"); - break; case PP_INPUTEVENT_TYPE_KEYDOWN: - GotKeyEvent(pp::KeyboardInputEvent(event), "Down"); - break; case PP_INPUTEVENT_TYPE_KEYUP: - GotKeyEvent(pp::KeyboardInputEvent(event), "Up"); - break; case PP_INPUTEVENT_TYPE_CHAR: - GotKeyEvent(pp::KeyboardInputEvent(event), "Character"); - break; case PP_INPUTEVENT_TYPE_CONTEXTMENU: - GotKeyEvent(pp::KeyboardInputEvent(event), "Context"); - break; - // Note that if we receive an IME event we just send a message back - // to the browser to indicate we have received it. - case PP_INPUTEVENT_TYPE_IME_COMPOSITION_START: - PostMessage(pp::Var("PP_INPUTEVENT_TYPE_IME_COMPOSITION_START")); - break; - case PP_INPUTEVENT_TYPE_IME_COMPOSITION_UPDATE: - PostMessage(pp::Var("PP_INPUTEVENT_TYPE_IME_COMPOSITION_UPDATE")); - break; - case PP_INPUTEVENT_TYPE_IME_COMPOSITION_END: - PostMessage(pp::Var("PP_INPUTEVENT_TYPE_IME_COMPOSITION_END")); - break; - case PP_INPUTEVENT_TYPE_IME_TEXT: - PostMessage(pp::Var("PP_INPUTEVENT_TYPE_IME_COMPOSITION_TEXT")); + { + pp::KeyboardInputEvent key_event(event); + event_ptr = new KeyEvent( + ConvertEventModifier(key_event.GetModifiers()), + key_event.GetKeyCode(), key_event.GetTimeStamp(), + key_event.GetCharacterText().DebugString()); + } break; case PP_INPUTEVENT_TYPE_TOUCHSTART: - GotTouchEvent(pp::TouchInputEvent(event), "Start"); - break; case PP_INPUTEVENT_TYPE_TOUCHMOVE: - GotTouchEvent(pp::TouchInputEvent(event), "Move"); - break; case PP_INPUTEVENT_TYPE_TOUCHEND: - GotTouchEvent(pp::TouchInputEvent(event), "End"); - break; case PP_INPUTEVENT_TYPE_TOUCHCANCEL: - GotTouchEvent(pp::TouchInputEvent(event), "Cancel"); + { + pp::TouchInputEvent touch_event(event); + + TouchEvent::Kind touch_kind = TouchEvent::kNone; + if (event.GetType() == PP_INPUTEVENT_TYPE_TOUCHSTART) + touch_kind = TouchEvent::kStart; + else if (event.GetType() == PP_INPUTEVENT_TYPE_TOUCHMOVE) + touch_kind = TouchEvent::kMove; + else if (event.GetType() == PP_INPUTEVENT_TYPE_TOUCHEND) + touch_kind = TouchEvent::kEnd; + else if (event.GetType() == PP_INPUTEVENT_TYPE_TOUCHCANCEL) + touch_kind = TouchEvent::kCancel; + + TouchEvent* touch_event_ptr = new TouchEvent( + ConvertEventModifier(touch_event.GetModifiers()), + touch_kind, touch_event.GetTimeStamp()); + event_ptr = touch_event_ptr; + + uint32_t touch_count = + touch_event.GetTouchCount(PP_TOUCHLIST_TYPE_CHANGEDTOUCHES); + for (uint32_t i = 0; i < touch_count; ++i) { + pp::TouchPoint point = touch_event.GetTouchByIndex( + PP_TOUCHLIST_TYPE_CHANGEDTOUCHES, i); + touch_event_ptr->AddTouch(point.id(), point.position().x(), + point.position().y(), point.radii().x(), point.radii().y(), + point.rotation_angle(), point.pressure()); + } + } break; default: - assert(false); - return false; + { + // For any unhandled events, send a message to the browser + // so that the user is aware of these and can investigate. + std::stringstream oss; + oss << "Default (unhandled) event, type=" << event.GetType(); + PostMessage(oss.str()); + } + break; } + event_queue_.Push(event_ptr); return true; } + + // Return an event from the thread-safe queue, waiting for a new event + // to occur if the queue is empty. Set |was_queue_cancelled| to indicate + // whether the queue was cancelled. If it was cancelled, then the + // Event* will be NULL. + const Event* GetEventFromQueue(bool *was_queue_cancelled) { + Event* event = NULL; + QueueGetResult result = event_queue_.GetItem(&event, kWait); + if (result == kQueueWasCancelled) { + *was_queue_cancelled = true; + return NULL; + } + *was_queue_cancelled = false; + return event; + } + + // This method is called from the worker thread using CallOnMainThread. + // It is not static, and allows PostMessage to be called. + void* PostStringToBrowser(int32_t result, std::string data_to_send) { + PostMessage(pp::Var(data_to_send)); + return 0; + } + + // |ProcessEventOnWorkerThread| is a static method that is run + // by a thread. It pulls events from the queue, converts + // them to a string, and calls CallOnMainThread so that + // PostStringToBrowser will be called, which will call PostMessage + // to send the converted event back to the browser. + static void* ProcessEventOnWorkerThread(void* param) { + EventInstance* event_instance = static_cast<EventInstance*>(param); + while (1) { + // Grab a generic Event* so that down below we can call + // event->ToString(), which will use the correct virtual method + // to convert the event to a string. This 'conversion' is + // the 'work' being done on the worker thread. In an application + // the work might involve changing application state based on + // the event that was processed. + bool queue_cancelled; + const Event* event = event_instance->GetEventFromQueue(&queue_cancelled); + if (queue_cancelled) { + printf("Queue was cancelled, worker thread exiting\n"); + pthread_exit(NULL); + } + std::string event_string = event->ToString(); + delete event; + // Need to invoke callback on main thread. + pp::Module::Get()->core()->CallOnMainThread( + 0, + event_instance->callback_factory().NewCallback( + &EventInstance::PostStringToBrowser, + event_string)); + } // end of while loop. + return 0; + } + + // Return the callback factory. + // Allows the static method (ProcessEventOnWorkerThread) to use + // the |event_instance| pointer to get the factory. + pp::CompletionCallbackFactory<EventInstance>& callback_factory() { + return callback_factory_; + } + + private: + // Cancels the queue (which will cause the thread to exit). + // Wait for the thread. Set |event_thread_| to NULL so we only + // execute the body once. + void CancelQueueAndWaitForWorker() { + if (event_thread_) { + event_queue_.CancelQueue(); + pthread_join(event_thread_, NULL); + event_thread_ = NULL; + } + } + pthread_t event_thread_; + LockingQueue<Event*> event_queue_; + pp::CompletionCallbackFactory<EventInstance> callback_factory_; }; // The EventModule provides an implementation of pp::Module that creates @@ -274,11 +341,14 @@ class EventModule : public pp::Module { } }; +} // namespace + // Implement the required pp::CreateModule function that creates our specific // kind of Module (in this case, EventModule). This is part of the glue code // that makes our example accessible to ppapi. namespace pp { Module* CreateModule() { - return new EventModule(); + return new event_queue::EventModule(); } } + diff --git a/native_client_sdk/src/examples/multithreaded_input_events/shared_queue.h b/native_client_sdk/src/examples/input_events/shared_queue.h index 48b70b3..48b70b3 100644 --- a/native_client_sdk/src/examples/multithreaded_input_events/shared_queue.h +++ b/native_client_sdk/src/examples/input_events/shared_queue.h diff --git a/native_client_sdk/src/examples/multithreaded_input_events/example.dsc b/native_client_sdk/src/examples/multithreaded_input_events/example.dsc deleted file mode 100644 index 8b61418..0000000 --- a/native_client_sdk/src/examples/multithreaded_input_events/example.dsc +++ /dev/null @@ -1,30 +0,0 @@ -{ - 'TOOLS': ['newlib', 'glibc', 'pnacl'], - 'TARGETS': [ - { - 'NAME' : 'mt_input_events', - 'TYPE' : 'main', - 'SOURCES' : [ - 'custom_events.cc', - 'custom_events.h', - 'mt_input_events.cc', - 'shared_queue.h', - ], - 'LIBS': ['ppapi_cpp', 'ppapi', 'pthread'] - } - ], - 'DATA': ['example.js'], - 'DEST': 'examples', - 'NAME': 'mt_input_events', - 'TITLE': 'Multi-threaded Input Events', - 'DESC': """ -The Multithreaded Input Events example combines HTML, Javascript, -and C++ (the C++ is compiled to create a .nexe file). -The C++ shows how to handle input events in a multi-threaded application. -The main thread converts input events to non-pepper events and puts them on -a queue. The worker thread pulls them off of the queue, converts them to a -string, and then uses CallOnMainThread so that PostMessage can be send the -result of the worker thread to the browser.""", - 'INFO': 'Multithreaded event handling.' -} - diff --git a/native_client_sdk/src/examples/multithreaded_input_events/example.js b/native_client_sdk/src/examples/multithreaded_input_events/example.js deleted file mode 100644 index eb335f0..0000000 --- a/native_client_sdk/src/examples/multithreaded_input_events/example.js +++ /dev/null @@ -1,37 +0,0 @@ -// Copyright (c) 2012 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -var kMaxArraySize = 20; -var messageArray = new Array(); - -// Called by the common.js module. -function moduleDidLoad() { - common.naclModule.style.backgroundColor = 'gray'; -} - -// Called by the common.js module. -function attachListeners() { - document.getElementById('killButton').addEventListener('click', cancelQueue); -} - -// Called by the common.js module. -function handleMessage(message) { - // Show last |kMaxArraySize| events in html. - messageArray.push(message.data); - if (messageArray.length > kMaxArraySize) { - messageArray.shift(); - } - var newData = messageArray.join('<BR>'); - document.getElementById('eventString').innerHTML = newData; - // Print event to console. - console.log(message.data); -} - -function cancelQueue() { - if (common.naclModule == null) { - console.log('Module is not loaded.'); - return; - } - common.naclModule.postMessage('CANCEL'); -} diff --git a/native_client_sdk/src/examples/multithreaded_input_events/index.html b/native_client_sdk/src/examples/multithreaded_input_events/index.html deleted file mode 100644 index e0fa20c..0000000 --- a/native_client_sdk/src/examples/multithreaded_input_events/index.html +++ /dev/null @@ -1,43 +0,0 @@ -<!DOCTYPE html> -<html> -<!-- -Copyright (c) 2012 The Chromium Authors. All rights reserved. -Use of this source code is governed by a BSD-style license that can be -found in the LICENSE file. ---> -<head> - <title><TITLE></title> - <meta http-equiv="Pragma" content="no-cache"> - <meta http-equiv="Expires" content="-1"> - <title><TITLE></title> - <script type="text/javascript" src="common.js"></script> - <script type="text/javascript" src="example.js"></script> -</head> -<body data-name="<NAME>" data-tc="<tc>" data-path="<path>"> - <h1><TITLE></h1> - <h2>Status: <code id="statusField">NO-STATUS</code></h2> - <button id="killButton">Kill worker thread and queue</button> - - <p>This example demonstrates handling of input events in PPAPI.</p> - <p>Each time an input event happens in the context of the gray box, the main - thread in the embedded NaCl module converts it from a Pepper input event to a - non-Pepper event and puts this custom event onto a shared queue. A worker - thread in the embedded NaCl module reads events from the queue, and converts - each event to a string and then uses CallOnMainThread to post a message - describing the event back to JavaScript, which prints a message to the - JavaScript console in Chrome and to a string on the page.</p> - <p>If you press the 'Kill worker thread and queue' button, then the main - thread (which puts events on the queue) will call CancelQueue, indicating - that the main thread will no longer put events on the queue. When the worker - sees that the shared queue has been cancelled, the worker thread will - terminate.</p> - - <!-- The NaCl plugin will be embedded inside the element with id "listener". - See common.js.--> - <div id="listener"></div> - <h2>Events</h2> - <pre> - <p><b id='eventString'>None</b></p> - </pre> -</body> -</html> diff --git a/native_client_sdk/src/examples/multithreaded_input_events/mt_input_events.cc b/native_client_sdk/src/examples/multithreaded_input_events/mt_input_events.cc deleted file mode 100644 index d0adb31..0000000 --- a/native_client_sdk/src/examples/multithreaded_input_events/mt_input_events.cc +++ /dev/null @@ -1,354 +0,0 @@ -// Copyright (c) 2012 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -// C headers -#include <cassert> -#include <cstdio> - -// C++ headers -#include <sstream> -#include <string> - -// PPAPI headers -#include "ppapi/cpp/completion_callback.h" -#include "ppapi/cpp/input_event.h" -#include "ppapi/cpp/instance.h" -#include "ppapi/cpp/module.h" -#include "ppapi/cpp/point.h" -#include "ppapi/cpp/var.h" -#include "ppapi/utility/completion_callback_factory.h" - -#include "custom_events.h" -#include "shared_queue.h" - -namespace event_queue { -const char* const kDidChangeView = "DidChangeView"; -const char* const kHandleInputEvent = "DidHandleInputEvent"; -const char* const kDidChangeFocus = "DidChangeFocus"; -const char* const kHaveFocus = "HaveFocus"; -const char* const kDontHaveFocus = "DontHaveFocus"; -const char* const kCancelMessage = "CANCEL"; - -// Convert a pepper inputevent modifier value into a -// custom event modifier. -unsigned int ConvertEventModifier(uint32_t pp_modifier) { - unsigned int custom_modifier = 0; - if (pp_modifier & PP_INPUTEVENT_MODIFIER_SHIFTKEY) { - custom_modifier |= kShiftKeyModifier; - } - if (pp_modifier & PP_INPUTEVENT_MODIFIER_CONTROLKEY) { - custom_modifier |= kControlKeyModifier; - } - if (pp_modifier & PP_INPUTEVENT_MODIFIER_ALTKEY) { - custom_modifier |= kAltKeyModifier; - } - if (pp_modifier & PP_INPUTEVENT_MODIFIER_METAKEY) { - custom_modifier |= kMetaKeyModifer; - } - if (pp_modifier & PP_INPUTEVENT_MODIFIER_ISKEYPAD) { - custom_modifier |= kKeyPadModifier; - } - if (pp_modifier & PP_INPUTEVENT_MODIFIER_ISAUTOREPEAT) { - custom_modifier |= kAutoRepeatModifier; - } - if (pp_modifier & PP_INPUTEVENT_MODIFIER_LEFTBUTTONDOWN) { - custom_modifier |= kLeftButtonModifier; - } - if (pp_modifier & PP_INPUTEVENT_MODIFIER_MIDDLEBUTTONDOWN) { - custom_modifier |= kMiddleButtonModifier; - } - if (pp_modifier & PP_INPUTEVENT_MODIFIER_RIGHTBUTTONDOWN) { - custom_modifier |= kRightButtonModifier; - } - if (pp_modifier & PP_INPUTEVENT_MODIFIER_CAPSLOCKKEY) { - custom_modifier |= kCapsLockModifier; - } - if (pp_modifier & PP_INPUTEVENT_MODIFIER_NUMLOCKKEY) { - custom_modifier |= kNumLockModifier; - } - return custom_modifier; -} - -class EventInstance : public pp::Instance { - public: - explicit EventInstance(PP_Instance instance) - : pp::Instance(instance), - event_thread_(NULL), - callback_factory_(this) { - RequestInputEvents(PP_INPUTEVENT_CLASS_MOUSE | PP_INPUTEVENT_CLASS_WHEEL - | PP_INPUTEVENT_CLASS_TOUCH); - RequestFilteringInputEvents(PP_INPUTEVENT_CLASS_KEYBOARD); - } - - // Not guaranteed to be called in Pepper, but a good idea to cancel the - // queue and signal to workers to die if it is called. - virtual ~EventInstance() { - CancelQueueAndWaitForWorker(); - } - - // Create the 'worker thread'. - bool Init(uint32_t argc, const char* argn[], const char* argv[]) { - pthread_create(&event_thread_, NULL, ProcessEventOnWorkerThread, this); - return true; - } - - /// Clicking outside of the instance's bounding box - /// will create a DidChangeFocus event (the NaCl instance is - /// out of focus). Clicking back inside the instance's - /// bounding box will create another DidChangeFocus event - /// (the NaCl instance is back in focus). The default is - /// that the instance is out of focus. - void DidChangeFocus(bool focus) { - PostMessage(pp::Var(kDidChangeFocus)); - if (focus == true) { - PostMessage(pp::Var(kHaveFocus)); - } else { - PostMessage(pp::Var(kDontHaveFocus)); - } - } - - /// Scrolling the mouse wheel causes a DidChangeView event. - void DidChangeView(const pp::View& view) { - PostMessage(pp::Var(kDidChangeView)); - } - - /// Called by the browser to handle the postMessage() call in Javascript. - /// Detects which method is being called from the message contents, and - /// calls the appropriate function. Posts the result back to the browser - /// asynchronously. - /// @param[in] var_message The message posted by the browser. The only - /// supported message is |kCancelMessage|. If we receive this, we - /// cancel the shared queue. - virtual void HandleMessage(const pp::Var& var_message) { - std::string message = var_message.AsString(); - if (kCancelMessage == message) { - std::string reply = "Received cancel : only Focus events will be " - "displayed. Worker thread for mouse/wheel/keyboard will exit."; - PostMessage(pp::Var(reply)); - printf("Calling cancel queue\n"); - CancelQueueAndWaitForWorker(); - } - } - - // HandleInputEvent operates on the main Pepper thread. Here we - // illustrate copying the Pepper input event to our own custom event type. - // Since we need to use Pepper API calls to convert it, we must do the - // conversion on the main thread. Once we have converted it to our own - // event type, we push that into a thread-safe queue and quickly return. - // The worker thread can process the custom event and do whatever - // (possibly slow) things it wants to do without making the browser - // become unresponsive. - // We dynamically allocate a sub-class of our custom event (Event) - // so that the queue can contain an Event*. - virtual bool HandleInputEvent(const pp::InputEvent& event) { - Event* event_ptr = NULL; - switch (event.GetType()) { - case PP_INPUTEVENT_TYPE_IME_COMPOSITION_START: - case PP_INPUTEVENT_TYPE_IME_COMPOSITION_UPDATE: - case PP_INPUTEVENT_TYPE_IME_COMPOSITION_END: - case PP_INPUTEVENT_TYPE_IME_TEXT: - // these cases are not handled...fall through below... - case PP_INPUTEVENT_TYPE_UNDEFINED: - break; - case PP_INPUTEVENT_TYPE_MOUSEDOWN: - case PP_INPUTEVENT_TYPE_MOUSEUP: - case PP_INPUTEVENT_TYPE_MOUSEMOVE: - case PP_INPUTEVENT_TYPE_MOUSEENTER: - case PP_INPUTEVENT_TYPE_MOUSELEAVE: - { - pp::MouseInputEvent mouse_event(event); - PP_InputEvent_MouseButton pp_button = mouse_event.GetButton(); - MouseEvent::MouseButton mouse_button = MouseEvent::kNone; - switch (pp_button) { - case PP_INPUTEVENT_MOUSEBUTTON_NONE: - mouse_button = MouseEvent::kNone; - break; - case PP_INPUTEVENT_MOUSEBUTTON_LEFT: - mouse_button = MouseEvent::kLeft; - break; - case PP_INPUTEVENT_MOUSEBUTTON_MIDDLE: - mouse_button = MouseEvent::kMiddle; - break; - case PP_INPUTEVENT_MOUSEBUTTON_RIGHT: - mouse_button = MouseEvent::kRight; - break; - } - event_ptr = new MouseEvent( - ConvertEventModifier(mouse_event.GetModifiers()), - mouse_button, mouse_event.GetPosition().x(), - mouse_event.GetPosition().y(), mouse_event.GetClickCount(), - mouse_event.GetTimeStamp()); - } - break; - case PP_INPUTEVENT_TYPE_WHEEL: - { - pp::WheelInputEvent wheel_event(event); - event_ptr = new WheelEvent( - ConvertEventModifier(wheel_event.GetModifiers()), - wheel_event.GetDelta().x(), wheel_event.GetDelta().y(), - wheel_event.GetTicks().x(), wheel_event.GetTicks().y(), - wheel_event.GetScrollByPage(), wheel_event.GetTimeStamp()); - } - break; - case PP_INPUTEVENT_TYPE_RAWKEYDOWN: - case PP_INPUTEVENT_TYPE_KEYDOWN: - case PP_INPUTEVENT_TYPE_KEYUP: - case PP_INPUTEVENT_TYPE_CHAR: - case PP_INPUTEVENT_TYPE_CONTEXTMENU: - { - pp::KeyboardInputEvent key_event(event); - event_ptr = new KeyEvent( - ConvertEventModifier(key_event.GetModifiers()), - key_event.GetKeyCode(), key_event.GetTimeStamp(), - key_event.GetCharacterText().DebugString()); - } - break; - case PP_INPUTEVENT_TYPE_TOUCHSTART: - case PP_INPUTEVENT_TYPE_TOUCHMOVE: - case PP_INPUTEVENT_TYPE_TOUCHEND: - case PP_INPUTEVENT_TYPE_TOUCHCANCEL: - { - pp::TouchInputEvent touch_event(event); - - TouchEvent::Kind touch_kind = TouchEvent::kNone; - if (event.GetType() == PP_INPUTEVENT_TYPE_TOUCHSTART) - touch_kind = TouchEvent::kStart; - else if (event.GetType() == PP_INPUTEVENT_TYPE_TOUCHMOVE) - touch_kind = TouchEvent::kMove; - else if (event.GetType() == PP_INPUTEVENT_TYPE_TOUCHEND) - touch_kind = TouchEvent::kEnd; - else if (event.GetType() == PP_INPUTEVENT_TYPE_TOUCHCANCEL) - touch_kind = TouchEvent::kCancel; - - TouchEvent* touch_event_ptr = new TouchEvent( - ConvertEventModifier(touch_event.GetModifiers()), - touch_kind, touch_event.GetTimeStamp()); - event_ptr = touch_event_ptr; - - uint32_t touch_count = - touch_event.GetTouchCount(PP_TOUCHLIST_TYPE_CHANGEDTOUCHES); - for (uint32_t i = 0; i < touch_count; ++i) { - pp::TouchPoint point = touch_event.GetTouchByIndex( - PP_TOUCHLIST_TYPE_CHANGEDTOUCHES, i); - touch_event_ptr->AddTouch(point.id(), point.position().x(), - point.position().y(), point.radii().x(), point.radii().y(), - point.rotation_angle(), point.pressure()); - } - } - break; - default: - { - // For any unhandled events, send a message to the browser - // so that the user is aware of these and can investigate. - std::stringstream oss; - oss << "Default (unhandled) event, type=" << event.GetType(); - PostMessage(oss.str()); - } - break; - } - event_queue_.Push(event_ptr); - return true; - } - - // Return an event from the thread-safe queue, waiting for a new event - // to occur if the queue is empty. Set |was_queue_cancelled| to indicate - // whether the queue was cancelled. If it was cancelled, then the - // Event* will be NULL. - const Event* GetEventFromQueue(bool *was_queue_cancelled) { - Event* event; - QueueGetResult result = event_queue_.GetItem(&event, kWait); - if (result == kQueueWasCancelled) { - *was_queue_cancelled = true; - return NULL; - } - *was_queue_cancelled = false; - return event; - } - - // This method is called from the worker thread using CallOnMainThread. - // It is not static, and allows PostMessage to be called. - void* PostStringToBrowser(int32_t result, std::string data_to_send) { - PostMessage(pp::Var(data_to_send)); - return 0; - } - - // |ProcessEventOnWorkerThread| is a static method that is run - // by a thread. It pulls events from the queue, converts - // them to a string, and calls CallOnMainThread so that - // PostStringToBrowser will be called, which will call PostMessage - // to send the converted event back to the browser. - static void* ProcessEventOnWorkerThread(void* param) { - EventInstance* event_instance = static_cast<EventInstance*>(param); - while (1) { - // Grab a generic Event* so that down below we can call - // event->ToString(), which will use the correct virtual method - // to convert the event to a string. This 'conversion' is - // the 'work' being done on the worker thread. In an application - // the work might involve changing application state based on - // the event that was processed. - bool queue_cancelled; - const Event* event = event_instance->GetEventFromQueue(&queue_cancelled); - if (queue_cancelled) { - printf("Queue was cancelled, worker thread exiting\n"); - pthread_exit(NULL); - } - std::string event_string = event->ToString(); - delete event; - // Need to invoke callback on main thread. - pp::Module::Get()->core()->CallOnMainThread( - 0, - event_instance->callback_factory().NewCallback( - &EventInstance::PostStringToBrowser, - event_string)); - } // end of while loop. - return 0; - } - - // Return the callback factory. - // Allows the static method (ProcessEventOnWorkerThread) to use - // the |event_instance| pointer to get the factory. - pp::CompletionCallbackFactory<EventInstance>& callback_factory() { - return callback_factory_; - } - - private: - // Cancels the queue (which will cause the thread to exit). - // Wait for the thread. Set |event_thread_| to NULL so we only - // execute the body once. - void CancelQueueAndWaitForWorker() { - if (event_thread_) { - event_queue_.CancelQueue(); - pthread_join(event_thread_, NULL); - event_thread_ = NULL; - } - } - pthread_t event_thread_; - LockingQueue<Event*> event_queue_; - pp::CompletionCallbackFactory<EventInstance> callback_factory_; -}; - -// The EventModule provides an implementation of pp::Module that creates -// EventInstance objects when invoked. This is part of the glue code that makes -// our example accessible to ppapi. -class EventModule : public pp::Module { - public: - EventModule() : pp::Module() {} - virtual ~EventModule() {} - - virtual pp::Instance* CreateInstance(PP_Instance instance) { - return new EventInstance(instance); - } -}; - -} // namespace - -// Implement the required pp::CreateModule function that creates our specific -// kind of Module (in this case, EventModule). This is part of the glue code -// that makes our example accessible to ppapi. -namespace pp { - Module* CreateModule() { - return new event_queue::EventModule(); - } -} - |