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 <cassert> 9 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" 18 19 #ifdef WIN32 20 #undef min 21 #undef max 22 23 // Allow 'this' in initializer list 24 #pragma warning(disable : 4355) 25 #endif 26 27 class GamepadInstance : public pp::Instance { 28 public: 29 explicit GamepadInstance(PP_Instance instance); 30 virtual ~GamepadInstance(); 31 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); 35 36 // Flushes its contents of |pixel_buffer_| to the 2D graphics context. 37 void Paint(); 38 39 int width() const { 40 return pixel_buffer_ ? pixel_buffer_->size().width() : 0; 41 } 42 int height() const { 43 return pixel_buffer_ ? pixel_buffer_->size().height() : 0; 44 } 45 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; } 50 51 private: 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 58 // and do not flush. 59 void FlushPixelBuffer(); 60 61 void FlushCallback(int32_t result); 62 63 bool IsContextValid() const { return graphics_2d_context_ != NULL; } 64 65 pp::CompletionCallbackFactory<GamepadInstance> callback_factory_; 66 pp::Graphics2D* graphics_2d_context_; 67 pp::ImageData* pixel_buffer_; 68 const PPB_Gamepad* gamepad_; 69 bool flush_pending_; 70 }; 71 72 GamepadInstance::GamepadInstance(PP_Instance instance) 73 : pp::Instance(instance), 74 callback_factory_(this), 75 graphics_2d_context_(NULL), 76 pixel_buffer_(NULL), 77 flush_pending_(false) { 78 pp::Module* module = pp::Module::Get(); 79 assert(module); 80 gamepad_ = static_cast<const PPB_Gamepad*>( 81 module->GetBrowserInterface(PPB_GAMEPAD_INTERFACE)); 82 assert(gamepad_); 83 } 84 85 GamepadInstance::~GamepadInstance() { 86 DestroyContext(); 87 delete pixel_buffer_; 88 } 89 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. 95 96 // Create a new device context with the new size. 97 DestroyContext(); 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(), 106 false); 107 } 108 Paint(); 109 } 110 111 void FillRect(pp::ImageData* image, 112 int left, 113 int top, 114 int width, 115 int height, 116 uint32_t color) { 117 for (int y = std::max(0, top); 118 y < std::min(image->size().height() - 1, top + height); 119 y++) { 120 for (int x = std::max(0, left); 121 x < std::min(image->size().width() - 1, left + width); 122 x++) 123 *image->GetAddr32(pp::Point(x, y)) = color; 124 } 125 } 126 127 void GamepadInstance::Paint() { 128 // Clear the background. 129 FillRect(pixel_buffer_, 0, 0, width(), height(), 0xfff0f0f0); 130 131 // Get current gamepad data. 132 PP_GamepadsSampleData gamepad_data; 133 gamepad_->Sample(pp_instance(), &gamepad_data); 134 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]; 141 142 if (!pad.connected) 143 continue; 144 145 // Draw axes. 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); 151 } 152 153 // Draw buttons. 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; 158 int y = 10; 159 FillRect(pixel_buffer_, x - 3, y - 3, 7, 7, colour); 160 } 161 } 162 163 // Output to the screen. 164 FlushPixelBuffer(); 165 } 166 167 void GamepadInstance::CreateContext(const pp::Size& size) { 168 if (IsContextValid()) 169 return; 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"); 173 } 174 } 175 176 void GamepadInstance::DestroyContext() { 177 if (!IsContextValid()) 178 return; 179 delete graphics_2d_context_; 180 graphics_2d_context_ = NULL; 181 } 182 183 void GamepadInstance::FlushPixelBuffer() { 184 if (!IsContextValid()) 185 return; 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()); 189 if (flush_pending()) 190 return; 191 set_flush_pending(true); 192 graphics_2d_context_->Flush( 193 callback_factory_.NewCallback(&GamepadInstance::FlushCallback)); 194 } 195 196 void GamepadInstance::FlushCallback(int32_t result) { 197 set_flush_pending(false); 198 Paint(); 199 } 200 201 class GamepadModule : public pp::Module { 202 public: 203 GamepadModule() : pp::Module() {} 204 virtual ~GamepadModule() {} 205 206 virtual pp::Instance* CreateInstance(PP_Instance instance) { 207 return new GamepadInstance(instance); 208 } 209 }; 210 211 namespace pp { 212 Module* CreateModule() { return new GamepadModule(); } 213 } // namespace pp 214