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
);