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.
10 #include <sys/ioctl.h>
11 #include <sys/types.h>
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"
49 static PSInstance* s_InstanceObject = NULL;
51 PSInstance* PSInstance::GetInstance() {
52 return s_InstanceObject;
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];
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__
86 instance->ExitHandshake(ret);
91 void PSInstance::ExitHandshake(int status) {
92 if (exit_message_ == NULL)
95 RegisterMessageHandler(exit_message_, MessageHandlerExitStatic, this);
97 // Send the exit message to JavaScript. Then wait
98 // for the reply/confirmation.
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[]) {
111 Error("No main defined.\n");
115 Trace("Starting MAIN.\n");
116 int ret = main_cb_(argc, argv);
117 Log("Main thread returned with %d.\n", ret);
122 PSInstance::PSInstance(PP_Instance instance)
123 : pp::Instance(instance),
125 pp::Graphics3DClient(this),
127 events_enabled_(PSE_NONE),
128 verbosity_(PSV_WARN),
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);
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) {
157 bool PSInstance::Init(uint32_t arg,
159 const char* argv[]) {
160 StartInfo* si = new StartInfo;
164 si->argv_ = new char *[arg+1];
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.
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)
190 char* value = new char[strlen(next_arg) + 1];
191 strcpy(value, next_arg);
192 si->argv_[si->argc_++] = value;
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
201 for (uint32_t i = 0; i < arg; i++) {
203 Trace("attribs[%d] '%s=%s'\n", i, argn[i], argv[i]);
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");
218 pthread_t main_thread;
219 int ret = pthread_create(&main_thread, NULL, MainThreadThunk, si);
220 Trace("Created thread: %d.\n", ret);
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);
243 int fd1 = open(getenv("PS_STDOUT"), O_WRONLY);
246 int fd2 = open(getenv("PS_STDERR"), O_WRONLY);
249 tty_prefix_ = getenv("PS_TTY_PREFIX");
251 tty_fd_ = open("/dev/tty", O_WRONLY);
253 RegisterMessageHandler(tty_prefix_, MessageHandlerInputStatic, this);
254 const char* tty_resize = getenv("PS_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);
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);
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);
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
300 setvbuf(stderr, NULL, _IOLBF, 0);
301 setvbuf(stdout, NULL, _IOLBF, 0);
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, ...) {
320 VALog(PSV_TRACE, fmt, ap);
324 void PSInstance::Log(const char *fmt, ...) {
327 VALog(PSV_LOG, fmt, ap);
331 void PSInstance::Warn(const char *fmt, ...) {
334 VALog(PSV_WARN, fmt, ap);
338 void PSInstance::Error(const char *fmt, ...) {
341 VALog(PSV_ERROR, fmt, ap);
345 void PSInstance::SetEnabledEvents(uint32_t mask) {
346 events_enabled_ = mask;
348 static bool warn_once = true;
350 Warn("PSInstance::SetEnabledEvents(mask) where mask == 0 will block\n");
351 Warn("all events. This can come from PSEventSetFilter(PSE_NONE);\n");
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));
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));
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);
382 PSInterfaceCore()->AddRefResource(resource);
384 PSEvent *env = (PSEvent *) malloc(sizeof(PSEvent));
385 memset(env, 0, sizeof(*env));
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));
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";
422 Error("unexpected input key: %s", key_string.c_str());
426 int fd = open(filename, O_RDONLY);
428 Error("error opening file: %s (%s)", filename, strerror(errno));
432 int ret = ioctl(fd, NACL_IOC_HANDLEMESSAGE, &message.pp_var());
434 Error("ioctl on %s failed: %d.\n", filename, ret);
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) {
449 memset(&size, 0, sizeof(size));
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,
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,
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,
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,
489 PSInstance* instance = static_cast<PSInstance*>(user_data);
490 instance->MessageHandlerResize(value);
493 void PSInstance::RegisterMessageHandler(std::string message_name,
494 MessageHandler_t handler,
496 Trace("registering msg handler: %s", message_name.c_str());
497 if (handler == NULL) {
498 message_handlers_.erase(message_name);
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);
511 // If the message is a dictionary then see if it matches one
512 // of the specific handlers, then call that handler rather than
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);
530 PSInterfaceVar()->AddRef(var);
531 PSEvent *env = (PSEvent *) malloc(sizeof(PSEvent));
532 memset(env, 0, sizeof(*env));
535 event_queue_.Enqueue(env);
538 PSEvent* PSInstance::TryAcquireEvent() {
541 event = event_queue_.Dequeue(false);
544 if (events_enabled_ & event->type)
546 // Release filtered events & continue to acquire.
552 PSEvent* PSInstance::WaitAcquireEvent() {
555 event = event_queue_.Dequeue(true);
556 if (events_enabled_ & event->type)
558 // Release filtered events & continue to acquire.
564 void PSInstance::ReleaseEvent(PSEvent* event) {
566 switch(event->type) {
567 case PSE_INSTANCE_HANDLEMESSAGE:
568 PSInterfaceVar()->Release(event->as_var);
570 case PSE_INSTANCE_HANDLEINPUT:
571 case PSE_INSTANCE_DIDCHANGEVIEW:
572 if (event->as_resource) {
573 PSInterfaceCore()->ReleaseResource(event->as_resource);
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());
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);