Home | History | Annotate | Download | only in mouse_lock
      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