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 #include <cmath> 6 #include <stdarg.h> 7 #include <stdio.h> 8 9 #include "ppapi/c/ppb_console.h" 10 #include "ppapi/c/ppb_input_event.h" 11 #include "ppapi/cpp/graphics_2d.h" 12 #include "ppapi/cpp/image_data.h" 13 #include "ppapi/cpp/input_event.h" 14 #include "ppapi/cpp/instance.h" 15 #include "ppapi/cpp/logging.h" 16 #include "ppapi/cpp/module.h" 17 #include "ppapi/cpp/mouse_lock.h" 18 #include "ppapi/cpp/private/flash_fullscreen.h" 19 #include "ppapi/cpp/rect.h" 20 #include "ppapi/cpp/var.h" 21 #include "ppapi/utility/completion_callback_factory.h" 22 23 class MyInstance : public pp::Instance, public pp::MouseLock { 24 public: 25 explicit MyInstance(PP_Instance instance) 26 : pp::Instance(instance), 27 pp::MouseLock(this), 28 width_(0), 29 height_(0), 30 mouse_locked_(false), 31 pending_paint_(false), 32 waiting_for_flush_completion_(false), 33 callback_factory_(this), 34 console_(NULL), 35 flash_fullscreen_(this) { 36 } 37 virtual ~MyInstance() {} 38 39 virtual bool Init(uint32_t argc, const char* argn[], const char* argv[]) { 40 console_ = reinterpret_cast<const PPB_Console*>( 41 pp::Module::Get()->GetBrowserInterface(PPB_CONSOLE_INTERFACE)); 42 if (!console_) 43 return false; 44 45 RequestInputEvents(PP_INPUTEVENT_CLASS_MOUSE | 46 PP_INPUTEVENT_CLASS_KEYBOARD); 47 return true; 48 } 49 50 virtual bool HandleInputEvent(const pp::InputEvent& event) { 51 switch (event.GetType()) { 52 case PP_INPUTEVENT_TYPE_MOUSEDOWN: { 53 pp::MouseInputEvent mouse_event(event); 54 if (mouse_event.GetButton() == PP_INPUTEVENT_MOUSEBUTTON_LEFT && 55 !mouse_locked_) { 56 LockMouse(callback_factory_.NewCallback(&MyInstance::DidLockMouse)); 57 } 58 return true; 59 } 60 case PP_INPUTEVENT_TYPE_MOUSEMOVE: { 61 pp::MouseInputEvent mouse_event(event); 62 mouse_movement_ = mouse_event.GetMovement(); 63 static unsigned int i = 0; 64 Log(PP_LOGLEVEL_LOG, "[%d] movementX: %d; movementY: %d\n", i++, 65 mouse_movement_.x(), mouse_movement_.y()); 66 Paint(); 67 return true; 68 } 69 case PP_INPUTEVENT_TYPE_KEYDOWN: { 70 pp::KeyboardInputEvent key_event(event); 71 if (key_event.GetKeyCode() == 13) { 72 // Lock the mouse when the Enter key is pressed. 73 if (mouse_locked_) 74 UnlockMouse(); 75 else 76 LockMouse(callback_factory_.NewCallback(&MyInstance::DidLockMouse)); 77 return true; 78 } else if (key_event.GetKeyCode() == 70) { 79 // Enter Flash fullscreen mode when the 'f' key is pressed. 80 if (!flash_fullscreen_.IsFullscreen()) 81 flash_fullscreen_.SetFullscreen(true); 82 return true; 83 } 84 return false; 85 } 86 default: 87 return false; 88 } 89 } 90 91 virtual void DidChangeView(const pp::Rect& position, const pp::Rect& clip) { 92 if (position.size().width() == width_ && 93 position.size().height() == height_) 94 return; // We don't care about the position, only the size. 95 96 width_ = position.size().width(); 97 height_ = position.size().height(); 98 99 device_context_ = pp::Graphics2D(this, pp::Size(width_, height_), false); 100 if (!BindGraphics(device_context_)) 101 return; 102 103 Paint(); 104 } 105 106 virtual void MouseLockLost() { 107 if (mouse_locked_) { 108 mouse_locked_ = false; 109 Paint(); 110 } else { 111 PP_NOTREACHED(); 112 } 113 } 114 115 private: 116 void DidLockMouse(int32_t result) { 117 mouse_locked_ = result == PP_OK; 118 mouse_movement_.set_x(0); 119 mouse_movement_.set_y(0); 120 Paint(); 121 } 122 123 void DidFlush(int32_t result) { 124 waiting_for_flush_completion_ = false; 125 if (pending_paint_) { 126 pending_paint_ = false; 127 Paint(); 128 } 129 } 130 131 void Paint() { 132 if (waiting_for_flush_completion_) { 133 pending_paint_ = true; 134 return; 135 } 136 137 pp::ImageData image = PaintImage(width_, height_); 138 if (!image.is_null()) { 139 device_context_.ReplaceContents(&image); 140 waiting_for_flush_completion_ = true; 141 device_context_.Flush( 142 callback_factory_.NewCallback(&MyInstance::DidFlush)); 143 } 144 } 145 146 pp::ImageData PaintImage(int width, int height) { 147 pp::ImageData image(this, PP_IMAGEDATAFORMAT_BGRA_PREMUL, 148 pp::Size(width, height), false); 149 if (image.is_null()) 150 return image; 151 152 const static int kCenteralSpotRadius = 5; 153 const static uint32_t kBackgroundColor = 0xfff0f0f0; 154 const static uint32_t kLockedForegroundColor = 0xfff08080; 155 const static uint32_t kUnlockedForegroundColor = 0xff80f080; 156 157 int center_x = width / 2; 158 int center_y = height / 2; 159 pp::Point vertex(mouse_movement_.x() + center_x, 160 mouse_movement_.y() + center_y); 161 pp::Point anchor_1; 162 pp::Point anchor_2; 163 enum { 164 LEFT = 0, 165 RIGHT = 1, 166 UP = 2, 167 DOWN = 3 168 } direction = LEFT; 169 bool draw_needle = GetDistance(mouse_movement_.x(), mouse_movement_.y(), 170 0, 0) > kCenteralSpotRadius; 171 if (draw_needle) { 172 if (abs(mouse_movement_.x()) >= abs(mouse_movement_.y())) { 173 anchor_1.set_x(center_x); 174 anchor_1.set_y(center_y - kCenteralSpotRadius); 175 anchor_2.set_x(center_x); 176 anchor_2.set_y(center_y + kCenteralSpotRadius); 177 direction = (mouse_movement_.x() < 0) ? LEFT : RIGHT; 178 if (direction == LEFT) 179 anchor_1.swap(anchor_2); 180 } else { 181 anchor_1.set_x(center_x + kCenteralSpotRadius); 182 anchor_1.set_y(center_y); 183 anchor_2.set_x(center_x - kCenteralSpotRadius); 184 anchor_2.set_y(center_y); 185 direction = (mouse_movement_.y() < 0) ? UP : DOWN; 186 if (direction == UP) 187 anchor_1.swap(anchor_2); 188 } 189 } 190 uint32_t foreground_color = mouse_locked_ ? kLockedForegroundColor : 191 kUnlockedForegroundColor; 192 for (int y = 0; y < image.size().height(); ++y) { 193 for (int x = 0; x < image.size().width(); ++x) { 194 if (GetDistance(x, y, center_x, center_y) < kCenteralSpotRadius) { 195 *image.GetAddr32(pp::Point(x, y)) = foreground_color; 196 continue; 197 } 198 if (draw_needle) { 199 bool within_bound_1 = 200 ((y - anchor_1.y()) * (vertex.x() - anchor_1.x())) > 201 ((vertex.y() - anchor_1.y()) * (x - anchor_1.x())); 202 bool within_bound_2 = 203 ((y - anchor_2.y()) * (vertex.x() - anchor_2.x())) < 204 ((vertex.y() - anchor_2.y()) * (x - anchor_2.x())); 205 bool within_bound_3 = 206 (direction == UP && y < center_y) || 207 (direction == DOWN && y > center_y) || 208 (direction == LEFT && x < center_x) || 209 (direction == RIGHT && x > center_x); 210 211 if (within_bound_1 && within_bound_2 && within_bound_3) { 212 *image.GetAddr32(pp::Point(x, y)) = foreground_color; 213 continue; 214 } 215 } 216 *image.GetAddr32(pp::Point(x, y)) = kBackgroundColor; 217 } 218 } 219 220 return image; 221 } 222 223 double GetDistance(int point_1_x, int point_1_y, 224 int point_2_x, int point_2_y) { 225 return sqrt(pow(static_cast<double>(point_1_x - point_2_x), 2) + 226 pow(static_cast<double>(point_1_y - point_2_y), 2)); 227 } 228 229 void Log(PP_LogLevel level, const char* format, ...) { 230 va_list args; 231 va_start(args, format); 232 char buf[512]; 233 vsnprintf(buf, sizeof(buf) - 1, format, args); 234 buf[sizeof(buf) - 1] = '\0'; 235 va_end(args); 236 237 pp::Var value(buf); 238 console_->Log(pp_instance(), level, value.pp_var()); 239 } 240 241 int width_; 242 int height_; 243 244 bool mouse_locked_; 245 pp::Point mouse_movement_; 246 247 bool pending_paint_; 248 bool waiting_for_flush_completion_; 249 250 pp::CompletionCallbackFactory<MyInstance> callback_factory_; 251 252 const PPB_Console* console_; 253 254 pp::FlashFullscreen flash_fullscreen_; 255 256 pp::Graphics2D device_context_; 257 }; 258 259 // This object is the global object representing this plugin library as long 260 // as it is loaded. 261 class MyModule : public pp::Module { 262 public: 263 MyModule() : pp::Module() {} 264 virtual ~MyModule() {} 265 266 // Override CreateInstance to create your customized Instance object. 267 virtual pp::Instance* CreateInstance(PP_Instance instance) { 268 return new MyInstance(instance); 269 } 270 }; 271 272 namespace pp { 273 274 // Factory function for your specialization of the Module object. 275 Module* CreateModule() { 276 return new MyModule(); 277 } 278 279 } // namespace pp 280 281