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