1c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)// Copyright (c) 2012 The Chromium Authors. All rights reserved.
25821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// Use of this source code is governed by a BSD-style license that can be
35821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// found in the LICENSE file.
45821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
55821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include <stdio.h>
65821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include <stdlib.h>
7b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles)
81320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci#include <algorithm>
95821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include <cassert>
10b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles)
115821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "ppapi/c/ppb_gamepad.h"
12b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles)#include "ppapi/cpp/graphics_2d.h"
13b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles)#include "ppapi/cpp/image_data.h"
14b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles)#include "ppapi/cpp/instance.h"
15b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles)#include "ppapi/cpp/rect.h"
16b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles)#include "ppapi/cpp/size.h"
175821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "ppapi/cpp/var.h"
18b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles)#include "ppapi/utility/completion_callback_factory.h"
195821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
20a93a17c8d99d686bd4a1511e5504e5e6cc9fcadfTorne (Richard Coles)#ifdef WIN32
21a93a17c8d99d686bd4a1511e5504e5e6cc9fcadfTorne (Richard Coles)#undef min
22a93a17c8d99d686bd4a1511e5504e5e6cc9fcadfTorne (Richard Coles)#undef max
23a93a17c8d99d686bd4a1511e5504e5e6cc9fcadfTorne (Richard Coles)
24a93a17c8d99d686bd4a1511e5504e5e6cc9fcadfTorne (Richard Coles)// Allow 'this' in initializer list
25a93a17c8d99d686bd4a1511e5504e5e6cc9fcadfTorne (Richard Coles)#pragma warning(disable : 4355)
26a93a17c8d99d686bd4a1511e5504e5e6cc9fcadfTorne (Richard Coles)#endif
27a93a17c8d99d686bd4a1511e5504e5e6cc9fcadfTorne (Richard Coles)
28b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles)class GamepadInstance : public pp::Instance {
29b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles) public:
30b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles)  explicit GamepadInstance(PP_Instance instance);
31b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles)  virtual ~GamepadInstance();
325821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
33b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles)  // Update the graphics context to the new size, and regenerate |pixel_buffer_|
34b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles)  // to fit the new size as well.
35b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles)  virtual void DidChangeView(const pp::View& view);
365821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
37b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles)  // Flushes its contents of |pixel_buffer_| to the 2D graphics context.
38b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles)  void Paint();
395821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
40b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles)  int width() const {
41b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles)    return pixel_buffer_ ? pixel_buffer_->size().width() : 0;
42b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles)  }
43b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles)  int height() const {
44b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles)    return pixel_buffer_ ? pixel_buffer_->size().height() : 0;
45b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles)  }
465821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
47b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles)  // Indicate whether a flush is pending.  This can only be called from the
48b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles)  // main thread; it is not thread safe.
49b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles)  bool flush_pending() const { return flush_pending_; }
50b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles)  void set_flush_pending(bool flag) { flush_pending_ = flag; }
51b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles)
52b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles) private:
53b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles)  // Create and initialize the 2D context used for drawing.
54b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles)  void CreateContext(const pp::Size& size);
55b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles)  // Destroy the 2D drawing context.
56b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles)  void DestroyContext();
57b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles)  // Push the pixels to the browser, then attempt to flush the 2D context.  If
58b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles)  // there is a pending flush on the 2D context, then update the pixels only
59b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles)  // and do not flush.
60b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles)  void FlushPixelBuffer();
61b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles)
62b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles)  void FlushCallback(int32_t result);
63b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles)
64b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles)  bool IsContextValid() const { return graphics_2d_context_ != NULL; }
65b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles)
66b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles)  pp::CompletionCallbackFactory<GamepadInstance> callback_factory_;
67b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles)  pp::Graphics2D* graphics_2d_context_;
68b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles)  pp::ImageData* pixel_buffer_;
69b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles)  const PPB_Gamepad* gamepad_;
70b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles)  bool flush_pending_;
71b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles)};
72b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles)
73b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles)GamepadInstance::GamepadInstance(PP_Instance instance)
745821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    : pp::Instance(instance),
75b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles)      callback_factory_(this),
765821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      graphics_2d_context_(NULL),
775821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      pixel_buffer_(NULL),
78b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles)      flush_pending_(false) {
795821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  pp::Module* module = pp::Module::Get();
805821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  assert(module);
815821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  gamepad_ = static_cast<const PPB_Gamepad*>(
825821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      module->GetBrowserInterface(PPB_GAMEPAD_INTERFACE));
835821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  assert(gamepad_);
845821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
855821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
86b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles)GamepadInstance::~GamepadInstance() {
875821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  DestroyContext();
885821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  delete pixel_buffer_;
895821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
905821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
91b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles)void GamepadInstance::DidChangeView(const pp::View& view) {
925821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  pp::Rect position = view.GetRect();
935821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (position.size().width() == width() &&
945821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      position.size().height() == height())
955821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return;  // Size didn't change, no need to update anything.
965821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
975821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // Create a new device context with the new size.
985821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  DestroyContext();
995821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  CreateContext(position.size());
1005821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // Delete the old pixel buffer and create a new one.
1015821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  delete pixel_buffer_;
1025821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  pixel_buffer_ = NULL;
1035821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (graphics_2d_context_ != NULL) {
1045821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    pixel_buffer_ = new pp::ImageData(this,
1055821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                                      PP_IMAGEDATAFORMAT_BGRA_PREMUL,
1065821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                                      graphics_2d_context_->size(),
1075821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                                      false);
1085821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
1095821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  Paint();
1105821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
1115821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
112c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)void FillRect(pp::ImageData* image,
113c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)              int left,
114c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)              int top,
115c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)              int width,
116c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)              int height,
1175821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)              uint32_t color) {
1185821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  for (int y = std::max(0, top);
1195821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)       y < std::min(image->size().height() - 1, top + height);
1205821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)       y++) {
1215821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    for (int x = std::max(0, left);
1225821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)         x < std::min(image->size().width() - 1, left + width);
1235821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)         x++)
1245821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      *image->GetAddr32(pp::Point(x, y)) = color;
1255821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
1265821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
1275821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
128b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles)void GamepadInstance::Paint() {
1295821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // Clear the background.
1305821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  FillRect(pixel_buffer_, 0, 0, width(), height(), 0xfff0f0f0);
1315821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1325821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // Get current gamepad data.
1335821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  PP_GamepadsSampleData gamepad_data;
1345821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  gamepad_->Sample(pp_instance(), &gamepad_data);
1355821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1365821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // Draw the current state for each connected gamepad.
1375821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  for (size_t p = 0; p < gamepad_data.length; ++p) {
1385821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    int width2 = width() / gamepad_data.length / 2;
1395821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    int height2 = height() / 2;
1405821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    int offset = width2 * 2 * p;
1415821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    PP_GamepadSampleData& pad = gamepad_data.items[p];
1425821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1435821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    if (!pad.connected)
1445821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      continue;
1455821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1465821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    // Draw axes.
1475821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    for (size_t i = 0; i < pad.axes_length; i += 2) {
1485821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      int x = static_cast<int>(pad.axes[i + 0] * width2 + width2) + offset;
1495821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      int y = static_cast<int>(pad.axes[i + 1] * height2 + height2);
1505821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      uint32_t box_bgra = 0x80000000;  // Alpha 50%.
1515821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      FillRect(pixel_buffer_, x - 3, y - 3, 7, 7, box_bgra);
1525821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    }
1535821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1545821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    // Draw buttons.
1555821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    for (size_t i = 0; i < pad.buttons_length; ++i) {
1565821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      float button_val = pad.buttons[i];
1575821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      uint32_t colour = static_cast<uint32_t>((button_val * 192) + 63) << 24;
1585821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      int x = i * 8 + 10 + offset;
1595821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      int y = 10;
1605821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      FillRect(pixel_buffer_, x - 3, y - 3, 7, 7, colour);
1615821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    }
1625821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
1635821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1645821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // Output to the screen.
1655821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  FlushPixelBuffer();
1665821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
1675821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
168b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles)void GamepadInstance::CreateContext(const pp::Size& size) {
1695821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (IsContextValid())
1705821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return;
1715821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  graphics_2d_context_ = new pp::Graphics2D(this, size, false);
1725821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (!BindGraphics(*graphics_2d_context_)) {
1735821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    printf("Couldn't bind the device context\n");
1745821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
1755821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
1765821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
177b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles)void GamepadInstance::DestroyContext() {
1785821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (!IsContextValid())
1795821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return;
1805821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  delete graphics_2d_context_;
1815821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  graphics_2d_context_ = NULL;
1825821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
1835821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
184b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles)void GamepadInstance::FlushPixelBuffer() {
1855821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (!IsContextValid())
1865821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return;
1875821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // Note that the pixel lock is held while the buffer is copied into the
1885821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // device context and then flushed.
1895821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  graphics_2d_context_->PaintImageData(*pixel_buffer_, pp::Point());
1905821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (flush_pending())
1915821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return;
1925821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  set_flush_pending(true);
193b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles)  graphics_2d_context_->Flush(
194b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles)      callback_factory_.NewCallback(&GamepadInstance::FlushCallback));
1955821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
1965821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
197b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles)void GamepadInstance::FlushCallback(int32_t result) {
198b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles)  set_flush_pending(false);
199b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles)  Paint();
200b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles)}
201b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles)
202b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles)class GamepadModule : public pp::Module {
203b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles) public:
204b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles)  GamepadModule() : pp::Module() {}
205b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles)  virtual ~GamepadModule() {}
206b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles)
207b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles)  virtual pp::Instance* CreateInstance(PP_Instance instance) {
208b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles)    return new GamepadInstance(instance);
209b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles)  }
210b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles)};
211b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles)
212b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles)namespace pp {
213b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles)Module* CreateModule() { return new GamepadModule(); }
214b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles)}  // namespace pp
215