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.
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"
24 // Allow 'this' in initializer list
25 #pragma warning(disable : 4355)
28 class GamepadInstance
: public pp::Instance
{
30 explicit GamepadInstance(PP_Instance instance
);
31 virtual ~GamepadInstance();
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
);
37 // Flushes its contents of |pixel_buffer_| to the 2D graphics context.
41 return pixel_buffer_
? pixel_buffer_
->size().width() : 0;
44 return pixel_buffer_
? pixel_buffer_
->size().height() : 0;
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
; }
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
60 void FlushPixelBuffer();
62 void FlushCallback(int32_t result
);
64 bool IsContextValid() const { return graphics_2d_context_
!= NULL
; }
66 pp::CompletionCallbackFactory
<GamepadInstance
> callback_factory_
;
67 pp::Graphics2D
* graphics_2d_context_
;
68 pp::ImageData
* pixel_buffer_
;
69 const PPB_Gamepad
* gamepad_
;
73 GamepadInstance::GamepadInstance(PP_Instance instance
)
74 : pp::Instance(instance
),
75 callback_factory_(this),
76 graphics_2d_context_(NULL
),
78 flush_pending_(false) {
79 pp::Module
* module
= pp::Module::Get();
81 gamepad_
= static_cast<const PPB_Gamepad
*>(
82 module
->GetBrowserInterface(PPB_GAMEPAD_INTERFACE
));
86 GamepadInstance::~GamepadInstance() {
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.
97 // Create a new device context with the new size.
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(),
112 void FillRect(pp::ImageData
* image
,
118 for (int y
= std::max(0, top
);
119 y
< std::min(image
->size().height() - 1, top
+ height
);
121 for (int x
= std::max(0, left
);
122 x
< std::min(image
->size().width() - 1, left
+ width
);
124 *image
->GetAddr32(pp::Point(x
, y
)) = color
;
128 void GamepadInstance::Paint() {
129 // Clear the background.
130 FillRect(pixel_buffer_
, 0, 0, width(), height(), 0xfff0f0f0);
132 // Get current gamepad data.
133 PP_GamepadsSampleData gamepad_data
;
134 gamepad_
->Sample(pp_instance(), &gamepad_data
);
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
];
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
);
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
;
160 FillRect(pixel_buffer_
, x
- 3, y
- 3, 7, 7, colour
);
164 // Output to the screen.
168 void GamepadInstance::CreateContext(const pp::Size
& size
) {
169 if (IsContextValid())
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");
177 void GamepadInstance::DestroyContext() {
178 if (!IsContextValid())
180 delete graphics_2d_context_
;
181 graphics_2d_context_
= NULL
;
184 void GamepadInstance::FlushPixelBuffer() {
185 if (!IsContextValid())
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());
192 set_flush_pending(true);
193 graphics_2d_context_
->Flush(
194 callback_factory_
.NewCallback(&GamepadInstance::FlushCallback
));
197 void GamepadInstance::FlushCallback(int32_t result
) {
198 set_flush_pending(false);
202 class GamepadModule
: public pp::Module
{
204 GamepadModule() : pp::Module() {}
205 virtual ~GamepadModule() {}
207 virtual pp::Instance
* CreateInstance(PP_Instance instance
) {
208 return new GamepadInstance(instance
);
213 Module
* CreateModule() { return new GamepadModule(); }