Home | History | Annotate | Download | only in ppapi_simple
      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 <errno.h>
      6 #include <fcntl.h>
      7 #include <pthread.h>
      8 #include <stdio.h>
      9 #include <stdlib.h>
     10 #include <sys/ioctl.h>
     11 #include <sys/types.h>
     12 #include <sys/stat.h>
     13 #include <termios.h>
     14 
     15 #include <algorithm>
     16 #include <cstdlib>
     17 #include <cstring>
     18 #include <sstream>
     19 #include <string>
     20 #include <vector>
     21 
     22 #include "nacl_io/ioctl.h"
     23 #include "nacl_io/kernel_wrap.h"
     24 #include "nacl_io/log.h"
     25 #include "nacl_io/nacl_io.h"
     26 
     27 #include "ppapi/c/ppb_var.h"
     28 
     29 #include "ppapi/cpp/input_event.h"
     30 #include "ppapi/cpp/message_loop.h"
     31 #include "ppapi/cpp/module.h"
     32 #include "ppapi/cpp/rect.h"
     33 #include "ppapi/cpp/size.h"
     34 #include "ppapi/cpp/touch_point.h"
     35 #include "ppapi/cpp/var.h"
     36 #include "ppapi/cpp/var_array.h"
     37 #include "ppapi/cpp/var_dictionary.h"
     38 
     39 #include "ppapi_simple/ps_event.h"
     40 #include "ppapi_simple/ps_instance.h"
     41 #include "ppapi_simple/ps_interface.h"
     42 #include "ppapi_simple/ps_main.h"
     43 
     44 #if defined(WIN32)
     45 #define open _open
     46 #define dup2 _dup2
     47 #endif
     48 
     49 static PSInstance* s_InstanceObject = NULL;
     50 
     51 PSInstance* PSInstance::GetInstance() {
     52   return s_InstanceObject;
     53 }
     54 
     55 struct StartInfo {
     56   PSInstance* inst_;
     57   uint32_t argc_;
     58   char** argv_;
     59 };
     60 
     61 
     62 // The starting point for 'main'.  We create this thread to hide the real
     63 // main pepper thread which must never be blocked.
     64 void* PSInstance::MainThreadThunk(void *info) {
     65   s_InstanceObject->Trace("Got MainThreadThunk.\n");
     66   StartInfo* si = static_cast<StartInfo*>(info);
     67   PSInstance* instance = si->inst_;
     68   instance->main_loop_ = new pp::MessageLoop(si->inst_);
     69   instance->main_loop_->AttachToCurrentThread();
     70 
     71   int ret = instance->MainThread(si->argc_, si->argv_);
     72 
     73   // Clean up StartInfo.
     74   for (uint32_t i = 0; i < si->argc_; i++) {
     75     delete[] si->argv_[i];
     76   }
     77   delete[] si->argv_;
     78   delete si;
     79 
     80   // Exit the entire process once the 'main' thread returns.
     81   // The error code will be available to javascript via
     82   // the exitcode parameter of the crash event.
     83 #ifdef __native_client__
     84   exit(ret);
     85 #else
     86   instance->ExitHandshake(ret);
     87 #endif
     88   return NULL;
     89 }
     90 
     91 void PSInstance::ExitHandshake(int status) {
     92   if (exit_message_ == NULL)
     93     return;
     94 
     95   RegisterMessageHandler(exit_message_, MessageHandlerExitStatic, this);
     96 
     97   // Send the exit message to JavaScript. Then wait
     98   // for the reply/confirmation.
     99   std::stringstream ss;
    100   ss << exit_message_ << ":" << status;
    101 
    102   pthread_mutex_lock(&exit_lock_);
    103   PostMessage(ss.str());
    104   pthread_cond_wait(&exit_cond_, &exit_lock_);
    105   pthread_mutex_unlock(&exit_lock_);
    106 }
    107 
    108 // The default implementation supports running a 'C' main.
    109 int 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 
    122 PSInstance::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 
    149 PSInstance::~PSInstance() {
    150   s_InstanceObject = NULL;
    151 }
    152 
    153 void PSInstance::SetMain(PSMainFunc_t main) {
    154   main_cb_ = main;
    155 }
    156 
    157 bool 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.
    227 bool 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 
    306 void PSInstance::SetVerbosity(Verbosity verbosity) {
    307   verbosity_ = verbosity;
    308 }
    309 
    310 void 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 
    317 void 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 
    324 void 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 
    331 void 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 
    338 void 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 
    345 void 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 
    357 void 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 
    367 void 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 
    377 void 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 
    391 ssize_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 
    401 void 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 
    408 void 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 
    442 void PSInstance::HandleExitStatic(int status, void* user_data) {
    443   PSInstance* instance = static_cast<PSInstance*>(user_data);
    444   instance->ExitHandshake(status);
    445 }
    446 
    447 void 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 
    455 void 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 
    465 ssize_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 
    472 void 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 
    479 void 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 
    486 void 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 
    493 void 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 
    506 void 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 
    538 PSEvent* 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 
    552 PSEvent* 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 
    564 void 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 
    583 void PSInstance::HandleMessage(const pp::Var& message) {
    584   Trace("Got Message\n");
    585   PostEvent(PSE_INSTANCE_HANDLEMESSAGE, message.pp_var());
    586 }
    587 
    588 bool PSInstance::HandleInputEvent(const pp::InputEvent& event) {
    589   PostEvent(PSE_INSTANCE_HANDLEINPUT, event.pp_resource());
    590   return true;
    591 }
    592 
    593 void 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 
    599 void 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 
    604 void PSInstance::Graphics3DContextLost() {
    605   Log("Graphics3DContextLost\n");
    606   PostEvent(PSE_GRAPHICS3D_GRAPHICS3DCONTEXTLOST);
    607 }
    608 
    609 void PSInstance::MouseLockLost() {
    610   Log("MouseLockLost\n");
    611   PostEvent(PSE_MOUSELOCK_MOUSELOCKLOST);
    612 }
    613