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 28class 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 73GamepadInstance::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 86GamepadInstance::~GamepadInstance() { 87 DestroyContext(); 88 delete pixel_buffer_; 89} 90 91void 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 112void 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 128void 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 168void 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 177void GamepadInstance::DestroyContext() { 178 if (!IsContextValid()) 179 return; 180 delete graphics_2d_context_; 181 graphics_2d_context_ = NULL; 182} 183 184void 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 197void GamepadInstance::FlushCallback(int32_t result) { 198 set_flush_pending(false); 199 Paint(); 200} 201 202class 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 212namespace pp { 213Module* CreateModule() { return new GamepadModule(); } 214} // namespace pp 215