Home | History | Annotate | Download | only in gamepad
      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 <stdio.h>
      6 #include <stdlib.h>
      7 
      8 #include <cassert>
      9 
     10 #include "ppapi/c/ppb_gamepad.h"
     11 #include "ppapi/cpp/graphics_2d.h"
     12 #include "ppapi/cpp/image_data.h"
     13 #include "ppapi/cpp/instance.h"
     14 #include "ppapi/cpp/rect.h"
     15 #include "ppapi/cpp/size.h"
     16 #include "ppapi/cpp/var.h"
     17 #include "ppapi/utility/completion_callback_factory.h"
     18 
     19 #ifdef WIN32
     20 #undef min
     21 #undef max
     22 
     23 // Allow 'this' in initializer list
     24 #pragma warning(disable : 4355)
     25 #endif
     26 
     27 class GamepadInstance : public pp::Instance {
     28  public:
     29   explicit GamepadInstance(PP_Instance instance);
     30   virtual ~GamepadInstance();
     31 
     32   // Update the graphics context to the new size, and regenerate |pixel_buffer_|
     33   // to fit the new size as well.
     34   virtual void DidChangeView(const pp::View& view);
     35 
     36   // Flushes its contents of |pixel_buffer_| to the 2D graphics context.
     37   void Paint();
     38 
     39   int width() const {
     40     return pixel_buffer_ ? pixel_buffer_->size().width() : 0;
     41   }
     42   int height() const {
     43     return pixel_buffer_ ? pixel_buffer_->size().height() : 0;
     44   }
     45 
     46   // Indicate whether a flush is pending.  This can only be called from the
     47   // main thread; it is not thread safe.
     48   bool flush_pending() const { return flush_pending_; }
     49   void set_flush_pending(bool flag) { flush_pending_ = flag; }
     50 
     51  private:
     52   // Create and initialize the 2D context used for drawing.
     53   void CreateContext(const pp::Size& size);
     54   // Destroy the 2D drawing context.
     55   void DestroyContext();
     56   // Push the pixels to the browser, then attempt to flush the 2D context.  If
     57   // there is a pending flush on the 2D context, then update the pixels only
     58   // and do not flush.
     59   void FlushPixelBuffer();
     60 
     61   void FlushCallback(int32_t result);
     62 
     63   bool IsContextValid() const { return graphics_2d_context_ != NULL; }
     64 
     65   pp::CompletionCallbackFactory<GamepadInstance> callback_factory_;
     66   pp::Graphics2D* graphics_2d_context_;
     67   pp::ImageData* pixel_buffer_;
     68   const PPB_Gamepad* gamepad_;
     69   bool flush_pending_;
     70 };
     71 
     72 GamepadInstance::GamepadInstance(PP_Instance instance)
     73     : pp::Instance(instance),
     74       callback_factory_(this),
     75       graphics_2d_context_(NULL),
     76       pixel_buffer_(NULL),
     77       flush_pending_(false) {
     78   pp::Module* module = pp::Module::Get();
     79   assert(module);
     80   gamepad_ = static_cast<const PPB_Gamepad*>(
     81       module->GetBrowserInterface(PPB_GAMEPAD_INTERFACE));
     82   assert(gamepad_);
     83 }
     84 
     85 GamepadInstance::~GamepadInstance() {
     86   DestroyContext();
     87   delete pixel_buffer_;
     88 }
     89 
     90 void GamepadInstance::DidChangeView(const pp::View& view) {
     91   pp::Rect position = view.GetRect();
     92   if (position.size().width() == width() &&
     93       position.size().height() == height())
     94     return;  // Size didn't change, no need to update anything.
     95 
     96   // Create a new device context with the new size.
     97   DestroyContext();
     98   CreateContext(position.size());
     99   // Delete the old pixel buffer and create a new one.
    100   delete pixel_buffer_;
    101   pixel_buffer_ = NULL;
    102   if (graphics_2d_context_ != NULL) {
    103     pixel_buffer_ = new pp::ImageData(this,
    104                                       PP_IMAGEDATAFORMAT_BGRA_PREMUL,
    105                                       graphics_2d_context_->size(),
    106                                       false);
    107   }
    108   Paint();
    109 }
    110 
    111 void FillRect(pp::ImageData* image,
    112               int left,
    113               int top,
    114               int width,
    115               int height,
    116               uint32_t color) {
    117   for (int y = std::max(0, top);
    118        y < std::min(image->size().height() - 1, top + height);
    119        y++) {
    120     for (int x = std::max(0, left);
    121          x < std::min(image->size().width() - 1, left + width);
    122          x++)
    123       *image->GetAddr32(pp::Point(x, y)) = color;
    124   }
    125 }
    126 
    127 void GamepadInstance::Paint() {
    128   // Clear the background.
    129   FillRect(pixel_buffer_, 0, 0, width(), height(), 0xfff0f0f0);
    130 
    131   // Get current gamepad data.
    132   PP_GamepadsSampleData gamepad_data;
    133   gamepad_->Sample(pp_instance(), &gamepad_data);
    134 
    135   // Draw the current state for each connected gamepad.
    136   for (size_t p = 0; p < gamepad_data.length; ++p) {
    137     int width2 = width() / gamepad_data.length / 2;
    138     int height2 = height() / 2;
    139     int offset = width2 * 2 * p;
    140     PP_GamepadSampleData& pad = gamepad_data.items[p];
    141 
    142     if (!pad.connected)
    143       continue;
    144 
    145     // Draw axes.
    146     for (size_t i = 0; i < pad.axes_length; i += 2) {
    147       int x = static_cast<int>(pad.axes[i + 0] * width2 + width2) + offset;
    148       int y = static_cast<int>(pad.axes[i + 1] * height2 + height2);
    149       uint32_t box_bgra = 0x80000000;  // Alpha 50%.
    150       FillRect(pixel_buffer_, x - 3, y - 3, 7, 7, box_bgra);
    151     }
    152 
    153     // Draw buttons.
    154     for (size_t i = 0; i < pad.buttons_length; ++i) {
    155       float button_val = pad.buttons[i];
    156       uint32_t colour = static_cast<uint32_t>((button_val * 192) + 63) << 24;
    157       int x = i * 8 + 10 + offset;
    158       int y = 10;
    159       FillRect(pixel_buffer_, x - 3, y - 3, 7, 7, colour);
    160     }
    161   }
    162 
    163   // Output to the screen.
    164   FlushPixelBuffer();
    165 }
    166 
    167 void GamepadInstance::CreateContext(const pp::Size& size) {
    168   if (IsContextValid())
    169     return;
    170   graphics_2d_context_ = new pp::Graphics2D(this, size, false);
    171   if (!BindGraphics(*graphics_2d_context_)) {
    172     printf("Couldn't bind the device context\n");
    173   }
    174 }
    175 
    176 void GamepadInstance::DestroyContext() {
    177   if (!IsContextValid())
    178     return;
    179   delete graphics_2d_context_;
    180   graphics_2d_context_ = NULL;
    181 }
    182 
    183 void GamepadInstance::FlushPixelBuffer() {
    184   if (!IsContextValid())
    185     return;
    186   // Note that the pixel lock is held while the buffer is copied into the
    187   // device context and then flushed.
    188   graphics_2d_context_->PaintImageData(*pixel_buffer_, pp::Point());
    189   if (flush_pending())
    190     return;
    191   set_flush_pending(true);
    192   graphics_2d_context_->Flush(
    193       callback_factory_.NewCallback(&GamepadInstance::FlushCallback));
    194 }
    195 
    196 void GamepadInstance::FlushCallback(int32_t result) {
    197   set_flush_pending(false);
    198   Paint();
    199 }
    200 
    201 class GamepadModule : public pp::Module {
    202  public:
    203   GamepadModule() : pp::Module() {}
    204   virtual ~GamepadModule() {}
    205 
    206   virtual pp::Instance* CreateInstance(PP_Instance instance) {
    207     return new GamepadInstance(instance);
    208   }
    209 };
    210 
    211 namespace pp {
    212 Module* CreateModule() { return new GamepadModule(); }
    213 }  // namespace pp
    214