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/types.h>
     11 #include <sys/stat.h>
     12 
     13 #include <algorithm>
     14 #include <cstdlib>
     15 #include <cstring>
     16 #include <sstream>
     17 #include <string>
     18 #include <vector>
     19 
     20 #include "nacl_io/ioctl.h"
     21 #include "nacl_io/kernel_wrap.h"
     22 #include "nacl_io/nacl_io.h"
     23 
     24 #include "ppapi/c/ppb_var.h"
     25 
     26 #include "ppapi/cpp/input_event.h"
     27 #include "ppapi/cpp/message_loop.h"
     28 #include "ppapi/cpp/module.h"
     29 #include "ppapi/cpp/rect.h"
     30 #include "ppapi/cpp/size.h"
     31 #include "ppapi/cpp/touch_point.h"
     32 #include "ppapi/cpp/var.h"
     33 
     34 #include "ppapi_simple/ps_event.h"
     35 #include "ppapi_simple/ps_instance.h"
     36 #include "ppapi_simple/ps_interface.h"
     37 #include "ppapi_simple/ps_main.h"
     38 
     39 #if defined(WIN32)
     40 #define open _open
     41 #define dup2 _dup2
     42 #endif
     43 
     44 static PSInstance* s_InstanceObject = NULL;
     45 
     46 PSInstance* PSInstance::GetInstance() {
     47   return s_InstanceObject;
     48 }
     49 
     50 struct StartInfo {
     51   PSInstance* inst_;
     52   uint32_t argc_;
     53   char** argv_;
     54 };
     55 
     56 
     57 // The starting point for 'main'.  We create this thread to hide the real
     58 // main pepper thread which must never be blocked.
     59 void* PSInstance::MainThreadThunk(void *info) {
     60   s_InstanceObject->Trace("Got MainThreadThunk.\n");
     61   StartInfo* si = static_cast<StartInfo*>(info);
     62   si->inst_->main_loop_ = new pp::MessageLoop(si->inst_);
     63   si->inst_->main_loop_->AttachToCurrentThread();
     64 
     65   int ret = si->inst_->MainThread(si->argc_, si->argv_);
     66   for (uint32_t i = 0; i < si->argc_; i++) {
     67     delete[] si->argv_[i];
     68   }
     69   delete[] si->argv_;
     70   delete si;
     71 
     72   // Exit the entire process once the 'main' thread returns.
     73   // The error code will be available to javascript via
     74   // the exitcode paramater of the crash event.
     75   exit(ret);
     76   return NULL;
     77 }
     78 
     79 // The default implementation supports running a 'C' main.
     80 int PSInstance::MainThread(int argc, char *argv[]) {
     81   if (!main_cb_) {
     82     Error("No main defined.\n");
     83     return 0;
     84   }
     85 
     86   Trace("Starting MAIN.\n");
     87   int ret = main_cb_(argc, argv);
     88   Log("Main thread returned with %d.\n", ret);
     89   return ret;
     90 }
     91 
     92 PSInstance::PSInstance(PP_Instance instance)
     93     : pp::Instance(instance),
     94       pp::MouseLock(this),
     95       pp::Graphics3DClient(this),
     96       main_loop_(NULL),
     97       events_enabled_(PSE_NONE),
     98       verbosity_(PSV_WARN),
     99       fd_tty_(-1) {
    100   // Set the single Instance object
    101   s_InstanceObject = this;
    102 
    103 #ifdef NACL_SDK_DEBUG
    104   SetVerbosity(PSV_LOG);
    105 #endif
    106 
    107   RequestInputEvents(PP_INPUTEVENT_CLASS_MOUSE |
    108                      PP_INPUTEVENT_CLASS_KEYBOARD |
    109                      PP_INPUTEVENT_CLASS_WHEEL |
    110                      PP_INPUTEVENT_CLASS_TOUCH);
    111 }
    112 
    113 PSInstance::~PSInstance() {}
    114 
    115 void PSInstance::SetMain(PSMainFunc_t main) {
    116   main_cb_ = main;
    117 }
    118 
    119 bool PSInstance::Init(uint32_t arg,
    120                       const char* argn[],
    121                       const char* argv[]) {
    122   StartInfo* si = new StartInfo;
    123 
    124   si->inst_ = this;
    125   si->argc_ = 0;
    126   si->argv_ = new char *[arg+1];
    127   si->argv_[0] = NULL;
    128 
    129   // Process embed attributes into the environment.
    130   // Converted the attribute names to uppercase as environment variables are
    131   // case sensitive but are almost universally uppercase in practice.
    132   for (uint32_t i = 0; i < arg; i++) {
    133     std::string key = argn[i];
    134     std::transform(key.begin(), key.end(), key.begin(), toupper);
    135     setenv(key.c_str(), argv[i], 1);
    136   }
    137 
    138   // Set a default value for SRC.
    139   setenv("SRC", "NMF?", 0);
    140   // Use the src tag name if ARG0 is not explicitly specified.
    141   setenv("ARG0", getenv("SRC"), 0);
    142 
    143   // Walk ARG0..ARGn populating argv until an argument is missing.
    144   for (;;) {
    145     std::ostringstream arg_stream;
    146     arg_stream << "ARG" << si->argc_;
    147     std::string arg_name = arg_stream.str();
    148     const char* next_arg = getenv(arg_name.c_str());
    149     if (NULL == next_arg)
    150       break;
    151 
    152     char* value = new char[strlen(next_arg) + 1];
    153     strcpy(value, next_arg);
    154     si->argv_[si->argc_++] = value;
    155   }
    156 
    157   PSInterfaceInit();
    158   bool props_processed = ProcessProperties();
    159 
    160   // Log arg values only once ProcessProperties has been
    161   // called so that the ps_verbosity attribute will be in
    162   // effect.
    163   for (uint32_t i = 0; i < arg; i++) {
    164     if (argv[i]) {
    165       Trace("attribs[%d] '%s=%s'\n", i, argn[i], argv[i]);
    166     } else {
    167       Trace("attribs[%d] '%s'\n", i, argn[i]);
    168     }
    169   }
    170 
    171   for (uint32_t i = 0; i < si->argc_; i++) {
    172     Trace("argv[%d] '%s'\n", i, si->argv_[i]);
    173   }
    174 
    175   if (!props_processed) {
    176     Warn("Skipping create thread.\n");
    177     return false;
    178   }
    179 
    180   pthread_t main_thread;
    181   int ret = pthread_create(&main_thread, NULL, MainThreadThunk, si);
    182   Trace("Created thread: %d.\n", ret);
    183   return ret == 0;
    184 }
    185 
    186 // Processes the properties set at compile time via the
    187 // initialization macro, or via dynamically set embed attributes
    188 // through instance DidCreate.
    189 bool PSInstance::ProcessProperties() {
    190   // Set default values
    191   setenv("PS_STDIN", "/dev/stdin", 0);
    192   setenv("PS_STDOUT", "/dev/stdout", 0);
    193   setenv("PS_STDERR", "/dev/console3", 0);
    194 
    195   // Reset verbosity if passed in
    196   const char* verbosity = getenv("PS_VERBOSITY");
    197   if (verbosity) SetVerbosity(static_cast<Verbosity>(atoi(verbosity)));
    198 
    199   // Enable NaCl IO to map STDIN, STDOUT, and STDERR
    200   nacl_io_init_ppapi(PSGetInstanceId(), PSGetInterface);
    201   int fd0 = open(getenv("PS_STDIN"), O_RDONLY);
    202   dup2(fd0, 0);
    203 
    204   int fd1 = open(getenv("PS_STDOUT"), O_WRONLY);
    205   dup2(fd1, 1);
    206 
    207   int fd2 = open(getenv("PS_STDERR"), O_WRONLY);
    208   dup2(fd2, 2);
    209 
    210   const char* tty_prefix = getenv("PS_TTY_PREFIX");
    211   if (tty_prefix) {
    212     fd_tty_ = open("/dev/tty", O_WRONLY);
    213     if (fd_tty_ >= 0) {
    214       ioctl(fd_tty_, TIOCNACLPREFIX, const_cast<char*>(tty_prefix));
    215     } else {
    216       Error("Failed to open /dev/tty.\n");
    217     }
    218   }
    219 
    220   // Set line buffering on stdout and stderr
    221 #if !defined(WIN32)
    222   setvbuf(stderr, NULL, _IOLBF, 0);
    223   setvbuf(stdout, NULL, _IOLBF, 0);
    224 #endif
    225   return true;
    226 }
    227 
    228 void PSInstance::SetVerbosity(Verbosity verbosity) {
    229   verbosity_ = verbosity;
    230 }
    231 
    232 void PSInstance::VALog(Verbosity verbosity, const char *fmt, va_list args) {
    233   if (verbosity <= verbosity_) {
    234     fprintf(stderr, "ps: ");
    235     vfprintf(stderr, fmt, args);
    236   }
    237 }
    238 
    239 void PSInstance::Trace(const char *fmt, ...) {
    240   va_list ap;
    241   va_start(ap, fmt);
    242   VALog(PSV_TRACE, fmt, ap);
    243   va_end(ap);
    244 }
    245 
    246 void PSInstance::Log(const char *fmt, ...) {
    247   va_list ap;
    248   va_start(ap, fmt);
    249   VALog(PSV_LOG, fmt, ap);
    250   va_end(ap);
    251 }
    252 
    253 void PSInstance::Warn(const char *fmt, ...) {
    254   va_list ap;
    255   va_start(ap, fmt);
    256   VALog(PSV_WARN, fmt, ap);
    257   va_end(ap);
    258 }
    259 
    260 void PSInstance::Error(const char *fmt, ...) {
    261   va_list ap;
    262   va_start(ap, fmt);
    263   VALog(PSV_ERROR, fmt, ap);
    264   va_end(ap);
    265 }
    266 
    267 void PSInstance::SetEnabledEvents(uint32_t mask) {
    268   events_enabled_ = mask;
    269   if (mask == 0) {
    270     static bool warn_once = true;
    271     if (warn_once) {
    272       Warn("PSInstance::SetEnabledEvents(mask) where mask == 0 will block\n");
    273       Warn("all events. This can come from PSEventSetFilter(PSE_NONE);\n");
    274       warn_once = false;
    275     }
    276   }
    277 }
    278 
    279 void PSInstance::PostEvent(PSEventType type) {
    280   assert(PSE_GRAPHICS3D_GRAPHICS3DCONTEXTLOST == type ||
    281          PSE_MOUSELOCK_MOUSELOCKLOST == type);
    282 
    283   PSEvent *env = (PSEvent *) malloc(sizeof(PSEvent));
    284   memset(env, 0, sizeof(*env));
    285   env->type = type;
    286   event_queue_.Enqueue(env);
    287 }
    288 
    289 void PSInstance::PostEvent(PSEventType type, PP_Bool bool_value) {
    290   assert(PSE_INSTANCE_DIDCHANGEFOCUS == type);
    291 
    292   PSEvent *env = (PSEvent *) malloc(sizeof(PSEvent));
    293   memset(env, 0, sizeof(*env));
    294   env->type = type;
    295   env->as_bool = bool_value;
    296   event_queue_.Enqueue(env);
    297 }
    298 
    299 void PSInstance::PostEvent(PSEventType type, PP_Resource resource) {
    300   assert(PSE_INSTANCE_HANDLEINPUT == type ||
    301          PSE_INSTANCE_DIDCHANGEVIEW == type);
    302 
    303   if (resource) {
    304     PSInterfaceCore()->AddRefResource(resource);
    305   }
    306   PSEvent *env = (PSEvent *) malloc(sizeof(PSEvent));
    307   memset(env, 0, sizeof(*env));
    308   env->type = type;
    309   env->as_resource = resource;
    310   event_queue_.Enqueue(env);
    311 }
    312 
    313 void PSInstance::PostEvent(PSEventType type, const PP_Var& var) {
    314   assert(PSE_INSTANCE_HANDLEMESSAGE == type);
    315 
    316   // If the user has specified a tty_prefix_ (using ioctl), then we'll give the
    317   // tty node a chance to vacuum up any messages beginning with that prefix. If
    318   // the message does not start with the prefix, the ioctl call will return
    319   // ENOENT and we'll pass the message through to the event queue.
    320   if (fd_tty_ >= 0 && var.type == PP_VARTYPE_STRING) {
    321     uint32_t message_len;
    322     const char* message = PSInterfaceVar()->VarToUtf8(var, &message_len);
    323     std::string message_str(message, message + message_len);
    324 
    325     // Since our message may contain null characters, we can't send it as a
    326     // naked C string, so we package it up in this struct before sending it
    327     // to the ioctl.
    328     struct tioc_nacl_input_string ioctl_message;
    329     ioctl_message.length = message_len;
    330     ioctl_message.buffer = message_str.data();
    331     int ret =
    332       ioctl(fd_tty_, TIOCNACLINPUT, reinterpret_cast<char*>(&ioctl_message));
    333     if (ret != 0 && errno != ENOTTY) {
    334       Error("ioctl returned unexpected error: %d.\n", ret);
    335     }
    336 
    337     return;
    338   }
    339 
    340   PSInterfaceVar()->AddRef(var);
    341   PSEvent *env = (PSEvent *) malloc(sizeof(PSEvent));
    342   memset(env, 0, sizeof(*env));
    343   env->type = type;
    344   env->as_var = var;
    345   event_queue_.Enqueue(env);
    346 }
    347 
    348 PSEvent* PSInstance::TryAcquireEvent() {
    349   PSEvent* event;
    350   while(true) {
    351     event = event_queue_.Dequeue(false);
    352     if (NULL == event)
    353       break;
    354     if (events_enabled_ & event->type)
    355       break;
    356     // Release filtered events & continue to acquire.
    357     ReleaseEvent(event);
    358   }
    359   return event;
    360 }
    361 
    362 PSEvent* PSInstance::WaitAcquireEvent() {
    363   PSEvent* event;
    364   while(true) {
    365     event = event_queue_.Dequeue(true);
    366     if (events_enabled_ & event->type)
    367       break;
    368     // Release filtered events & continue to acquire.
    369     ReleaseEvent(event);
    370   }
    371   return event;
    372 }
    373 
    374 void PSInstance::ReleaseEvent(PSEvent* event) {
    375   if (event) {
    376     switch(event->type) {
    377       case PSE_INSTANCE_HANDLEMESSAGE:
    378         PSInterfaceVar()->Release(event->as_var);
    379         break;
    380       case PSE_INSTANCE_HANDLEINPUT:
    381       case PSE_INSTANCE_DIDCHANGEVIEW:
    382         if (event->as_resource) {
    383           PSInterfaceCore()->ReleaseResource(event->as_resource);
    384         }
    385         break;
    386       default:
    387         break;
    388     }
    389     free(event);
    390   }
    391 }
    392 
    393 void PSInstance::HandleMessage(const pp::Var& message) {
    394   Trace("Got Message\n");
    395   PostEvent(PSE_INSTANCE_HANDLEMESSAGE, message.pp_var());
    396 }
    397 
    398 bool PSInstance::HandleInputEvent(const pp::InputEvent& event) {
    399   PostEvent(PSE_INSTANCE_HANDLEINPUT, event.pp_resource());
    400   return true;
    401 }
    402 
    403 void PSInstance::DidChangeView(const pp::View& view) {
    404   pp::Size new_size = view.GetRect().size();
    405   Log("Got View change: %d,%d\n", new_size.width(), new_size.height());
    406   PostEvent(PSE_INSTANCE_DIDCHANGEVIEW, view.pp_resource());
    407 }
    408 
    409 void PSInstance::DidChangeFocus(bool focus) {
    410   Log("Got Focus change: %s\n", focus ? "FOCUS ON" : "FOCUS OFF");
    411   PostEvent(PSE_INSTANCE_DIDCHANGEFOCUS, focus ? PP_TRUE : PP_FALSE);
    412 }
    413 
    414 void PSInstance::Graphics3DContextLost() {
    415   Log("Graphics3DContextLost\n");
    416   PostEvent(PSE_GRAPHICS3D_GRAPHICS3DCONTEXTLOST);
    417 }
    418 
    419 void PSInstance::MouseLockLost() {
    420   Log("MouseLockLost\n");
    421   PostEvent(PSE_MOUSELOCK_MOUSELOCKLOST);
    422 }
    423