1eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch// Copyright (c) 2012 The Chromium Authors. All rights reserved.
25821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// Use of this source code is governed by a BSD-style license that can be
35821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// found in the LICENSE file.
45821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
55821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include <errno.h>
65821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include <fcntl.h>
75821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include <pthread.h>
85821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include <stdio.h>
95821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include <stdlib.h>
10868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)#include <sys/ioctl.h>
115821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include <sys/types.h>
125821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include <sys/stat.h>
13eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch#include <termios.h>
14eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch
151320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci#include <algorithm>
16eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch#include <cstdlib>
17eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch#include <cstring>
181320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci#include <sstream>
195d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)#include <string>
2058537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)#include <vector>
215821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
225821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "nacl_io/ioctl.h"
235821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "nacl_io/kernel_wrap.h"
245821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "nacl_io/log.h"
251320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci#include "nacl_io/nacl_io.h"
261320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci
275821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "ppapi/c/ppb_var.h"
285821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
295821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "ppapi/cpp/input_event.h"
305821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "ppapi/cpp/message_loop.h"
315821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "ppapi/cpp/module.h"
325821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "ppapi/cpp/rect.h"
335821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "ppapi/cpp/size.h"
345821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "ppapi/cpp/touch_point.h"
355821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "ppapi/cpp/var.h"
365d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)#include "ppapi/cpp/var_array.h"
375821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "ppapi/cpp/var_dictionary.h"
385821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
395821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "ppapi_simple/ps_event.h"
405821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "ppapi_simple/ps_instance.h"
415821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "ppapi_simple/ps_interface.h"
425821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "ppapi_simple/ps_main.h"
435821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
445821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#if defined(WIN32)
455821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#define open _open
465821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#define dup2 _dup2
475821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#endif
485821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
495821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)static PSInstance* s_InstanceObject = NULL;
505821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
515821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)PSInstance* PSInstance::GetInstance() {
525821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  return s_InstanceObject;
535821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
545821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
555821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)struct StartInfo {
565821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  PSInstance* inst_;
575821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  uint32_t argc_;
585821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  char** argv_;
595821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)};
605821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
615821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
625821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// The starting point for 'main'.  We create this thread to hide the real
63eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch// main pepper thread which must never be blocked.
645821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)void* PSInstance::MainThreadThunk(void *info) {
655821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  s_InstanceObject->Trace("Got MainThreadThunk.\n");
66eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch  StartInfo* si = static_cast<StartInfo*>(info);
675821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  PSInstance* instance = si->inst_;
685821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  instance->main_loop_ = new pp::MessageLoop(si->inst_);
695821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  instance->main_loop_->AttachToCurrentThread();
705821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
715821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  int ret = instance->MainThread(si->argc_, si->argv_);
725821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
735821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // Clean up StartInfo.
74a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)  for (uint32_t i = 0; i < si->argc_; i++) {
75a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)    delete[] si->argv_[i];
765821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
775821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  delete[] si->argv_;
78a93a17c8d99d686bd4a1511e5504e5e6cc9fcadfTorne (Richard Coles)  delete si;
795821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
805821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // Exit the entire process once the 'main' thread returns.
815821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // The error code will be available to javascript via
825821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // the exitcode parameter of the crash event.
835821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#ifdef __native_client__
84a93a17c8d99d686bd4a1511e5504e5e6cc9fcadfTorne (Richard Coles)  exit(ret);
855821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#else
865821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  instance->ExitHandshake(ret);
8758537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)#endif
885821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  return NULL;
895821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
90eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch
915821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)void PSInstance::ExitHandshake(int status) {
92eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch  if (exit_message_ == NULL)
93eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch    return;
94eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch
95eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch  RegisterMessageHandler(exit_message_, MessageHandlerExitStatic, this);
961320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci
971320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci  // Send the exit message to JavaScript. Then wait
981320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci  // for the reply/confirmation.
991320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci  std::stringstream ss;
1001320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci  ss << exit_message_ << ":" << status;
1011320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci
1027dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch  pthread_mutex_lock(&exit_lock_);
1037dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch  PostMessage(ss.str());
1047dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch  pthread_cond_wait(&exit_cond_, &exit_lock_);
1057dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch  pthread_mutex_unlock(&exit_lock_);
106}
107
108// The default implementation supports running a 'C' main.
109int PSInstance::MainThread(int argc, char* argv[]) {
110  if (!main_cb_) {
111    Error("No main defined.\n");
112    return 0;
113  }
114
115  Trace("Starting MAIN.\n");
116  int ret = main_cb_(argc, argv);
117  Log("Main thread returned with %d.\n", ret);
118
119  return ret;
120}
121
122PSInstance::PSInstance(PP_Instance instance)
123    : pp::Instance(instance),
124      pp::MouseLock(this),
125      pp::Graphics3DClient(this),
126      main_loop_(NULL),
127      events_enabled_(PSE_NONE),
128      verbosity_(PSV_WARN),
129      tty_fd_(-1),
130      tty_prefix_(NULL),
131      exit_message_(NULL) {
132
133  pthread_mutex_init(&exit_lock_, NULL);
134  pthread_cond_init(&exit_cond_, NULL);
135
136  // Set the single Instance object
137  s_InstanceObject = this;
138
139#ifdef NACL_SDK_DEBUG
140  SetVerbosity(PSV_LOG);
141#endif
142
143  RequestInputEvents(PP_INPUTEVENT_CLASS_MOUSE |
144                     PP_INPUTEVENT_CLASS_KEYBOARD |
145                     PP_INPUTEVENT_CLASS_WHEEL |
146                     PP_INPUTEVENT_CLASS_TOUCH);
147}
148
149PSInstance::~PSInstance() {
150  s_InstanceObject = NULL;
151}
152
153void PSInstance::SetMain(PSMainFunc_t main) {
154  main_cb_ = main;
155}
156
157bool PSInstance::Init(uint32_t arg,
158                      const char* argn[],
159                      const char* argv[]) {
160  StartInfo* si = new StartInfo;
161
162  si->inst_ = this;
163  si->argc_ = 0;
164  si->argv_ = new char *[arg+1];
165  si->argv_[0] = NULL;
166
167  // Process embed attributes into the environment.
168  // Converted the attribute names to uppercase as environment variables are
169  // case sensitive but are almost universally uppercase in practice.
170  for (uint32_t i = 0; i < arg; i++) {
171    std::string key = argn[i];
172    std::transform(key.begin(), key.end(), key.begin(), toupper);
173    setenv(key.c_str(), argv[i], 1);
174  }
175
176  // Set a default value for SRC.
177  setenv("SRC", "NMF?", 0);
178  // Use the src tag name if ARG0 is not explicitly specified.
179  setenv("ARG0", getenv("SRC"), 0);
180
181  // Walk ARG0..ARGn populating argv until an argument is missing.
182  for (;;) {
183    std::ostringstream arg_stream;
184    arg_stream << "ARG" << si->argc_;
185    std::string arg_name = arg_stream.str();
186    const char* next_arg = getenv(arg_name.c_str());
187    if (NULL == next_arg)
188      break;
189
190    char* value = new char[strlen(next_arg) + 1];
191    strcpy(value, next_arg);
192    si->argv_[si->argc_++] = value;
193  }
194
195  PSInterfaceInit();
196  bool props_processed = ProcessProperties();
197
198  // Log arg values only once ProcessProperties has been
199  // called so that the ps_verbosity attribute will be in
200  // effect.
201  for (uint32_t i = 0; i < arg; i++) {
202    if (argv[i]) {
203      Trace("attribs[%d] '%s=%s'\n", i, argn[i], argv[i]);
204    } else {
205      Trace("attribs[%d] '%s'\n", i, argn[i]);
206    }
207  }
208
209  for (uint32_t i = 0; i < si->argc_; i++) {
210    Trace("argv[%d] '%s'\n", i, si->argv_[i]);
211  }
212
213  if (!props_processed) {
214    Warn("Skipping create thread.\n");
215    return false;
216  }
217
218  pthread_t main_thread;
219  int ret = pthread_create(&main_thread, NULL, MainThreadThunk, si);
220  Trace("Created thread: %d.\n", ret);
221  return ret == 0;
222}
223
224// Processes the properties set at compile time via the
225// initialization macro, or via dynamically set embed attributes
226// through instance DidCreate.
227bool PSInstance::ProcessProperties() {
228  // Reset verbosity if passed in
229  const char* verbosity = getenv("PS_VERBOSITY");
230  if (verbosity) SetVerbosity(static_cast<Verbosity>(atoi(verbosity)));
231
232  // Enable NaCl IO to map STDIN, STDOUT, and STDERR
233  nacl_io_init_ppapi(PSGetInstanceId(), PSGetInterface);
234
235  // Set default values
236  setenv("PS_STDIN", "/dev/stdin", 0);
237  setenv("PS_STDOUT", "/dev/stdout", 0);
238  setenv("PS_STDERR", "/dev/console3", 0);
239
240  int fd0 = open(getenv("PS_STDIN"), O_RDONLY);
241  dup2(fd0, 0);
242
243  int fd1 = open(getenv("PS_STDOUT"), O_WRONLY);
244  dup2(fd1, 1);
245
246  int fd2 = open(getenv("PS_STDERR"), O_WRONLY);
247  dup2(fd2, 2);
248
249  tty_prefix_ = getenv("PS_TTY_PREFIX");
250  if (tty_prefix_) {
251    tty_fd_ = open("/dev/tty", O_WRONLY);
252    if (tty_fd_ >= 0) {
253      RegisterMessageHandler(tty_prefix_, MessageHandlerInputStatic, this);
254      const char* tty_resize = getenv("PS_TTY_RESIZE");
255      if (tty_resize)
256        RegisterMessageHandler(tty_resize, MessageHandlerResizeStatic, this);
257
258      char* tty_rows = getenv("PS_TTY_ROWS");
259      char* tty_cols = getenv("PS_TTY_COLS");
260      if (tty_rows && tty_cols) {
261        char* end = tty_rows;
262        int rows = strtol(tty_rows, &end, 10);
263        if (*end != '\0' || rows < 0) {
264          Error("Invalid value for PS_TTY_ROWS: %s", tty_rows);
265        } else {
266          end = tty_cols;
267          int cols = strtol(tty_cols, &end, 10);
268          if (*end != '\0' || cols < 0)
269            Error("Invalid value for PS_TTY_COLS: %s", tty_cols);
270          else
271            HandleResize(cols, rows);
272        }
273      }
274      else if (tty_rows || tty_cols) {
275        Error("PS_TTY_ROWS and PS_TTY_COLS must be set together");
276      }
277
278      tioc_nacl_output handler;
279      handler.handler = TtyOutputHandlerStatic;
280      handler.user_data = this;
281      ioctl(tty_fd_, TIOCNACLOUTPUT, &handler);
282    } else {
283      Error("Failed to open /dev/tty.\n");
284    }
285  }
286
287  RegisterMessageHandler("jspipe1", MessageHandlerInputStatic, this);
288  RegisterMessageHandler("jspipe2", MessageHandlerInputStatic, this);
289  RegisterMessageHandler("jspipe3", MessageHandlerInputStatic, this);
290
291  exit_message_ = getenv("PS_EXIT_MESSAGE");
292
293  // If PS_EXIT_MESSAGE is set in the environment then we perform a handshake
294  // with JavaScript when program exits.
295  if (exit_message_ != NULL)
296    nacl_io_set_exit_callback(HandleExitStatic, this);
297
298  // Set line buffering on stdout and stderr
299#if !defined(WIN32)
300  setvbuf(stderr, NULL, _IOLBF, 0);
301  setvbuf(stdout, NULL, _IOLBF, 0);
302#endif
303  return true;
304}
305
306void PSInstance::SetVerbosity(Verbosity verbosity) {
307  verbosity_ = verbosity;
308}
309
310void PSInstance::VALog(Verbosity verbosity, const char *fmt, va_list args) {
311  if (verbosity <= verbosity_) {
312    fprintf(stderr, "ps: ");
313    vfprintf(stderr, fmt, args);
314  }
315}
316
317void PSInstance::Trace(const char *fmt, ...) {
318  va_list ap;
319  va_start(ap, fmt);
320  VALog(PSV_TRACE, fmt, ap);
321  va_end(ap);
322}
323
324void PSInstance::Log(const char *fmt, ...) {
325  va_list ap;
326  va_start(ap, fmt);
327  VALog(PSV_LOG, fmt, ap);
328  va_end(ap);
329}
330
331void PSInstance::Warn(const char *fmt, ...) {
332  va_list ap;
333  va_start(ap, fmt);
334  VALog(PSV_WARN, fmt, ap);
335  va_end(ap);
336}
337
338void PSInstance::Error(const char *fmt, ...) {
339  va_list ap;
340  va_start(ap, fmt);
341  VALog(PSV_ERROR, fmt, ap);
342  va_end(ap);
343}
344
345void PSInstance::SetEnabledEvents(uint32_t mask) {
346  events_enabled_ = mask;
347  if (mask == 0) {
348    static bool warn_once = true;
349    if (warn_once) {
350      Warn("PSInstance::SetEnabledEvents(mask) where mask == 0 will block\n");
351      Warn("all events. This can come from PSEventSetFilter(PSE_NONE);\n");
352      warn_once = false;
353    }
354  }
355}
356
357void PSInstance::PostEvent(PSEventType type) {
358  assert(PSE_GRAPHICS3D_GRAPHICS3DCONTEXTLOST == type ||
359         PSE_MOUSELOCK_MOUSELOCKLOST == type);
360
361  PSEvent *env = (PSEvent *) malloc(sizeof(PSEvent));
362  memset(env, 0, sizeof(*env));
363  env->type = type;
364  event_queue_.Enqueue(env);
365}
366
367void PSInstance::PostEvent(PSEventType type, PP_Bool bool_value) {
368  assert(PSE_INSTANCE_DIDCHANGEFOCUS == type);
369
370  PSEvent *env = (PSEvent *) malloc(sizeof(PSEvent));
371  memset(env, 0, sizeof(*env));
372  env->type = type;
373  env->as_bool = bool_value;
374  event_queue_.Enqueue(env);
375}
376
377void PSInstance::PostEvent(PSEventType type, PP_Resource resource) {
378  assert(PSE_INSTANCE_HANDLEINPUT == type ||
379         PSE_INSTANCE_DIDCHANGEVIEW == type);
380
381  if (resource) {
382    PSInterfaceCore()->AddRefResource(resource);
383  }
384  PSEvent *env = (PSEvent *) malloc(sizeof(PSEvent));
385  memset(env, 0, sizeof(*env));
386  env->type = type;
387  env->as_resource = resource;
388  event_queue_.Enqueue(env);
389}
390
391ssize_t PSInstance::TtyOutputHandler(const char* buf, size_t count) {
392  // We prepend the prefix_ to the data in buf, then package it up
393  // and post it as a message to javascript.
394  const char* data = static_cast<const char*>(buf);
395  std::string message = tty_prefix_;
396  message.append(data, count);
397  PostMessage(pp::Var(message));
398  return count;
399}
400
401void PSInstance::MessageHandlerExit(const pp::Var& message) {
402  assert(message.is_string());
403  pthread_mutex_lock(&exit_lock_);
404  pthread_cond_signal(&exit_cond_);
405  pthread_mutex_unlock(&exit_lock_);
406}
407
408void PSInstance::MessageHandlerInput(const pp::Var& key,
409                                     const pp::Var& message) {
410  std::string key_string = key.AsString();
411
412  const char* filename = NULL;
413  if (key_string == tty_prefix_) {
414    filename = "/dev/tty";
415  } else if (key_string == "jspipe1") {
416    filename = "/dev/jspipe1";
417  } else if (key_string == "jspipe2") {
418    filename = "/dev/jspipe2";
419  } else if (key_string == "jspipe3") {
420    filename = "/dev/jspipe3";
421  } else {
422    Error("unexpected input key: %s", key_string.c_str());
423    return;
424  }
425
426  int fd = open(filename, O_RDONLY);
427  if (fd < 0) {
428    Error("error opening file: %s (%s)", filename, strerror(errno));
429    return;
430  }
431
432  int ret = ioctl(fd, NACL_IOC_HANDLEMESSAGE, &message.pp_var());
433  if (ret != 0) {
434    Error("ioctl on %s failed: %d.\n", filename, ret);
435    close(fd);
436    return;
437  }
438
439  close(fd);
440}
441
442void PSInstance::HandleExitStatic(int status, void* user_data) {
443  PSInstance* instance = static_cast<PSInstance*>(user_data);
444  instance->ExitHandshake(status);
445}
446
447void PSInstance::HandleResize(int width, int height) {
448  struct winsize size;
449  memset(&size, 0, sizeof(size));
450  size.ws_col = width;
451  size.ws_row = height;
452  ioctl(tty_fd_, TIOCSWINSZ, &size);
453}
454
455void PSInstance::MessageHandlerResize(const pp::Var& message) {
456  assert(message.is_array());
457  pp::VarArray array(message);
458  assert(array.GetLength() == 2);
459
460  int width = array.Get(0).AsInt();
461  int height = array.Get(1).AsInt();
462  HandleResize(width, height);
463}
464
465ssize_t PSInstance::TtyOutputHandlerStatic(const char* buf,
466                                           size_t count,
467                                           void* user_data) {
468  PSInstance* instance = static_cast<PSInstance*>(user_data);
469  return instance->TtyOutputHandler(buf, count);
470}
471
472void PSInstance::MessageHandlerExitStatic(const pp::Var& key,
473                                          const pp::Var& value,
474                                          void* user_data) {
475  PSInstance* instance = static_cast<PSInstance*>(user_data);
476  instance->MessageHandlerExit(value);
477}
478
479void PSInstance::MessageHandlerInputStatic(const pp::Var& key,
480                                           const pp::Var& value,
481                                           void* user_data) {
482  PSInstance* instance = static_cast<PSInstance*>(user_data);
483  instance->MessageHandlerInput(key, value);
484}
485
486void PSInstance::MessageHandlerResizeStatic(const pp::Var& key,
487                                            const pp::Var& value,
488                                            void* user_data) {
489  PSInstance* instance = static_cast<PSInstance*>(user_data);
490  instance->MessageHandlerResize(value);
491}
492
493void PSInstance::RegisterMessageHandler(std::string message_name,
494                                        MessageHandler_t handler,
495                                        void* user_data) {
496  Trace("registering msg handler: %s", message_name.c_str());
497  if (handler == NULL) {
498    message_handlers_.erase(message_name);
499    return;
500  }
501
502  MessageHandler message_handler = { handler, user_data };
503  message_handlers_[message_name] = message_handler;
504}
505
506void PSInstance::PostEvent(PSEventType type, const PP_Var& var) {
507  assert(PSE_INSTANCE_HANDLEMESSAGE == type);
508
509  pp::Var event(var);
510
511  // If the message is a dictionary then see if it matches one
512  // of the specific handlers, then call that handler rather than
513  // queuing an event.
514  if (event.is_dictionary()) {
515    pp::VarDictionary dictionary(var);
516    pp::VarArray keys = dictionary.GetKeys();
517    if (keys.GetLength() == 1) {
518      pp::Var key = keys.Get(0);
519      Trace("calling handler for: %s", key.AsString().c_str());
520      MessageHandlerMap::iterator iter = message_handlers_.find(key.AsString());
521      if (iter != message_handlers_.end()) {
522        MessageHandler_t handler = iter->second.handler;
523        void* user_data = iter->second.user_data;
524        handler(key, dictionary.Get(key), user_data);
525        return;
526      }
527    }
528  }
529
530  PSInterfaceVar()->AddRef(var);
531  PSEvent *env = (PSEvent *) malloc(sizeof(PSEvent));
532  memset(env, 0, sizeof(*env));
533  env->type = type;
534  env->as_var = var;
535  event_queue_.Enqueue(env);
536}
537
538PSEvent* PSInstance::TryAcquireEvent() {
539  PSEvent* event;
540  while(true) {
541    event = event_queue_.Dequeue(false);
542    if (NULL == event)
543      break;
544    if (events_enabled_ & event->type)
545      break;
546    // Release filtered events & continue to acquire.
547    ReleaseEvent(event);
548  }
549  return event;
550}
551
552PSEvent* PSInstance::WaitAcquireEvent() {
553  PSEvent* event;
554  while(true) {
555    event = event_queue_.Dequeue(true);
556    if (events_enabled_ & event->type)
557      break;
558    // Release filtered events & continue to acquire.
559    ReleaseEvent(event);
560  }
561  return event;
562}
563
564void PSInstance::ReleaseEvent(PSEvent* event) {
565  if (event) {
566    switch(event->type) {
567      case PSE_INSTANCE_HANDLEMESSAGE:
568        PSInterfaceVar()->Release(event->as_var);
569        break;
570      case PSE_INSTANCE_HANDLEINPUT:
571      case PSE_INSTANCE_DIDCHANGEVIEW:
572        if (event->as_resource) {
573          PSInterfaceCore()->ReleaseResource(event->as_resource);
574        }
575        break;
576      default:
577        break;
578    }
579    free(event);
580  }
581}
582
583void PSInstance::HandleMessage(const pp::Var& message) {
584  Trace("Got Message\n");
585  PostEvent(PSE_INSTANCE_HANDLEMESSAGE, message.pp_var());
586}
587
588bool PSInstance::HandleInputEvent(const pp::InputEvent& event) {
589  PostEvent(PSE_INSTANCE_HANDLEINPUT, event.pp_resource());
590  return true;
591}
592
593void PSInstance::DidChangeView(const pp::View& view) {
594  pp::Size new_size = view.GetRect().size();
595  Log("Got View change: %d,%d\n", new_size.width(), new_size.height());
596  PostEvent(PSE_INSTANCE_DIDCHANGEVIEW, view.pp_resource());
597}
598
599void PSInstance::DidChangeFocus(bool focus) {
600  Log("Got Focus change: %s\n", focus ? "FOCUS ON" : "FOCUS OFF");
601  PostEvent(PSE_INSTANCE_DIDCHANGEFOCUS, focus ? PP_TRUE : PP_FALSE);
602}
603
604void PSInstance::Graphics3DContextLost() {
605  Log("Graphics3DContextLost\n");
606  PostEvent(PSE_GRAPHICS3D_GRAPHICS3DCONTEXTLOST);
607}
608
609void PSInstance::MouseLockLost() {
610  Log("MouseLockLost\n");
611  PostEvent(PSE_MOUSELOCK_MOUSELOCKLOST);
612}
613