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>
21 #include "nacl_io/ioctl.h"
22 #include "nacl_io/kernel_wrap.h"
23 #include "nacl_io/nacl_io.h"
25 #include "ppapi/c/ppb_var.h"
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"
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"
45 static PSInstance* s_InstanceObject = NULL;
47 PSInstance* PSInstance::GetInstance() {
48 return s_InstanceObject;
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();
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];
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.
80 // The default implementation supports running a 'C' main.
81 int PSInstance::MainThread(int argc, char *argv[]) {
83 Error("No main defined.\n");
87 Trace("Starting MAIN.\n");
88 int ret = main_cb_(argc, argv);
89 Log("Main thread returned with %d.\n", ret);
93 PSInstance::PSInstance(PP_Instance instance)
94 : pp::Instance(instance),
96 pp::Graphics3DClient(this),
98 events_enabled_(PSE_NONE),
102 // Set the single Instance object
103 s_InstanceObject = this;
105 #ifdef NACL_SDK_DEBUG
106 SetVerbosity(PSV_LOG);
109 RequestInputEvents(PP_INPUTEVENT_CLASS_MOUSE |
110 PP_INPUTEVENT_CLASS_KEYBOARD |
111 PP_INPUTEVENT_CLASS_WHEEL |
112 PP_INPUTEVENT_CLASS_TOUCH);
115 PSInstance::~PSInstance() {}
117 void PSInstance::SetMain(PSMainFunc_t main) {
121 bool PSInstance::Init(uint32_t arg,
123 const char* argv[]) {
124 StartInfo* si = new StartInfo;
128 si->argv_ = new char *[arg+1];
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);
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);
145 // Walk ARG0..ARGn populating argv until an argument is missing.
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)
154 char* value = new char[strlen(next_arg) + 1];
155 strcpy(value, next_arg);
156 si->argv_[si->argc_++] = value;
160 bool props_processed = ProcessProperties();
162 // Log arg values only once ProcessProperties has been
163 // called so that the ps_verbosity attribute will be in
165 for (uint32_t i = 0; i < arg; i++) {
167 Trace("attribs[%d] '%s=%s'\n", i, argn[i], argv[i]);
169 Trace("attribs[%d] '%s'\n", i, argn[i]);
173 for (uint32_t i = 0; i < si->argc_; i++) {
174 Trace("argv[%d] '%s'\n", i, si->argv_[i]);
177 if (!props_processed) {
178 Warn("Skipping create thread.\n");
182 pthread_t main_thread;
183 int ret = pthread_create(&main_thread, NULL, MainThreadThunk, si);
184 Trace("Created thread: %d.\n", ret);
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);
197 // Reset verbosity if passed in
198 const char* verbosity = getenv("PS_VERBOSITY");
199 if (verbosity) SetVerbosity(static_cast<Verbosity>(atoi(verbosity)));
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);
206 int fd1 = open(getenv("PS_STDOUT"), O_WRONLY);
209 int fd2 = open(getenv("PS_STDERR"), O_WRONLY);
212 tty_prefix_ = getenv("PS_TTY_PREFIX");
214 tty_fd_ = open("/dev/tty", O_WRONLY);
216 RegisterMessageHandler(tty_prefix_, MessageHandlerInputStatic, this);
217 const char* tty_resize = getenv("PS_TTY_RESIZE");
219 RegisterMessageHandler(tty_resize, MessageHandlerResizeStatic, this);
221 tioc_nacl_output handler;
222 handler.handler = TtyOutputHandlerStatic;
223 handler.user_data = this;
224 ioctl(tty_fd_, TIOCNACLOUTPUT, reinterpret_cast<char*>(&handler));
226 Error("Failed to open /dev/tty.\n");
230 // Set line buffering on stdout and stderr
232 setvbuf(stderr, NULL, _IOLBF, 0);
233 setvbuf(stdout, NULL, _IOLBF, 0);
238 void PSInstance::SetVerbosity(Verbosity verbosity) {
239 verbosity_ = verbosity;
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);
249 void PSInstance::Trace(const char *fmt, ...) {
252 VALog(PSV_TRACE, fmt, ap);
256 void PSInstance::Log(const char *fmt, ...) {
259 VALog(PSV_LOG, fmt, ap);
263 void PSInstance::Warn(const char *fmt, ...) {
266 VALog(PSV_WARN, fmt, ap);
270 void PSInstance::Error(const char *fmt, ...) {
273 VALog(PSV_ERROR, fmt, ap);
277 void PSInstance::SetEnabledEvents(uint32_t mask) {
278 events_enabled_ = mask;
280 static bool warn_once = true;
282 Warn("PSInstance::SetEnabledEvents(mask) where mask == 0 will block\n");
283 Warn("all events. This can come from PSEventSetFilter(PSE_NONE);\n");
289 void PSInstance::PostEvent(PSEventType type) {
290 assert(PSE_GRAPHICS3D_GRAPHICS3DCONTEXTLOST == type ||
291 PSE_MOUSELOCK_MOUSELOCKLOST == type);
293 PSEvent *env = (PSEvent *) malloc(sizeof(PSEvent));
294 memset(env, 0, sizeof(*env));
296 event_queue_.Enqueue(env);
299 void PSInstance::PostEvent(PSEventType type, PP_Bool bool_value) {
300 assert(PSE_INSTANCE_DIDCHANGEFOCUS == type);
302 PSEvent *env = (PSEvent *) malloc(sizeof(PSEvent));
303 memset(env, 0, sizeof(*env));
305 env->as_bool = bool_value;
306 event_queue_.Enqueue(env);
309 void PSInstance::PostEvent(PSEventType type, PP_Resource resource) {
310 assert(PSE_INSTANCE_HANDLEINPUT == type ||
311 PSE_INSTANCE_DIDCHANGEVIEW == type);
314 PSInterfaceCore()->AddRefResource(resource);
316 PSEvent *env = (PSEvent *) malloc(sizeof(PSEvent));
317 memset(env, 0, sizeof(*env));
319 env->as_resource = resource;
320 event_queue_.Enqueue(env);
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));
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
337 assert(message.is_string());
338 std::string buffer = message.AsString();
340 struct tioc_nacl_input_string ioctl_message;
341 ioctl_message.length = buffer.size();
342 ioctl_message.buffer = buffer.c_str();
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);
350 void PSInstance::MessageHandlerResize(const pp::Var& message) {
351 assert(message.is_array());
352 pp::VarArray array(message);
353 assert(array.GetLength() == 2);
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));
362 ssize_t PSInstance::TtyOutputHandlerStatic(const char* buf,
365 PSInstance* instance = reinterpret_cast<PSInstance*>(user_data);
366 return instance->TtyOutputHandler(buf, count);
369 void PSInstance::MessageHandlerInputStatic(const pp::Var& key,
370 const pp::Var& value,
372 PSInstance* instance = reinterpret_cast<PSInstance*>(user_data);
373 instance->MessageHandlerInput(value);
376 void PSInstance::MessageHandlerResizeStatic(const pp::Var& key,
377 const pp::Var& value,
379 PSInstance* instance = reinterpret_cast<PSInstance*>(user_data);
380 instance->MessageHandlerResize(value);
383 void PSInstance::RegisterMessageHandler(std::string message_name,
384 MessageHandler_t handler,
386 if (handler == NULL) {
387 message_handlers_.erase(message_name);
391 MessageHandler message_handler = { handler, user_data };
392 message_handlers_[message_name] = message_handler;
395 void PSInstance::PostEvent(PSEventType type, const PP_Var& var) {
396 assert(PSE_INSTANCE_HANDLEMESSAGE == type);
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.
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)));
413 // If the message is a dictionary then see if it matches one
414 // of the specific handlers, then call that handler rather than
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);
432 PSInterfaceVar()->AddRef(var);
433 PSEvent *env = (PSEvent *) malloc(sizeof(PSEvent));
434 memset(env, 0, sizeof(*env));
437 event_queue_.Enqueue(env);
440 PSEvent* PSInstance::TryAcquireEvent() {
443 event = event_queue_.Dequeue(false);
446 if (events_enabled_ & event->type)
448 // Release filtered events & continue to acquire.
454 PSEvent* PSInstance::WaitAcquireEvent() {
457 event = event_queue_.Dequeue(true);
458 if (events_enabled_ & event->type)
460 // Release filtered events & continue to acquire.
466 void PSInstance::ReleaseEvent(PSEvent* event) {
468 switch(event->type) {
469 case PSE_INSTANCE_HANDLEMESSAGE:
470 PSInterfaceVar()->Release(event->as_var);
472 case PSE_INSTANCE_HANDLEINPUT:
473 case PSE_INSTANCE_DIDCHANGEVIEW:
474 if (event->as_resource) {
475 PSInterfaceCore()->ReleaseResource(event->as_resource);
485 void PSInstance::HandleMessage(const pp::Var& message) {
486 Trace("Got Message\n");
487 PostEvent(PSE_INSTANCE_HANDLEMESSAGE, message.pp_var());
490 bool PSInstance::HandleInputEvent(const pp::InputEvent& event) {
491 PostEvent(PSE_INSTANCE_HANDLEINPUT, event.pp_resource());
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());
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);
506 void PSInstance::Graphics3DContextLost() {
507 Log("Graphics3DContextLost\n");
508 PostEvent(PSE_GRAPHICS3D_GRAPHICS3DCONTEXTLOST);
511 void PSInstance::MouseLockLost() {
512 Log("MouseLockLost\n");
513 PostEvent(PSE_MOUSELOCK_MOUSELOCKLOST);