Updating trunk VERSION from 2139.0 to 2140.0
[chromium-blink-merge.git] / native_client_sdk / src / libraries / ppapi_simple / ps_instance.cc
blobdff9ab264fa9b83e68c319af2e57b25a6f8a42e7
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.
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>
15 #include <algorithm>
16 #include <cstdlib>
17 #include <cstring>
18 #include <sstream>
19 #include <string>
20 #include <vector>
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"
27 #include "ppapi/c/ppb_var.h"
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"
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"
44 #if defined(WIN32)
45 #define open _open
46 #define dup2 _dup2
47 #endif
49 static PSInstance* s_InstanceObject = NULL;
51 PSInstance* PSInstance::GetInstance() {
52 return s_InstanceObject;
55 struct StartInfo {
56 PSInstance* inst_;
57 uint32_t argc_;
58 char** argv_;
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();
71 int ret = instance->MainThread(si->argc_, si->argv_);
73 // Clean up StartInfo.
74 for (uint32_t i = 0; i < si->argc_; i++) {
75 delete[] si->argv_[i];
77 delete[] si->argv_;
78 delete si;
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;
91 void PSInstance::ExitHandshake(int status) {
92 if (exit_message_ == NULL)
93 return;
95 RegisterMessageHandler(exit_message_, MessageHandlerExitStatic, this);
97 // Send the exit message to JavaScript. Then wait
98 // for the reply/confirmation.
99 std::stringstream ss;
100 ss << exit_message_ << ":" << status;
102 pthread_mutex_lock(&exit_lock_);
103 PostMessage(ss.str());
104 pthread_cond_wait(&exit_cond_, &exit_lock_);
105 pthread_mutex_unlock(&exit_lock_);
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;
115 Trace("Starting MAIN.\n");
116 int ret = main_cb_(argc, argv);
117 Log("Main thread returned with %d.\n", ret);
119 return ret;
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) {
133 pthread_mutex_init(&exit_lock_, NULL);
134 pthread_cond_init(&exit_cond_, NULL);
136 // Set the single Instance object
137 s_InstanceObject = this;
139 #ifdef NACL_SDK_DEBUG
140 SetVerbosity(PSV_LOG);
141 #endif
143 RequestInputEvents(PP_INPUTEVENT_CLASS_MOUSE |
144 PP_INPUTEVENT_CLASS_KEYBOARD |
145 PP_INPUTEVENT_CLASS_WHEEL |
146 PP_INPUTEVENT_CLASS_TOUCH);
149 PSInstance::~PSInstance() {
150 s_InstanceObject = NULL;
153 void PSInstance::SetMain(PSMainFunc_t main) {
154 main_cb_ = main;
157 bool PSInstance::Init(uint32_t arg,
158 const char* argn[],
159 const char* argv[]) {
160 StartInfo* si = new StartInfo;
162 si->inst_ = this;
163 si->argc_ = 0;
164 si->argv_ = new char *[arg+1];
165 si->argv_[0] = NULL;
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);
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);
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;
190 char* value = new char[strlen(next_arg) + 1];
191 strcpy(value, next_arg);
192 si->argv_[si->argc_++] = value;
195 PSInterfaceInit();
196 bool props_processed = ProcessProperties();
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]);
209 for (uint32_t i = 0; i < si->argc_; i++) {
210 Trace("argv[%d] '%s'\n", i, si->argv_[i]);
213 if (!props_processed) {
214 Warn("Skipping create thread.\n");
215 return false;
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;
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)));
232 // Enable NaCl IO to map STDIN, STDOUT, and STDERR
233 nacl_io_init_ppapi(PSGetInstanceId(), PSGetInterface);
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);
240 int fd0 = open(getenv("PS_STDIN"), O_RDONLY);
241 dup2(fd0, 0);
243 int fd1 = open(getenv("PS_STDOUT"), O_WRONLY);
244 dup2(fd1, 1);
246 int fd2 = open(getenv("PS_STDERR"), O_WRONLY);
247 dup2(fd2, 2);
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);
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);
274 else if (tty_rows || tty_cols) {
275 Error("PS_TTY_ROWS and PS_TTY_COLS must be set together");
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");
287 RegisterMessageHandler("jspipe1", MessageHandlerInputStatic, this);
288 RegisterMessageHandler("jspipe2", MessageHandlerInputStatic, this);
289 RegisterMessageHandler("jspipe3", MessageHandlerInputStatic, this);
291 exit_message_ = getenv("PS_EXIT_MESSAGE");
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);
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;
306 void PSInstance::SetVerbosity(Verbosity verbosity) {
307 verbosity_ = verbosity;
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);
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);
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);
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);
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);
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;
357 void PSInstance::PostEvent(PSEventType type) {
358 assert(PSE_GRAPHICS3D_GRAPHICS3DCONTEXTLOST == type ||
359 PSE_MOUSELOCK_MOUSELOCKLOST == type);
361 PSEvent *env = (PSEvent *) malloc(sizeof(PSEvent));
362 memset(env, 0, sizeof(*env));
363 env->type = type;
364 event_queue_.Enqueue(env);
367 void PSInstance::PostEvent(PSEventType type, PP_Bool bool_value) {
368 assert(PSE_INSTANCE_DIDCHANGEFOCUS == type);
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);
377 void PSInstance::PostEvent(PSEventType type, PP_Resource resource) {
378 assert(PSE_INSTANCE_HANDLEINPUT == type ||
379 PSE_INSTANCE_DIDCHANGEVIEW == type);
381 if (resource) {
382 PSInterfaceCore()->AddRefResource(resource);
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);
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;
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_);
408 void PSInstance::MessageHandlerInput(const pp::Var& key,
409 const pp::Var& message) {
410 std::string key_string = key.AsString();
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;
426 int fd = open(filename, O_RDONLY);
427 if (fd < 0) {
428 Error("error opening file: %s (%s)", filename, strerror(errno));
429 return;
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;
439 close(fd);
442 void PSInstance::HandleExitStatic(int status, void* user_data) {
443 PSInstance* instance = static_cast<PSInstance*>(user_data);
444 instance->ExitHandshake(status);
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);
455 void PSInstance::MessageHandlerResize(const pp::Var& message) {
456 assert(message.is_array());
457 pp::VarArray array(message);
458 assert(array.GetLength() == 2);
460 int width = array.Get(0).AsInt();
461 int height = array.Get(1).AsInt();
462 HandleResize(width, height);
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);
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);
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);
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);
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;
502 MessageHandler message_handler = { handler, user_data };
503 message_handlers_[message_name] = message_handler;
506 void PSInstance::PostEvent(PSEventType type, const PP_Var& var) {
507 assert(PSE_INSTANCE_HANDLEMESSAGE == type);
509 pp::Var event(var);
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;
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);
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);
549 return event;
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);
561 return event;
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);
575 break;
576 default:
577 break;
579 free(event);
583 void PSInstance::HandleMessage(const pp::Var& message) {
584 Trace("Got Message\n");
585 PostEvent(PSE_INSTANCE_HANDLEMESSAGE, message.pp_var());
588 bool PSInstance::HandleInputEvent(const pp::InputEvent& event) {
589 PostEvent(PSE_INSTANCE_HANDLEINPUT, event.pp_resource());
590 return true;
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());
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);
604 void PSInstance::Graphics3DContextLost() {
605 Log("Graphics3DContextLost\n");
606 PostEvent(PSE_GRAPHICS3D_GRAPHICS3DCONTEXTLOST);
609 void PSInstance::MouseLockLost() {
610 Log("MouseLockLost\n");
611 PostEvent(PSE_MOUSELOCK_MOUSELOCKLOST);