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