Upstream version 11.40.277.0
[platform/framework/web/crosswalk.git] / src / native_client_sdk / src / examples / api / gamepad / gamepad.cc
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