Upstream version 9.38.198.0
[platform/framework/web/crosswalk.git] / src / native_client_sdk / src / libraries / ppapi_simple / ps_instance.cc
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 }