| OLD | NEW |
| 1 // Copyright 2010 The Native Client SDK Authors. All rights reserved. | 1 // Copyright 2011 The Native Client SDK Authors. All rights reserved. |
| 2 // Use of this source code is governed by a BSD-style license that can | 2 // Use of this source code is governed by a BSD-style license that can |
| 3 // be found in the LICENSE file. | 3 // be found in the LICENSE file. |
| 4 | 4 |
| 5 #include <assert.h> | 5 #include "examples/graphics/life/life.h" |
| 6 #include <math.h> | 6 |
| 7 #include <stdlib.h> | 7 #include <ppapi/cpp/completion_callback.h> |
| 8 #include <string.h> | 8 #include <ppapi/cpp/var.h> |
| 9 | 9 |
| 10 #include <algorithm> | 10 #include <algorithm> |
| 11 #include <cassert> |
| 12 #include <cmath> |
| 13 #include <cstdio> |
| 14 #include <cstring> |
| 11 #include <string> | 15 #include <string> |
| 12 | 16 |
| 13 #include <nacl/nacl_npapi.h> | |
| 14 #include <nacl/npapi_extensions.h> | |
| 15 #include <nacl/npupp.h> | |
| 16 | |
| 17 #define JS_LOG(msg) \ | |
| 18 const size_t line_number = __LINE__; \ | |
| 19 size_t len = floor(line_number) + 1; \ | |
| 20 len = std::string(msg).size() + strlen(__FILE__) + len + 5; \ | |
| 21 char buffer[len]; \ | |
| 22 memset(buffer, 0, len); \ | |
| 23 snprintf(buffer, len, "%s:%i - ", __FILE__, line_number); \ | |
| 24 strncat(buffer, std::string(msg).c_str(), len - strlen(buffer)); \ | |
| 25 Log(npp_, buffer); | |
| 26 | |
| 27 namespace { | 17 namespace { |
| 28 // Log given message to javascript console. | 18 const char* const kUpdateMethodId = "update"; |
| 29 bool Log(NPP npp, const char* msg, ...) { | 19 const char* const kAddCellAtPointMethodId = "addCellAtPoint"; |
| 30 bool rv = false; | 20 const unsigned int kInitialRandSeed = 0xC0DE533D; |
| 31 NPObject* window = NULL; | |
| 32 if (NPERR_NO_ERROR == NPN_GetValue(npp, NPNVWindowNPObject, &window)) { | |
| 33 const char buffer[] = "top.console"; | |
| 34 NPString console_stript = { 0 }; | |
| 35 console_stript.UTF8Length = strlen(buffer); | |
| 36 console_stript.UTF8Characters = buffer; | |
| 37 NPVariant console; | |
| 38 if (NPN_Evaluate(npp, window, &console_stript, &console)) { | |
| 39 if (NPVARIANT_IS_OBJECT(console)) { | |
| 40 // Convert the message to NPString; | |
| 41 NPVariant text; | |
| 42 STRINGN_TO_NPVARIANT(msg, static_cast<uint32_t>(strlen(msg)), | |
| 43 text); | |
| 44 NPVariant result; | |
| 45 if (NPN_Invoke(npp, NPVARIANT_TO_OBJECT(console), | |
| 46 NPN_GetStringIdentifier("log"), &text, 1, &result)) { | |
| 47 NPN_ReleaseVariantValue(&result); | |
| 48 rv = true; | |
| 49 } | |
| 50 } | |
| 51 NPN_ReleaseVariantValue(&console); | |
| 52 } | |
| 53 } | |
| 54 return rv; | |
| 55 } | |
| 56 | |
| 57 // seed for rand_r() - we only call rand_r from main thread. | |
| 58 static unsigned int gSeed = 0xC0DE533D; | |
| 59 | |
| 60 // random number helper | |
| 61 // binary rand() returns 0 or 1 | |
| 62 inline unsigned char brand() { | |
| 63 return static_cast<unsigned char>(rand_r(&gSeed) & 1); | |
| 64 } | |
| 65 | 21 |
| 66 inline uint32_t MakeRGBA(uint32_t r, uint32_t g, uint32_t b, uint32_t a) { | 22 inline uint32_t MakeRGBA(uint32_t r, uint32_t g, uint32_t b, uint32_t a) { |
| 67 return (((a) << 24) | ((r) << 16) | ((g) << 8) | (b)); | 23 return (((a) << 24) | ((r) << 16) | ((g) << 8) | (b)); |
| 68 } | 24 } |
| 69 | 25 |
| 70 void FlushCallback(NPP instance, NPDeviceContext* context, | 26 // Map of neighboring colors. |
| 71 NPError err, void* user_data) { | 27 const uint32_t kNeighborColors[] = { |
| 72 } | 28 MakeRGBA(0x00, 0x00, 0x00, 0xff), |
| 73 | 29 MakeRGBA(0x00, 0x40, 0x00, 0xff), |
| 74 void* life(void* data); | 30 MakeRGBA(0x00, 0x60, 0x00, 0xff), |
| 75 | 31 MakeRGBA(0x00, 0x80, 0x00, 0xff), |
| 76 // Life class holds information and functionality needed to render | 32 MakeRGBA(0x00, 0xA0, 0x00, 0xff), |
| 77 // life into an Pepper 2D surface. | 33 MakeRGBA(0x00, 0xC0, 0x00, 0xff), |
| 78 class Life : public NPObject { | 34 MakeRGBA(0x00, 0xE0, 0x00, 0xff), |
| 79 public: | 35 MakeRGBA(0x00, 0x00, 0x00, 0xff), |
| 80 Life(NPP npp); | 36 MakeRGBA(0x00, 0x40, 0x00, 0xff), |
| 81 ~Life(); | 37 MakeRGBA(0x00, 0x60, 0x00, 0xff), |
| 82 NPError SetWindow(NPWindow* window); | 38 MakeRGBA(0x00, 0x80, 0x00, 0xff), |
| 83 void Update(); | 39 MakeRGBA(0x00, 0xA0, 0x00, 0xff), |
| 84 void Plot(int x, int y); | 40 MakeRGBA(0x00, 0xC0, 0x00, 0xff), |
| 85 void Stir(); | 41 MakeRGBA(0x00, 0xE0, 0x00, 0xff), |
| 86 void Draw(); | 42 MakeRGBA(0x00, 0xFF, 0x00, 0xff), |
| 87 void UpdateCells(); | 43 MakeRGBA(0x00, 0xFF, 0x00, 0xff), |
| 88 void Swap(); | 44 MakeRGBA(0x00, 0xFF, 0x00, 0xff), |
| 89 void HandleEvent(NPPepperEvent* event); | 45 MakeRGBA(0x00, 0xFF, 0x00, 0xff), |
| 90 | |
| 91 private: | |
| 92 void CreateContext(); | |
| 93 void DestroyContext(); | |
| 94 bool IsContextValid() { | |
| 95 return context2d_.region != NULL; | |
| 96 } | |
| 97 int width() const { | |
| 98 return width_; | |
| 99 } | |
| 100 int height() const { | |
| 101 return height_; | |
| 102 } | |
| 103 void* pixels() { | |
| 104 return context2d_.region; | |
| 105 } | |
| 106 | |
| 107 NPP npp_; | |
| 108 NPExtensions* extensions_; | |
| 109 int width_, height_; | |
| 110 NPDevice* device2d_; // The PINPAPI 2D device. | |
| 111 NPDeviceContext2D context2d_; // The PINPAPI 2D drawing context. | |
| 112 bool scribble_; | |
| 113 bool quit_; | |
| 114 char *cell_in_; | |
| 115 char *cell_out_; | |
| 116 }; | 46 }; |
| 117 | 47 |
| 118 Life::Life(NPP npp) : npp_(npp), extensions_(NULL), width_(0), height_(0), | 48 // These represent the new health value of a cell based on its neighboring |
| 119 device2d_(NULL), cell_in_(NULL), cell_out_(NULL) { | 49 // values. The health is binary: either alive or dead. |
| 120 memset(&context2d_, 0, sizeof(context2d_)); | 50 const uint8_t kIsAlive[] = { |
| 121 NPN_GetValue(npp_, NPNVPepperExtensions, &extensions_); | 51 0, 0, 0, 1, 0, 0, 0, 0, 0, // Values if the center cell is dead. |
| 122 device2d_ = extensions_->acquireDevice(npp_, NPPepper2DDevice); | 52 0, 0, 1, 1, 0, 0, 0, 0, 0 // Values if the center cell is alive. |
| 123 assert(extensions_); | 53 }; |
| 54 |
| 55 void FlushCallback(void* data, int32_t result) { |
| 56 static_cast<life::Life*>(data)->set_flush_pending(false); |
| 57 } |
| 58 } // namespace |
| 59 |
| 60 namespace life { |
| 61 Life::Life(PP_Instance instance) : pp::Instance(instance), |
| 62 graphics_2d_context_(NULL), |
| 63 pixel_buffer_(NULL), |
| 64 random_bits_(kInitialRandSeed), |
| 65 flush_pending_(false), |
| 66 cell_in_(NULL), |
| 67 cell_out_(NULL) { |
| 124 } | 68 } |
| 125 | 69 |
| 126 Life::~Life() { | 70 Life::~Life() { |
| 127 delete[] cell_in_; | 71 delete[] cell_in_; |
| 128 delete[] cell_out_; | 72 delete[] cell_out_; |
| 129 DestroyContext(); | 73 DestroyContext(); |
| 74 delete pixel_buffer_; |
| 130 } | 75 } |
| 131 | 76 |
| 132 void Life::CreateContext() { | 77 pp::Var Life::GetInstanceObject() { |
| 133 if (IsContextValid()) | 78 LifeScriptObject* script_object = new LifeScriptObject(this); |
| 134 return; | 79 return pp::Var(this, script_object); |
| 135 device2d_ = extensions_->acquireDevice(npp_, NPPepper2DDevice); | |
| 136 assert(device2d_); | |
| 137 NPDeviceContext2DConfig config; | |
| 138 NPError init_err = device2d_->initializeContext(npp_, &config, &context2d_); | |
| 139 assert(NPERR_NO_ERROR == init_err); | |
| 140 } | 80 } |
| 141 | 81 |
| 142 void Life::HandleEvent(NPPepperEvent* event) { | 82 bool Life::Init(uint32_t argc, const char* argn[], const char* argv[]) { |
| 143 bool plot = false; | 83 return true; |
| 144 if (event->type == NPEventType_MouseDown) { | |
| 145 scribble_ = true; | |
| 146 plot = true; | |
| 147 } | |
| 148 if (event->type == NPEventType_MouseUp) { | |
| 149 JS_LOG("MouseUp event"); | |
| 150 scribble_ = false; | |
| 151 } | |
| 152 if (event->type == NPEventType_MouseMove) { | |
| 153 plot = scribble_; | |
| 154 } | |
| 155 if (plot) { | |
| 156 // place a blob of life | |
| 157 Plot(event->u.mouse.x - 1, event->u.mouse.y - 1); | |
| 158 Plot(event->u.mouse.x + 0, event->u.mouse.y - 1); | |
| 159 Plot(event->u.mouse.x + 1, event->u.mouse.y - 1); | |
| 160 Plot(event->u.mouse.x - 1, event->u.mouse.y + 0); | |
| 161 Plot(event->u.mouse.x + 0, event->u.mouse.y + 0); | |
| 162 Plot(event->u.mouse.x + 1, event->u.mouse.y + 0); | |
| 163 Plot(event->u.mouse.x - 1, event->u.mouse.y + 1); | |
| 164 Plot(event->u.mouse.x + 0, event->u.mouse.y + 1); | |
| 165 Plot(event->u.mouse.x + 1, event->u.mouse.y + 1); | |
| 166 } | |
| 167 } | |
| 168 | |
| 169 void Life::DestroyContext() { | |
| 170 if (!IsContextValid()) | |
| 171 return; | |
| 172 device2d_->destroyContext(npp_, &context2d_); | |
| 173 } | 84 } |
| 174 | 85 |
| 175 void Life::Plot(int x, int y) { | 86 void Life::Plot(int x, int y) { |
| 87 if (cell_in_ == NULL) |
| 88 return; |
| 176 if (x < 0) return; | 89 if (x < 0) return; |
| 177 if (x >= width()) return; | 90 if (x >= width()) return; |
| 178 if (y < 0) return; | 91 if (y < 0) return; |
| 179 if (y >= height()) return; | 92 if (y >= height()) return; |
| 180 *(cell_in_ + x + y * width()) = 1; | 93 *(cell_in_ + x + y * width()) = 1; |
| 181 } | 94 } |
| 182 | 95 |
| 183 NPError Life::SetWindow(NPWindow* window) { | 96 void Life::DidChangeView(const pp::Rect& position, const pp::Rect& clip) { |
| 184 if (!window) | 97 if (position.size().width() == width() && |
| 185 return NPERR_NO_ERROR; | 98 position.size().height() == height()) |
| 186 width_ = window->width; | 99 return; // Size didn't change, no need to update anything. |
| 187 height_ = window->height; | |
| 188 if (!IsContextValid()) | |
| 189 CreateContext(); | |
| 190 | 100 |
| 191 const size_t size = width() * height(); | 101 // Create a new device context with the new size. |
| 102 DestroyContext(); |
| 103 CreateContext(position.size()); |
| 104 // Delete the old pixel buffer and create a new one. |
| 105 delete pixel_buffer_; |
| 192 delete[] cell_in_; | 106 delete[] cell_in_; |
| 193 delete[] cell_out_; | 107 delete[] cell_out_; |
| 194 cell_in_ = new char[size]; | 108 pixel_buffer_ = NULL; |
| 195 cell_out_ = new char[size]; | 109 cell_in_ = cell_out_ = NULL; |
| 196 std::fill(cell_in_, cell_in_ + size, 0); | 110 if (graphics_2d_context_ != NULL) { |
| 197 std::fill(cell_out_, cell_out_ + size, 0); | 111 pixel_buffer_ = new pp::ImageData(this, |
| 198 | 112 PP_IMAGEDATAFORMAT_BGRA_PREMUL, |
| 199 NPDeviceFlushContextCallbackPtr callback = | 113 graphics_2d_context_->size(), |
| 200 reinterpret_cast<NPDeviceFlushContextCallbackPtr>(&FlushCallback); | 114 false); |
| 201 device2d_->flushContext(npp_, &context2d_, callback, NULL); | 115 const size_t size = width() * height(); |
| 202 return NPERR_NO_ERROR; | 116 cell_in_ = new uint8_t[size]; |
| 117 cell_out_ = new uint8_t[size]; |
| 118 std::fill(cell_in_, cell_in_ + size, 0); |
| 119 std::fill(cell_out_, cell_out_ + size, 0); |
| 120 } |
| 203 } | 121 } |
| 204 | 122 |
| 205 void Life::Update() { | 123 void Life::Update() { |
| 206 Stir(); | 124 Stir(); |
| 207 UpdateCells(); | 125 UpdateCells(); |
| 208 Swap(); | 126 Swap(); |
| 209 NPDeviceFlushContextCallbackPtr callback = | 127 FlushPixelBuffer(); |
| 210 reinterpret_cast<NPDeviceFlushContextCallbackPtr>(&FlushCallback); | 128 } |
| 211 device2d_->flushContext(npp_, &context2d_, callback, NULL); | 129 |
| 130 void Life::AddCellAtPoint(const pp::Var& var_x, const pp::Var& var_y) { |
| 131 if (!var_x.is_number() || !var_y.is_number()) |
| 132 return; |
| 133 int32_t x, y; |
| 134 x = var_x.is_int() ? var_x.AsInt() : static_cast<int32_t>(var_x.AsDouble()); |
| 135 y = var_y.is_int() ? var_y.AsInt() : static_cast<int32_t>(var_y.AsDouble()); |
| 136 Plot(x - 1, y - 1); |
| 137 Plot(x + 0, y - 1); |
| 138 Plot(x + 1, y - 1); |
| 139 Plot(x - 1, y + 0); |
| 140 Plot(x + 0, y + 0); |
| 141 Plot(x + 1, y + 0); |
| 142 Plot(x - 1, y + 1); |
| 143 Plot(x + 0, y + 1); |
| 144 Plot(x + 1, y + 1); |
| 212 } | 145 } |
| 213 | 146 |
| 214 void Life::Stir() { | 147 void Life::Stir() { |
| 148 if (cell_in_ == NULL || cell_out_ == NULL) |
| 149 return; |
| 215 const int height = this->height(); | 150 const int height = this->height(); |
| 216 const int width = this->width(); | 151 const int width = this->width(); |
| 217 for (int i = 0; i < width; ++i) { | 152 for (int i = 0; i < width; ++i) { |
| 218 cell_in_[i] = brand(); | 153 cell_in_[i] = random_bits_.value(); |
| 219 cell_in_[i + (height - 1) * width] = brand(); | 154 cell_in_[i + (height - 1) * width] = random_bits_.value(); |
| 220 } | 155 } |
| 221 for (int i = 0; i < height; ++i) { | 156 for (int i = 0; i < height; ++i) { |
| 222 cell_in_[i * width] = brand(); | 157 cell_in_[i * width] = random_bits_.value(); |
| 223 cell_in_[i * width + (width - 2)] = brand(); | 158 cell_in_[i * width + (width - 1)] = random_bits_.value(); |
| 224 } | 159 } |
| 225 } | 160 } |
| 226 | 161 |
| 227 void Life::UpdateCells() { | 162 void Life::UpdateCells() { |
| 228 // map neighbor count to color | 163 if (cell_in_ == NULL || cell_out_ == NULL || pixels() == NULL) |
| 229 static unsigned int colors[18] = { | 164 return; |
| 230 MakeRGBA(0x00, 0x00, 0x00, 0xff), | |
| 231 MakeRGBA(0x00, 0x40, 0x00, 0xff), | |
| 232 MakeRGBA(0x00, 0x60, 0x00, 0xff), | |
| 233 MakeRGBA(0x00, 0x80, 0x00, 0xff), | |
| 234 MakeRGBA(0x00, 0xA0, 0x00, 0xff), | |
| 235 MakeRGBA(0x00, 0xC0, 0x00, 0xff), | |
| 236 MakeRGBA(0x00, 0xE0, 0x00, 0xff), | |
| 237 MakeRGBA(0x00, 0x00, 0x00, 0xff), | |
| 238 MakeRGBA(0x00, 0x40, 0x00, 0xff), | |
| 239 MakeRGBA(0x00, 0x60, 0x00, 0xff), | |
| 240 MakeRGBA(0x00, 0x80, 0x00, 0xff), | |
| 241 MakeRGBA(0x00, 0xA0, 0x00, 0xff), | |
| 242 MakeRGBA(0x00, 0xC0, 0x00, 0xff), | |
| 243 MakeRGBA(0x00, 0xE0, 0x00, 0xff), | |
| 244 MakeRGBA(0x00, 0xFF, 0x00, 0xff), | |
| 245 MakeRGBA(0x00, 0xFF, 0x00, 0xff), | |
| 246 MakeRGBA(0x00, 0xFF, 0x00, 0xff), | |
| 247 MakeRGBA(0x00, 0xFF, 0x00, 0xff), | |
| 248 }; | |
| 249 // map neighbor count to alive/dead | |
| 250 static char replace[18] = { | |
| 251 0, 0, 0, 1, 0, 0, 0, 0, // row for center cell dead | |
| 252 0, 0, 1, 1, 0, 0, 0, 0, // row for center cell alive | |
| 253 }; | |
| 254 const int height = this->height(); | 165 const int height = this->height(); |
| 255 const int width = this->width(); | 166 const int width = this->width(); |
| 256 // do neighbor sumation; apply rules, output pixel color | 167 // Do neighbor sumation; apply rules, output pixel color. |
| 257 for (int y = 1; y < (height - 1); ++y) { | 168 for (int y = 1; y < (height - 1); ++y) { |
| 258 char *src0 = cell_in_ + (y - 1) * width; | 169 uint8_t *src0 = (cell_in_ + (y - 1) * width) + 1; |
| 259 char *src1 = cell_in_ + (y) * width; | 170 uint8_t *src1 = src0 + width; |
| 260 char *src2 = cell_in_ + (y + 1) * width; | 171 uint8_t *src2 = src1 + width; |
| 261 int count; | 172 int count; |
| 262 unsigned int color; | 173 uint32_t color; |
| 263 char *dst = cell_out_ + (y) * width; | 174 uint8_t *dst = (cell_out_ + (y) * width) + 1; |
| 264 uint32_t *pixels = static_cast<uint32_t*>(this->pixels()) + y * width; | 175 uint32_t *pixel_buffer = pixels() + y * width; |
| 265 for (int x = 1; x < (width - 1); ++x) { | 176 for (int x = 1; x < (width - 1); ++x) { |
| 266 // build sum, weight center by 8x | 177 // Build sum, weight center by 9x. |
| 267 count = src0[-1] + src0[0] + src0[1] + | 178 count = src0[-1] + src0[0] + src0[1] + |
| 268 src1[-1] + src1[0] * 8 + src1[1] + | 179 src1[-1] + src1[0] * 9 + src1[1] + |
| 269 src2[-1] + src2[0] + src2[1]; | 180 src2[-1] + src2[0] + src2[1]; |
| 270 color = colors[count]; | 181 color = kNeighborColors[count]; |
| 271 *pixels++ = color; | 182 *pixel_buffer++ = color; |
| 272 *dst++ = replace[count]; | 183 *dst++ = kIsAlive[count]; |
| 273 ++src0, ++src1, ++src2; | 184 ++src0; |
| 185 ++src1; |
| 186 ++src2; |
| 274 } | 187 } |
| 275 } | 188 } |
| 276 } | 189 } |
| 277 | 190 |
| 278 void Life::Swap() { | 191 void Life::Swap() { |
| 279 char* tmp = cell_in_; | 192 uint8_t* tmp = cell_in_; |
| 280 cell_in_ = cell_out_; | 193 cell_in_ = cell_out_; |
| 281 cell_out_ = tmp; | 194 cell_out_ = tmp; |
| 282 } | 195 } |
| 283 | 196 |
| 284 extern "C" { | 197 void Life::CreateContext(const pp::Size& size) { |
| 285 | 198 if (IsContextValid()) |
| 286 // The following functions implement functions to be used with npruntime. | 199 return; |
| 287 static NPObject* AllocateLife(NPP npp, NPClass* npclass) { | 200 graphics_2d_context_ = new pp::Graphics2D(this, size, false); |
| 288 Life* rv = new Life(npp); | 201 if (!BindGraphics(*graphics_2d_context_)) { |
| 289 return rv; | 202 printf("Couldn't bind the device context\n"); |
| 203 } |
| 290 } | 204 } |
| 291 | 205 |
| 292 static void Deallocate(NPObject* obj) { | 206 void Life::DestroyContext() { |
| 293 Life* const life = static_cast<Life*>(obj); | 207 if (!IsContextValid()) |
| 294 delete life; | 208 return; |
| 209 delete graphics_2d_context_; |
| 210 graphics_2d_context_ = NULL; |
| 295 } | 211 } |
| 296 | 212 |
| 297 // These are for npruntime. | 213 void Life::FlushPixelBuffer() { |
| 298 static void Invalidate(NPObject*) { | 214 if (!IsContextValid()) |
| 215 return; |
| 216 graphics_2d_context_->PaintImageData(*pixel_buffer_, pp::Point()); |
| 217 if (flush_pending()) |
| 218 return; |
| 219 set_flush_pending(true); |
| 220 graphics_2d_context_->Flush(pp::CompletionCallback(&FlushCallback, this)); |
| 299 } | 221 } |
| 300 | 222 |
| 301 static bool HasMethod(NPObject*, NPIdentifier name) { | 223 bool Life::LifeScriptObject::HasMethod( |
| 302 bool rv = false; | 224 const pp::Var& method, |
| 303 NPUTF8* method_name = NPN_UTF8FromIdentifier(name); | 225 pp::Var* exception) { |
| 304 if (0 == memcmp(method_name, "update", sizeof("update"))) { | 226 if (!method.is_string()) { |
| 305 rv = true; | 227 return false; |
| 306 } | 228 } |
| 307 NPN_MemFree(method_name); | 229 std::string method_name = method.AsString(); |
| 308 return rv; | 230 return method_name == kUpdateMethodId || |
| 231 method_name == kAddCellAtPointMethodId; |
| 309 } | 232 } |
| 310 | 233 |
| 311 static bool Invoke(NPObject *obj, NPIdentifier name, const NPVariant *args, | 234 pp::Var Life::LifeScriptObject::Call( |
| 312 uint32_t argc, NPVariant *result) { | 235 const pp::Var& method, |
| 313 bool rv = false; | 236 const std::vector<pp::Var>& args, |
| 314 NPUTF8* method_name = NPN_UTF8FromIdentifier(name); | 237 pp::Var* exception) { |
| 315 if (0 == memcmp(method_name, "update", sizeof("update"))) { | 238 if (!method.is_string()) { |
| 316 rv = true; | 239 return pp::Var(false); |
| 317 Life* const life = static_cast<Life*>(obj); | |
| 318 life->Update(); | |
| 319 } | 240 } |
| 320 NPN_MemFree(method_name); | 241 std::string method_name = method.AsString(); |
| 321 return rv; | 242 if (app_instance_ != NULL) { |
| 243 if (method_name == kUpdateMethodId) { |
| 244 app_instance_->Update(); |
| 245 } else if (method_name == kAddCellAtPointMethodId) { |
| 246 // Pull off the first two params. |
| 247 if (args.size() < 2) |
| 248 return pp::Var(false); |
| 249 app_instance_->AddCellAtPoint(args[0], args[1]); |
| 250 } |
| 251 } |
| 252 return pp::Var(); |
| 322 } | 253 } |
| 323 | 254 |
| 324 static bool InvokeDefault(NPObject *npobj, const NPVariant *args, | 255 uint8_t Life::RandomBitGenerator::value() { |
| 325 uint32_t argCount, NPVariant *result) { | 256 return static_cast<uint8_t>(rand_r(&random_bit_seed_) & 1); |
| 326 return false; | |
| 327 } | 257 } |
| 328 | 258 |
| 329 static bool HasProperty(NPObject *npobj, NPIdentifier name) { | 259 } // namespace life |
| 330 return false; | |
| 331 } | |
| 332 | 260 |
| 333 static bool GetProperty(NPObject *npobj, NPIdentifier name, NPVariant *result) { | |
| 334 return false; | |
| 335 } | |
| 336 | |
| 337 static bool SetProperty(NPObject *npobj, NPIdentifier name, | |
| 338 const NPVariant *value) { | |
| 339 return false; | |
| 340 } | |
| 341 | |
| 342 static bool RemoveProperty(NPObject *npobj, NPIdentifier name) { | |
| 343 return false; | |
| 344 } | |
| 345 | |
| 346 NPClass np_class = { | |
| 347 NP_CLASS_STRUCT_VERSION, | |
| 348 AllocateLife, | |
| 349 Deallocate, | |
| 350 Invalidate, | |
| 351 HasMethod, | |
| 352 Invoke, | |
| 353 InvokeDefault, | |
| 354 HasProperty, | |
| 355 GetProperty, | |
| 356 SetProperty, | |
| 357 RemoveProperty | |
| 358 }; | |
| 359 | |
| 360 // These functions are required by both the develop and publish versions, | |
| 361 // they are called when a module instance is first loaded, and when the module | |
| 362 // instance is finally deleted. They must use C-style linkage. | |
| 363 NPError NPP_Destroy(NPP instance, NPSavedData** save) { | |
| 364 if (instance == NULL) { | |
| 365 return NPERR_INVALID_INSTANCE_ERROR; | |
| 366 } | |
| 367 | |
| 368 Life* life = static_cast<Life*>(instance->pdata); | |
| 369 if (life != NULL) { | |
| 370 NPN_ReleaseObject(life); | |
| 371 } | |
| 372 return NPERR_NO_ERROR; | |
| 373 } | |
| 374 | |
| 375 // NPP_GetScriptableInstance retruns the NPObject pointer that corresponds to | |
| 376 // NPPVpluginScriptableNPObject queried by NPP_GetValue() from the browser. | |
| 377 NPObject* NPP_GetScriptableInstance(NPP instance) { | |
| 378 if (instance == NULL) { | |
| 379 return NULL; | |
| 380 } | |
| 381 Life* life = static_cast<Life*>(instance->pdata); | |
| 382 return life; | |
| 383 } | |
| 384 | |
| 385 NPError NPP_GetValue(NPP instance, NPPVariable variable, void *value) { | |
| 386 if (NPPVpluginScriptableNPObject == variable) { | |
| 387 NPObject* scriptable_object = NPP_GetScriptableInstance(instance); | |
| 388 if (scriptable_object == NULL) | |
| 389 return NPERR_INVALID_INSTANCE_ERROR; | |
| 390 *reinterpret_cast<NPObject**>(value) = scriptable_object; | |
| 391 return NPERR_NO_ERROR; | |
| 392 } | |
| 393 return NPERR_INVALID_PARAM; | |
| 394 } | |
| 395 | |
| 396 int16_t NPP_HandleEvent(NPP instance, void* event) { | |
| 397 Life* life = static_cast<Life*>(instance->pdata); | |
| 398 if (NULL != life) { | |
| 399 NPPepperEvent* npevent = static_cast<NPPepperEvent*>(event); | |
| 400 life->HandleEvent(npevent); | |
| 401 } | |
| 402 return 0; | |
| 403 } | |
| 404 | |
| 405 NPError NPP_New(NPMIMEType mime_type, | |
| 406 NPP instance, | |
| 407 uint16_t mode, | |
| 408 int16_t argc, | |
| 409 char* argn[], | |
| 410 char* argv[], | |
| 411 NPSavedData* saved) { | |
| 412 if (instance == NULL) { | |
| 413 return NPERR_INVALID_INSTANCE_ERROR; | |
| 414 } | |
| 415 | |
| 416 Life* const life = static_cast<Life*>(NPN_CreateObject(instance, &np_class)); | |
| 417 instance->pdata = life; | |
| 418 return NPERR_NO_ERROR; | |
| 419 } | |
| 420 | |
| 421 NPError NPP_SetWindow(NPP instance, NPWindow* window) { | |
| 422 if (instance == NULL) { | |
| 423 return NPERR_INVALID_INSTANCE_ERROR; | |
| 424 } | |
| 425 if (window == NULL) { | |
| 426 return NPERR_GENERIC_ERROR; | |
| 427 } | |
| 428 Life* life = static_cast<Life*>(instance->pdata); | |
| 429 if (life != NULL) { | |
| 430 return life->SetWindow(window); | |
| 431 } | |
| 432 return NPERR_NO_ERROR; | |
| 433 } | |
| 434 | |
| 435 NPError NP_GetEntryPoints(NPPluginFuncs* plugin_funcs) { | |
| 436 extern NPError InitializePluginFunctions(NPPluginFuncs* plugin_funcs); | |
| 437 return InitializePluginFunctions(plugin_funcs); | |
| 438 } | |
| 439 | |
| 440 NPError InitializePluginFunctions(NPPluginFuncs* plugin_funcs) { | |
| 441 memset(plugin_funcs, 0, sizeof(*plugin_funcs)); | |
| 442 plugin_funcs->version = NPVERS_HAS_PLUGIN_THREAD_ASYNC_CALL; | |
| 443 plugin_funcs->size = sizeof(*plugin_funcs); | |
| 444 plugin_funcs->newp = NPP_New; | |
| 445 plugin_funcs->destroy = NPP_Destroy; | |
| 446 plugin_funcs->setwindow = NPP_SetWindow; | |
| 447 plugin_funcs->event = NPP_HandleEvent; | |
| 448 plugin_funcs->getvalue = NPP_GetValue; | |
| 449 return NPERR_NO_ERROR; | |
| 450 } | |
| 451 | |
| 452 NPError NP_Initialize(NPNetscapeFuncs* browser_functions, | |
| 453 NPPluginFuncs* plugin_functions) { | |
| 454 return NP_GetEntryPoints(plugin_functions); | |
| 455 } | |
| 456 | |
| 457 NPError NP_Shutdown() { | |
| 458 return NPERR_NO_ERROR; | |
| 459 } | |
| 460 | |
| 461 } // extern "C" | |
| 462 } | |
| OLD | NEW |