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.
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"
23 // Allow 'this' in initializer list
24 #pragma warning(disable : 4355)
27 class GamepadInstance : public pp::Instance {
29 explicit GamepadInstance(PP_Instance instance);
30 virtual ~GamepadInstance();
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);
36 // Flushes its contents of |pixel_buffer_| to the 2D graphics context.
40 return pixel_buffer_ ? pixel_buffer_->size().width() : 0;
43 return pixel_buffer_ ? pixel_buffer_->size().height() : 0;
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; }
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
59 void FlushPixelBuffer();
61 void FlushCallback(int32_t result);
63 bool IsContextValid() const { return graphics_2d_context_ != NULL; }
65 pp::CompletionCallbackFactory<GamepadInstance> callback_factory_;
66 pp::Graphics2D* graphics_2d_context_;
67 pp::ImageData* pixel_buffer_;
68 const PPB_Gamepad* gamepad_;
72 GamepadInstance::GamepadInstance(PP_Instance instance)
73 : pp::Instance(instance),
74 callback_factory_(this),
75 graphics_2d_context_(NULL),
77 flush_pending_(false) {
78 pp::Module* module = pp::Module::Get();
80 gamepad_ = static_cast<const PPB_Gamepad*>(
81 module->GetBrowserInterface(PPB_GAMEPAD_INTERFACE));
85 GamepadInstance::~GamepadInstance() {
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.
96 // Create a new device context with the new size.
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(),
111 void FillRect(pp::ImageData* image,
117 for (int y = std::max(0, top);
118 y < std::min(image->size().height() - 1, top + height);
120 for (int x = std::max(0, left);
121 x < std::min(image->size().width() - 1, left + width);
123 *image->GetAddr32(pp::Point(x, y)) = color;
127 void GamepadInstance::Paint() {
128 // Clear the background.
129 FillRect(pixel_buffer_, 0, 0, width(), height(), 0xfff0f0f0);
131 // Get current gamepad data.
132 PP_GamepadsSampleData gamepad_data;
133 gamepad_->Sample(pp_instance(), &gamepad_data);
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];
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);
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;
159 FillRect(pixel_buffer_, x - 3, y - 3, 7, 7, colour);
163 // Output to the screen.
167 void GamepadInstance::CreateContext(const pp::Size& size) {
168 if (IsContextValid())
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");
176 void GamepadInstance::DestroyContext() {
177 if (!IsContextValid())
179 delete graphics_2d_context_;
180 graphics_2d_context_ = NULL;
183 void GamepadInstance::FlushPixelBuffer() {
184 if (!IsContextValid())
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());
191 set_flush_pending(true);
192 graphics_2d_context_->Flush(
193 callback_factory_.NewCallback(&GamepadInstance::FlushCallback));
196 void GamepadInstance::FlushCallback(int32_t result) {
197 set_flush_pending(false);
201 class GamepadModule : public pp::Module {
203 GamepadModule() : pp::Module() {}
204 virtual ~GamepadModule() {}
206 virtual pp::Instance* CreateInstance(PP_Instance instance) {
207 return new GamepadInstance(instance);
212 Module* CreateModule() { return new GamepadModule(); }