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