// 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. #include <cmath> #include <stdarg.h> #include <stdio.h> #include "ppapi/c/ppb_console.h" #include "ppapi/c/ppb_input_event.h" #include "ppapi/cpp/graphics_2d.h" #include "ppapi/cpp/image_data.h" #include "ppapi/cpp/input_event.h" #include "ppapi/cpp/instance.h" #include "ppapi/cpp/logging.h" #include "ppapi/cpp/module.h" #include "ppapi/cpp/mouse_lock.h" #include "ppapi/cpp/private/flash_fullscreen.h" #include "ppapi/cpp/rect.h" #include "ppapi/cpp/var.h" #include "ppapi/utility/completion_callback_factory.h" class MyInstance : public pp::Instance, public pp::MouseLock { public: explicit MyInstance(PP_Instance instance) : pp::Instance(instance), pp::MouseLock(this), width_(0), height_(0), mouse_locked_(false), pending_paint_(false), waiting_for_flush_completion_(false), callback_factory_(this), console_(NULL), flash_fullscreen_(this) { } virtual ~MyInstance() {} virtual bool Init(uint32_t argc, const char* argn[], const char* argv[]) { console_ = reinterpret_cast<const PPB_Console*>( pp::Module::Get()->GetBrowserInterface(PPB_CONSOLE_INTERFACE)); if (!console_) return false; RequestInputEvents(PP_INPUTEVENT_CLASS_MOUSE | PP_INPUTEVENT_CLASS_KEYBOARD); return true; } virtual bool HandleInputEvent(const pp::InputEvent& event) { switch (event.GetType()) { case PP_INPUTEVENT_TYPE_MOUSEDOWN: { pp::MouseInputEvent mouse_event(event); if (mouse_event.GetButton() == PP_INPUTEVENT_MOUSEBUTTON_LEFT && !mouse_locked_) { LockMouse(callback_factory_.NewCallback(&MyInstance::DidLockMouse)); } return true; } case PP_INPUTEVENT_TYPE_MOUSEMOVE: { pp::MouseInputEvent mouse_event(event); mouse_movement_ = mouse_event.GetMovement(); static unsigned int i = 0; Log(PP_LOGLEVEL_LOG, "[%d] movementX: %d; movementY: %d\n", i++, mouse_movement_.x(), mouse_movement_.y()); Paint(); return true; } case PP_INPUTEVENT_TYPE_KEYDOWN: { pp::KeyboardInputEvent key_event(event); if (key_event.GetKeyCode() == 13) { // Lock the mouse when the Enter key is pressed. if (mouse_locked_) UnlockMouse(); else LockMouse(callback_factory_.NewCallback(&MyInstance::DidLockMouse)); return true; } else if (key_event.GetKeyCode() == 70) { // Enter Flash fullscreen mode when the 'f' key is pressed. if (!flash_fullscreen_.IsFullscreen()) flash_fullscreen_.SetFullscreen(true); return true; } return false; } default: return false; } } virtual void DidChangeView(const pp::Rect& position, const pp::Rect& clip) { if (position.size().width() == width_ && position.size().height() == height_) return; // We don't care about the position, only the size. width_ = position.size().width(); height_ = position.size().height(); device_context_ = pp::Graphics2D(this, pp::Size(width_, height_), false); if (!BindGraphics(device_context_)) return; Paint(); } virtual void MouseLockLost() { if (mouse_locked_) { mouse_locked_ = false; Paint(); } else { PP_NOTREACHED(); } } private: void DidLockMouse(int32_t result) { mouse_locked_ = result == PP_OK; mouse_movement_.set_x(0); mouse_movement_.set_y(0); Paint(); } void DidFlush(int32_t result) { waiting_for_flush_completion_ = false; if (pending_paint_) { pending_paint_ = false; Paint(); } } void Paint() { if (waiting_for_flush_completion_) { pending_paint_ = true; return; } pp::ImageData image = PaintImage(width_, height_); if (!image.is_null()) { device_context_.ReplaceContents(&image); waiting_for_flush_completion_ = true; device_context_.Flush( callback_factory_.NewCallback(&MyInstance::DidFlush)); } } pp::ImageData PaintImage(int width, int height) { pp::ImageData image(this, PP_IMAGEDATAFORMAT_BGRA_PREMUL, pp::Size(width, height), false); if (image.is_null()) return image; const static int kCenteralSpotRadius = 5; const static uint32_t kBackgroundColor = 0xfff0f0f0; const static uint32_t kLockedForegroundColor = 0xfff08080; const static uint32_t kUnlockedForegroundColor = 0xff80f080; int center_x = width / 2; int center_y = height / 2; pp::Point vertex(mouse_movement_.x() + center_x, mouse_movement_.y() + center_y); pp::Point anchor_1; pp::Point anchor_2; enum { LEFT = 0, RIGHT = 1, UP = 2, DOWN = 3 } direction = LEFT; bool draw_needle = GetDistance(mouse_movement_.x(), mouse_movement_.y(), 0, 0) > kCenteralSpotRadius; if (draw_needle) { if (abs(mouse_movement_.x()) >= abs(mouse_movement_.y())) { anchor_1.set_x(center_x); anchor_1.set_y(center_y - kCenteralSpotRadius); anchor_2.set_x(center_x); anchor_2.set_y(center_y + kCenteralSpotRadius); direction = (mouse_movement_.x() < 0) ? LEFT : RIGHT; if (direction == LEFT) anchor_1.swap(anchor_2); } else { anchor_1.set_x(center_x + kCenteralSpotRadius); anchor_1.set_y(center_y); anchor_2.set_x(center_x - kCenteralSpotRadius); anchor_2.set_y(center_y); direction = (mouse_movement_.y() < 0) ? UP : DOWN; if (direction == UP) anchor_1.swap(anchor_2); } } uint32_t foreground_color = mouse_locked_ ? kLockedForegroundColor : kUnlockedForegroundColor; for (int y = 0; y < image.size().height(); ++y) { for (int x = 0; x < image.size().width(); ++x) { if (GetDistance(x, y, center_x, center_y) < kCenteralSpotRadius) { *image.GetAddr32(pp::Point(x, y)) = foreground_color; continue; } if (draw_needle) { bool within_bound_1 = ((y - anchor_1.y()) * (vertex.x() - anchor_1.x())) > ((vertex.y() - anchor_1.y()) * (x - anchor_1.x())); bool within_bound_2 = ((y - anchor_2.y()) * (vertex.x() - anchor_2.x())) < ((vertex.y() - anchor_2.y()) * (x - anchor_2.x())); bool within_bound_3 = (direction == UP && y < center_y) || (direction == DOWN && y > center_y) || (direction == LEFT && x < center_x) || (direction == RIGHT && x > center_x); if (within_bound_1 && within_bound_2 && within_bound_3) { *image.GetAddr32(pp::Point(x, y)) = foreground_color; continue; } } *image.GetAddr32(pp::Point(x, y)) = kBackgroundColor; } } return image; } double GetDistance(int point_1_x, int point_1_y, int point_2_x, int point_2_y) { return sqrt(pow(static_cast<double>(point_1_x - point_2_x), 2) + pow(static_cast<double>(point_1_y - point_2_y), 2)); } void Log(PP_LogLevel level, const char* format, ...) { va_list args; va_start(args, format); char buf[512]; vsnprintf(buf, sizeof(buf) - 1, format, args); buf[sizeof(buf) - 1] = '\0'; va_end(args); pp::Var value(buf); console_->Log(pp_instance(), level, value.pp_var()); } int width_; int height_; bool mouse_locked_; pp::Point mouse_movement_; bool pending_paint_; bool waiting_for_flush_completion_; pp::CompletionCallbackFactory<MyInstance> callback_factory_; const PPB_Console* console_; pp::FlashFullscreen flash_fullscreen_; pp::Graphics2D device_context_; }; // This object is the global object representing this plugin library as long // as it is loaded. class MyModule : public pp::Module { public: MyModule() : pp::Module() {} virtual ~MyModule() {} // Override CreateInstance to create your customized Instance object. virtual pp::Instance* CreateInstance(PP_Instance instance) { return new MyInstance(instance); } }; namespace pp { // Factory function for your specialization of the Module object. Module* CreateModule() { return new MyModule(); } } // namespace pp