Merge Chromium + Blink git repositories
[chromium-blink-merge.git] / native_client_sdk / src / libraries / ppapi_simple / ps_instance.c
blob3374736249cbbfdc9e33b96b23eaf3bd0127c983
1 /* Copyright 2015 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 "ppapi_simple/ps_instance.h"
7 #include <alloca.h>
8 #include <assert.h>
9 #include <ctype.h>
10 #include <errno.h>
11 #include <fcntl.h>
12 #include <pthread.h>
13 #include <stdio.h>
14 #include <stdlib.h>
15 #include <string.h>
16 #include <sys/ioctl.h>
17 #include <termios.h> /* Needed for struct winsize in bionic */
18 #include <unistd.h>
20 #include <ppapi/c/pp_errors.h>
21 #include <ppapi/c/pp_instance.h>
22 #include <ppapi/c/pp_module.h>
23 #include <ppapi/c/pp_rect.h>
24 #include <ppapi/c/pp_size.h>
25 #include <ppapi/c/ppb.h>
26 #include <ppapi/c/ppp.h>
27 #include <ppapi/c/ppp_graphics_3d.h>
28 #include <ppapi/c/ppp_input_event.h>
29 #include <ppapi/c/ppp_instance.h>
30 #include <ppapi/c/ppp_messaging.h>
31 #include <ppapi/c/ppp_mouse_lock.h>
33 #include "nacl_io/ioctl.h"
34 #include "nacl_io/nacl_io.h"
35 #include "nacl_io/log.h"
36 #include "ppapi_simple/ps_interface.h"
37 #include "ppapi_simple/ps_internal.h"
38 #include "ppapi_simple/ps_main.h"
40 struct StartInfo {
41 uint32_t argc_;
42 char** argv_;
45 PP_Instance g_ps_instance;
46 PPB_GetInterface g_ps_get_interface;
47 PSMainFunc_t g_ps_main_cb;
49 static enum PSVerbosity s_verbosity;
51 /* TTY handling */
52 static int s_tty_fd;
53 static const char* s_tty_prefix;
55 /* Condition variable and lock used to wait for exit confirmation from
56 * JavaScript. */
57 static pthread_cond_t s_exit_cond;
58 static pthread_mutex_t s_exit_lock;
60 /* A message to Post to JavaScript instead of exiting, or NULL if exit() should
61 * be called instead. */
62 static char* s_exit_message;
64 static int ProcessProperties(void);
65 ssize_t TtyOutputHandler(const char* buf, size_t count, void* user_data);
66 static void MessageHandlerExit(struct PP_Var key,
67 struct PP_Var value,
68 void* user_data);
69 static void MessageHandlerInput(struct PP_Var key,
70 struct PP_Var value,
71 void* user_data);
72 static void MessageHandlerResize(struct PP_Var key,
73 struct PP_Var value,
74 void* user_data);
75 static void HandleResize(int width, int height);
76 static void* MainThread(void* info);
77 static void ExitHandshake(int status, void* user_data);
79 static void PostMessageString(const char* message) {
80 struct PP_Var message_var =
81 PSInterfaceVar()->VarFromUtf8(message, strlen(message));
82 PSInterfaceMessaging()->PostMessage(g_ps_instance, message_var);
83 PSInterfaceVar()->Release(message_var);
86 static PP_Bool Instance_DidCreate(PP_Instance instance,
87 uint32_t argc,
88 const char* argn[],
89 const char* argv[]) {
90 g_ps_instance = instance;
91 g_ps_main_cb = PSUserMainGet();
92 s_verbosity = PSV_LOG;
93 PSInterfaceInputEvent()->RequestInputEvents(
94 g_ps_instance, PP_INPUTEVENT_CLASS_MOUSE | PP_INPUTEVENT_CLASS_KEYBOARD |
95 PP_INPUTEVENT_CLASS_WHEEL | PP_INPUTEVENT_CLASS_TOUCH);
97 uint32_t i;
98 struct StartInfo* si = malloc(sizeof(struct StartInfo));
100 si->argc_ = 0;
101 si->argv_ = calloc(argc + 1, sizeof(char*));
102 si->argv_[0] = NULL;
104 /* Process embed attributes into the environment.
105 * Convert the attribute names to uppercase as environment variables are case
106 * sensitive but are almost universally uppercase in practice. */
107 for (i = 0; i < argc; i++) {
108 char* key = strdup(argn[i]);
109 char* c = key;
110 while (*c) {
111 *c = toupper((int)*c);
112 ++c;
114 setenv(key, argv[i], 1);
115 free(key);
118 /* Set a default value for SRC. */
119 setenv("SRC", "NMF?", 0);
120 /* Use the src tag name if ARG0 is not explicitly specified. */
121 setenv("ARG0", getenv("SRC"), 0);
123 /* Walk ARG0..ARGn populating argv until an argument is missing. */
124 for (;;) {
125 char arg_name[32];
126 snprintf(arg_name, 32, "ARG%d", si->argc_);
127 const char* next_arg = getenv(arg_name);
128 if (NULL == next_arg)
129 break;
131 si->argv_[si->argc_++] = strdup(next_arg);
134 int props_processed = ProcessProperties();
136 /* Log arg values only once ProcessProperties has been called so that the
137 * PS_VERBOSITY attribute will be in effect. */
138 for (i = 0; i < argc; i++) {
139 if (argv[i]) {
140 PSInstanceTrace("attribs[%d] '%s=%s'\n", i, argn[i], argv[i]);
141 } else {
142 PSInstanceTrace("attribs[%d] '%s'\n", i, argn[i]);
146 for (i = 0; i < si->argc_; i++) {
147 PSInstanceTrace("argv[%d] '%s'\n", i, si->argv_[i]);
150 if (!props_processed) {
151 PSInstanceWarn("Skipping create thread.\n");
152 return PP_FALSE;
155 pthread_t main_thread;
156 int ret = pthread_create(&main_thread, NULL, MainThread, si);
157 PSInstanceTrace("Created thread: %d.\n", ret);
158 return ret == 0 ? PP_TRUE : PP_FALSE;
161 int ProcessProperties(void) {
162 /* Reset verbosity if passed in */
163 const char* verbosity = getenv("PS_VERBOSITY");
164 if (verbosity)
165 PSInstanceSetVerbosity(atoi(verbosity));
167 /* Enable NaCl IO to map STDIN, STDOUT, and STDERR */
168 nacl_io_init_ppapi(PSGetInstanceId(), PSGetInterface);
170 s_tty_prefix = getenv("PS_TTY_PREFIX");
171 if (s_tty_prefix) {
172 s_tty_fd = open("/dev/tty", O_WRONLY);
173 if (s_tty_fd >= 0) {
174 PSEventRegisterMessageHandler(s_tty_prefix, MessageHandlerInput, NULL);
175 const char* tty_resize = getenv("PS_TTY_RESIZE");
176 if (tty_resize)
177 PSEventRegisterMessageHandler(tty_resize, MessageHandlerResize, NULL);
179 char* tty_rows = getenv("PS_TTY_ROWS");
180 char* tty_cols = getenv("PS_TTY_COLS");
181 if (tty_rows && tty_cols) {
182 char* end = tty_rows;
183 int rows = strtol(tty_rows, &end, 10);
184 if (*end != '\0' || rows < 0) {
185 PSInstanceError("Invalid value for PS_TTY_ROWS: %s\n", tty_rows);
186 } else {
187 end = tty_cols;
188 int cols = strtol(tty_cols, &end, 10);
189 if (*end != '\0' || cols < 0)
190 PSInstanceError("Invalid value for PS_TTY_COLS: %s\n", tty_cols);
191 else
192 HandleResize(cols, rows);
194 } else if (tty_rows || tty_cols) {
195 PSInstanceError("PS_TTY_ROWS and PS_TTY_COLS must be set together\n");
198 struct tioc_nacl_output handler;
199 handler.handler = TtyOutputHandler;
200 handler.user_data = NULL;
201 ioctl(s_tty_fd, TIOCNACLOUTPUT, &handler);
202 } else {
203 PSInstanceError("Failed to open /dev/tty.\n");
207 /* Set default values */
208 setenv("PS_STDIN", "/dev/stdin", 0);
209 setenv("PS_STDOUT", "/dev/stdout", 0);
210 setenv("PS_STDERR", "/dev/console3", 0);
212 int fd0 = open(getenv("PS_STDIN"), O_RDONLY);
213 dup2(fd0, 0);
215 int fd1 = open(getenv("PS_STDOUT"), O_WRONLY);
216 dup2(fd1, 1);
218 int fd2 = open(getenv("PS_STDERR"), O_WRONLY);
219 dup2(fd2, 2);
221 PSEventRegisterMessageHandler("jspipe1", MessageHandlerInput, NULL);
222 PSEventRegisterMessageHandler("jspipe2", MessageHandlerInput, NULL);
223 PSEventRegisterMessageHandler("jspipe3", MessageHandlerInput, NULL);
225 s_exit_message = getenv("PS_EXIT_MESSAGE");
227 /* If PS_EXIT_MESSAGE is set in the environment then we perform a handshake
228 * with JavaScript when program exits. */
229 if (s_exit_message != NULL)
230 nacl_io_set_exit_callback(ExitHandshake, NULL);
232 /* Set line buffering on stdout and stderr */
233 #if !defined(WIN32)
234 setvbuf(stderr, NULL, _IOLBF, 0);
235 setvbuf(stdout, NULL, _IOLBF, 0);
236 #endif
237 return 1;
240 ssize_t TtyOutputHandler(const char* data, size_t count, void* user_data) {
241 /* We prepend the s_tty_prefix to the data in buf, then package it up and
242 * post it as a message to javascript. */
243 size_t tty_prefix_len = strlen(s_tty_prefix);
244 char* message = alloca(tty_prefix_len + count + 1);
245 memcpy(message, s_tty_prefix, tty_prefix_len);
246 memcpy(message + tty_prefix_len, data, count);
247 message[tty_prefix_len + count] = 0;
248 PostMessageString(message);
249 return count;
252 void MessageHandlerExit(struct PP_Var key,
253 struct PP_Var value,
254 void* user_data) {
255 pthread_mutex_lock(&s_exit_lock);
256 pthread_cond_signal(&s_exit_cond);
257 pthread_mutex_unlock(&s_exit_lock);
260 void MessageHandlerInput(struct PP_Var key,
261 struct PP_Var value,
262 void* user_data) {
263 uint32_t key_len;
264 const char* key_str = PSInterfaceVar()->VarToUtf8(key, &key_len);
266 const char* filename = NULL;
267 if (strncmp(key_str, s_tty_prefix, key_len) == 0) {
268 filename = "/dev/tty";
269 } else if (strncmp(key_str, "jspipe1", key_len) == 0) {
270 filename = "/dev/jspipe1";
271 } else if (strncmp(key_str, "jspipe2", key_len) == 0) {
272 filename = "/dev/jspipe2";
273 } else if (strncmp(key_str, "jspipe3", key_len) == 0) {
274 filename = "/dev/jspipe3";
275 } else {
276 PSInstanceError("unexpected input key: %s", key_str);
277 return;
280 int fd = open(filename, O_RDONLY);
281 if (fd < 0) {
282 PSInstanceError("error opening file: %s (%s)", filename, strerror(errno));
283 return;
286 int ret = ioctl(fd, NACL_IOC_HANDLEMESSAGE, &value);
287 if (ret != 0) {
288 PSInstanceError("ioctl on %s failed: %d.\n", filename, ret);
289 close(fd);
290 return;
293 close(fd);
296 void MessageHandlerResize(struct PP_Var key,
297 struct PP_Var value,
298 void* user_data) {
299 assert(value.type == PP_VARTYPE_ARRAY);
300 assert(PSInterfaceVarArray()->GetLength(value) == 2);
302 struct PP_Var width_var = PSInterfaceVarArray()->Get(value, 0);
303 struct PP_Var height_var = PSInterfaceVarArray()->Get(value, 1);
305 assert(width_var.type == PP_VARTYPE_INT32);
306 assert(height_var.type == PP_VARTYPE_INT32);
308 int width = width_var.value.as_int;
309 int height = height_var.value.as_int;
311 HandleResize(width, height);
314 void HandleResize(int width, int height) {
315 struct winsize size;
316 memset(&size, 0, sizeof(size));
317 size.ws_col = width;
318 size.ws_row = height;
319 ioctl(s_tty_fd, TIOCSWINSZ, &size);
322 void* MainThread(void* info) {
323 int ret;
324 uint32_t i;
325 PSInstanceTrace("Running MainThread.\n");
326 struct StartInfo* si = (struct StartInfo*)info;
328 PP_Resource message_loop = PSInterfaceMessageLoop()->Create(g_ps_instance);
329 if (PSInterfaceMessageLoop()->AttachToCurrentThread(message_loop) != PP_OK) {
330 PSInstanceError("Unable to attach message loop to thread.\n");
331 return NULL;
334 if (!g_ps_main_cb) {
335 PSInstanceError("No main defined.\n");
336 return 0;
339 PSInstanceTrace("Starting MAIN.\n");
340 ret = g_ps_main_cb(si->argc_, si->argv_);
341 PSInstanceLog("Main thread returned with %d.\n", ret);
343 /* Clean up StartInfo. */
344 for (i = 0; i < si->argc_; i++) {
345 free(si->argv_[i]);
347 free(si->argv_);
348 free(si);
350 /* Exit the entire process once the 'main' thread returns. The error code
351 * will be available to javascript via the exitcode parameter of the crash
352 * event. */
353 #ifdef __native_client__
354 exit(ret);
355 #else
356 ExitHandshake(ret, NULL);
357 #endif
358 return NULL;
361 void ExitHandshake(int status, void* user_data) {
362 if (s_exit_message == NULL)
363 return;
365 PSEventRegisterMessageHandler(s_exit_message, MessageHandlerExit, NULL);
367 /* exit message + ':' + num + \0 */
368 size_t message_len = strlen(s_exit_message) + 1 + 11 + 1;
369 char* message = alloca(message_len);
370 snprintf(message, message_len, "%s:%d", s_exit_message, status);
372 pthread_mutex_lock(&s_exit_lock);
373 PostMessageString(message);
374 pthread_cond_wait(&s_exit_cond, &s_exit_lock);
375 pthread_mutex_unlock(&s_exit_lock);
378 static void Instance_DidDestroy(PP_Instance instance) {
381 static void Instance_DidChangeView(PP_Instance instance, PP_Resource view) {
382 struct PP_Rect rect;
383 if (PSInterfaceView()->GetRect(view, &rect)) {
384 PSInstanceLog("Got View change: %d,%d\n", rect.size.width,
385 rect.size.height);
386 PSEventPostResource(PSE_INSTANCE_DIDCHANGEVIEW, view);
390 static void Instance_DidChangeFocus(PP_Instance instance, PP_Bool has_focus) {
391 PSInstanceLog("Got Focus change: %s\n", has_focus ? "FOCUS ON" : "FOCUS OFF");
392 PSEventPostBool(PSE_INSTANCE_DIDCHANGEFOCUS, has_focus ? PP_TRUE : PP_FALSE);
395 static PP_Bool Instance_HandleDocumentLoad(PP_Instance instance,
396 PP_Resource url_loader) {
397 return PP_FALSE;
400 static void Messaging_HandleMessage(PP_Instance instance,
401 struct PP_Var message) {
402 PSInstanceTrace("Got Message\n");
403 PSEventPostVar(PSE_INSTANCE_HANDLEMESSAGE, message);
406 static PP_Bool InputEvent_HandleInputEvent(PP_Instance instance,
407 PP_Resource input_event) {
408 PSEventPostResource(PSE_INSTANCE_HANDLEINPUT, input_event);
409 return PP_TRUE;
412 static void MouseLock_MouseLockLost(PP_Instance instance) {
413 PSInstanceLog("MouseLockLost\n");
414 PSEventPost(PSE_MOUSELOCK_MOUSELOCKLOST);
417 static void Graphics3D_Graphics3DContextLost(PP_Instance instance) {
418 PSInstanceLog("Graphics3DContextLost\n");
419 PSEventPost(PSE_GRAPHICS3D_GRAPHICS3DCONTEXTLOST);
422 void PSInstanceSetVerbosity(enum PSVerbosity verbosity) {
423 s_verbosity = verbosity;
426 static void VALog(enum PSVerbosity verbosity, const char* fmt, va_list args) {
427 if (verbosity <= s_verbosity) {
428 fprintf(stderr, "ps: ");
429 vfprintf(stderr, fmt, args);
433 void PSInstanceTrace(const char* fmt, ...) {
434 va_list ap;
435 va_start(ap, fmt);
436 VALog(PSV_TRACE, fmt, ap);
437 va_end(ap);
440 void PSInstanceLog(const char* fmt, ...) {
441 va_list ap;
442 va_start(ap, fmt);
443 VALog(PSV_LOG, fmt, ap);
444 va_end(ap);
447 void PSInstanceWarn(const char* fmt, ...) {
448 va_list ap;
449 va_start(ap, fmt);
450 VALog(PSV_WARN, fmt, ap);
451 va_end(ap);
454 void PSInstanceError(const char* fmt, ...) {
455 va_list ap;
456 va_start(ap, fmt);
457 VALog(PSV_ERROR, fmt, ap);
458 va_end(ap);
461 const void* PSGetInterfaceImplementation(const char* interface_name) {
462 if (strcmp(interface_name, PPP_INSTANCE_INTERFACE_1_1) == 0) {
463 static struct PPP_Instance_1_1 interface = {
464 &Instance_DidCreate,
465 &Instance_DidDestroy,
466 &Instance_DidChangeView,
467 &Instance_DidChangeFocus,
468 &Instance_HandleDocumentLoad,
470 return &interface;
471 } else if (strcmp(interface_name, PPP_MESSAGING_INTERFACE_1_0) == 0) {
472 static struct PPP_Messaging_1_0 interface = {
473 &Messaging_HandleMessage,
475 return &interface;
476 } else if (strcmp(interface_name, PPP_INPUT_EVENT_INTERFACE_0_1) == 0) {
477 static struct PPP_InputEvent_0_1 interface = {
478 &InputEvent_HandleInputEvent,
480 return &interface;
481 } else if (strcmp(interface_name, PPP_MOUSELOCK_INTERFACE_1_0) == 0) {
482 static struct PPP_MouseLock_1_0 interface = {
483 &MouseLock_MouseLockLost,
485 return &interface;
486 } else if (strcmp(interface_name, PPP_GRAPHICS_3D_INTERFACE_1_0) == 0) {
487 static struct PPP_Graphics3D_1_0 interface = {
488 &Graphics3D_Graphics3DContextLost,
490 return &interface;
493 return NULL;