| Index: native_client_sdk/src/libraries/ppapi_simple/ps_instance.c
|
| diff --git a/native_client_sdk/src/libraries/ppapi_simple/ps_instance.c b/native_client_sdk/src/libraries/ppapi_simple/ps_instance.c
|
| new file mode 100644
|
| index 0000000000000000000000000000000000000000..bc81c277d50aa4e63b87ed7b62b8105e26b07778
|
| --- /dev/null
|
| +++ b/native_client_sdk/src/libraries/ppapi_simple/ps_instance.c
|
| @@ -0,0 +1,492 @@
|
| +/* Copyright 2015 The Chromium Authors. All rights reserved.
|
| + * Use of this source code is governed by a BSD-style license that can be
|
| + * found in the LICENSE file. */
|
| +
|
| +#include "ppapi_simple/ps_instance.h"
|
| +
|
| +#include <alloca.h>
|
| +#include <assert.h>
|
| +#include <ctype.h>
|
| +#include <errno.h>
|
| +#include <fcntl.h>
|
| +#include <pthread.h>
|
| +#include <stdio.h>
|
| +#include <stdlib.h>
|
| +#include <string.h>
|
| +#include <sys/ioctl.h>
|
| +#include <unistd.h>
|
| +
|
| +#include <ppapi/c/pp_errors.h>
|
| +#include <ppapi/c/pp_instance.h>
|
| +#include <ppapi/c/pp_module.h>
|
| +#include <ppapi/c/pp_rect.h>
|
| +#include <ppapi/c/pp_size.h>
|
| +#include <ppapi/c/ppb.h>
|
| +#include <ppapi/c/ppp.h>
|
| +#include <ppapi/c/ppp_graphics_3d.h>
|
| +#include <ppapi/c/ppp_input_event.h>
|
| +#include <ppapi/c/ppp_instance.h>
|
| +#include <ppapi/c/ppp_messaging.h>
|
| +#include <ppapi/c/ppp_mouse_lock.h>
|
| +
|
| +#include "nacl_io/ioctl.h"
|
| +#include "nacl_io/nacl_io.h"
|
| +#include "nacl_io/log.h"
|
| +#include "ppapi_simple/ps_interface.h"
|
| +#include "ppapi_simple/ps_main.h"
|
| +
|
| +struct StartInfo {
|
| + uint32_t argc_;
|
| + char** argv_;
|
| +};
|
| +
|
| +PP_Instance g_ps_instance;
|
| +PPB_GetInterface g_ps_get_interface;
|
| +PSMainFunc_t g_ps_main_cb;
|
| +
|
| +static enum PSVerbosity s_verbosity;
|
| +
|
| +/* TTY handling */
|
| +static int s_tty_fd;
|
| +static const char* s_tty_prefix;
|
| +
|
| +/* Condition variable and lock used to wait for exit confirmation from
|
| + * JavaScript. */
|
| +static pthread_cond_t s_exit_cond;
|
| +static pthread_mutex_t s_exit_lock;
|
| +
|
| +/* A message to Post to JavaScript instead of exiting, or NULL if exit() should
|
| + * be called instead. */
|
| +static char* s_exit_message;
|
| +
|
| +static int ProcessProperties(void);
|
| +ssize_t TtyOutputHandler(const char* buf, size_t count, void* user_data);
|
| +static void MessageHandlerExit(struct PP_Var key,
|
| + struct PP_Var value,
|
| + void* user_data);
|
| +static void MessageHandlerInput(struct PP_Var key,
|
| + struct PP_Var value,
|
| + void* user_data);
|
| +static void MessageHandlerResize(struct PP_Var key,
|
| + struct PP_Var value,
|
| + void* user_data);
|
| +static void HandleResize(int width, int height);
|
| +static void* MainThread(void* info);
|
| +static void ExitHandshake(int status, void* user_data);
|
| +
|
| +static void PostMessageString(const char* message) {
|
| + struct PP_Var message_var =
|
| + PSInterfaceVar()->VarFromUtf8(message, strlen(message));
|
| + PSInterfaceMessaging()->PostMessage(g_ps_instance, message_var);
|
| + PSInterfaceVar()->Release(message_var);
|
| +}
|
| +
|
| +static PP_Bool Instance_DidCreate(PP_Instance instance,
|
| + uint32_t argc,
|
| + const char* argn[],
|
| + const char* argv[]) {
|
| + g_ps_instance = instance;
|
| + g_ps_main_cb = PSUserMainGet();
|
| + s_verbosity = PSV_LOG;
|
| + PSInterfaceInputEvent()->RequestInputEvents(
|
| + g_ps_instance, PP_INPUTEVENT_CLASS_MOUSE | PP_INPUTEVENT_CLASS_KEYBOARD |
|
| + PP_INPUTEVENT_CLASS_WHEEL | PP_INPUTEVENT_CLASS_TOUCH);
|
| +
|
| + uint32_t i;
|
| + struct StartInfo* si = malloc(sizeof(struct StartInfo));
|
| +
|
| + si->argc_ = 0;
|
| + si->argv_ = calloc(argc + 1, sizeof(char*));
|
| + si->argv_[0] = NULL;
|
| +
|
| + /* Process embed attributes into the environment.
|
| + * Convert the attribute names to uppercase as environment variables are case
|
| + * sensitive but are almost universally uppercase in practice. */
|
| + for (i = 0; i < argc; i++) {
|
| + char* key = strdup(argn[i]);
|
| + char* c = key;
|
| + while (*c) {
|
| + *c = toupper((int)*c);
|
| + ++c;
|
| + }
|
| + setenv(key, argv[i], 1);
|
| + free(key);
|
| + }
|
| +
|
| + /* Set a default value for SRC. */
|
| + setenv("SRC", "NMF?", 0);
|
| + /* Use the src tag name if ARG0 is not explicitly specified. */
|
| + setenv("ARG0", getenv("SRC"), 0);
|
| +
|
| + /* Walk ARG0..ARGn populating argv until an argument is missing. */
|
| + for (;;) {
|
| + char arg_name[32];
|
| + snprintf(arg_name, 32, "ARG%d", si->argc_);
|
| + const char* next_arg = getenv(arg_name);
|
| + if (NULL == next_arg)
|
| + break;
|
| +
|
| + si->argv_[si->argc_++] = strdup(next_arg);
|
| + }
|
| +
|
| + int props_processed = ProcessProperties();
|
| +
|
| + /* Log arg values only once ProcessProperties has been called so that the
|
| + * PS_VERBOSITY attribute will be in effect. */
|
| + for (i = 0; i < argc; i++) {
|
| + if (argv[i]) {
|
| + PSInstanceTrace("attribs[%d] '%s=%s'\n", i, argn[i], argv[i]);
|
| + } else {
|
| + PSInstanceTrace("attribs[%d] '%s'\n", i, argn[i]);
|
| + }
|
| + }
|
| +
|
| + for (i = 0; i < si->argc_; i++) {
|
| + PSInstanceTrace("argv[%d] '%s'\n", i, si->argv_[i]);
|
| + }
|
| +
|
| + if (!props_processed) {
|
| + PSInstanceWarn("Skipping create thread.\n");
|
| + return PP_FALSE;
|
| + }
|
| +
|
| + pthread_t main_thread;
|
| + int ret = pthread_create(&main_thread, NULL, MainThread, si);
|
| + PSInstanceTrace("Created thread: %d.\n", ret);
|
| + return ret == 0 ? PP_TRUE : PP_FALSE;
|
| +}
|
| +
|
| +int ProcessProperties(void) {
|
| + /* Reset verbosity if passed in */
|
| + const char* verbosity = getenv("PS_VERBOSITY");
|
| + if (verbosity)
|
| + PSInstanceSetVerbosity(atoi(verbosity));
|
| +
|
| + /* Enable NaCl IO to map STDIN, STDOUT, and STDERR */
|
| + nacl_io_init_ppapi(PSGetInstanceId(), PSGetInterface);
|
| +
|
| + s_tty_prefix = getenv("PS_TTY_PREFIX");
|
| + if (s_tty_prefix) {
|
| + s_tty_fd = open("/dev/tty", O_WRONLY);
|
| + if (s_tty_fd >= 0) {
|
| + PSEventRegisterMessageHandler(s_tty_prefix, MessageHandlerInput, NULL);
|
| + const char* tty_resize = getenv("PS_TTY_RESIZE");
|
| + if (tty_resize)
|
| + PSEventRegisterMessageHandler(tty_resize, MessageHandlerResize, NULL);
|
| +
|
| + char* tty_rows = getenv("PS_TTY_ROWS");
|
| + char* tty_cols = getenv("PS_TTY_COLS");
|
| + if (tty_rows && tty_cols) {
|
| + char* end = tty_rows;
|
| + int rows = strtol(tty_rows, &end, 10);
|
| + if (*end != '\0' || rows < 0) {
|
| + PSInstanceError("Invalid value for PS_TTY_ROWS: %s\n", tty_rows);
|
| + } else {
|
| + end = tty_cols;
|
| + int cols = strtol(tty_cols, &end, 10);
|
| + if (*end != '\0' || cols < 0)
|
| + PSInstanceError("Invalid value for PS_TTY_COLS: %s\n", tty_cols);
|
| + else
|
| + HandleResize(cols, rows);
|
| + }
|
| + } else if (tty_rows || tty_cols) {
|
| + PSInstanceError("PS_TTY_ROWS and PS_TTY_COLS must be set together\n");
|
| + }
|
| +
|
| + struct tioc_nacl_output handler;
|
| + handler.handler = TtyOutputHandler;
|
| + handler.user_data = NULL;
|
| + ioctl(s_tty_fd, TIOCNACLOUTPUT, &handler);
|
| + } else {
|
| + PSInstanceError("Failed to open /dev/tty.\n");
|
| + }
|
| + }
|
| +
|
| + /* Set default values */
|
| + setenv("PS_STDIN", "/dev/stdin", 0);
|
| + setenv("PS_STDOUT", "/dev/stdout", 0);
|
| + setenv("PS_STDERR", "/dev/console3", 0);
|
| +
|
| + int fd0 = open(getenv("PS_STDIN"), O_RDONLY);
|
| + dup2(fd0, 0);
|
| +
|
| + int fd1 = open(getenv("PS_STDOUT"), O_WRONLY);
|
| + dup2(fd1, 1);
|
| +
|
| + int fd2 = open(getenv("PS_STDERR"), O_WRONLY);
|
| + dup2(fd2, 2);
|
| +
|
| + PSEventRegisterMessageHandler("jspipe1", MessageHandlerInput, NULL);
|
| + PSEventRegisterMessageHandler("jspipe2", MessageHandlerInput, NULL);
|
| + PSEventRegisterMessageHandler("jspipe3", MessageHandlerInput, NULL);
|
| +
|
| + s_exit_message = getenv("PS_EXIT_MESSAGE");
|
| +
|
| + /* If PS_EXIT_MESSAGE is set in the environment then we perform a handshake
|
| + * with JavaScript when program exits. */
|
| + if (s_exit_message != NULL)
|
| + nacl_io_set_exit_callback(ExitHandshake, NULL);
|
| +
|
| + /* Set line buffering on stdout and stderr */
|
| +#if !defined(WIN32)
|
| + setvbuf(stderr, NULL, _IOLBF, 0);
|
| + setvbuf(stdout, NULL, _IOLBF, 0);
|
| +#endif
|
| + return 1;
|
| +}
|
| +
|
| +ssize_t TtyOutputHandler(const char* data, size_t count, void* user_data) {
|
| + /* We prepend the s_tty_prefix to the data in buf, then package it up and
|
| + * post it as a message to javascript. */
|
| + size_t tty_prefix_len = strlen(s_tty_prefix);
|
| + char* message = alloca(tty_prefix_len + count + 1);
|
| + memcpy(message, s_tty_prefix, tty_prefix_len);
|
| + memcpy(message + tty_prefix_len, data, count);
|
| + message[tty_prefix_len + count] = 0;
|
| + PostMessageString(message);
|
| + return count;
|
| +}
|
| +
|
| +void MessageHandlerExit(struct PP_Var key,
|
| + struct PP_Var value,
|
| + void* user_data) {
|
| + pthread_mutex_lock(&s_exit_lock);
|
| + pthread_cond_signal(&s_exit_cond);
|
| + pthread_mutex_unlock(&s_exit_lock);
|
| +}
|
| +
|
| +void MessageHandlerInput(struct PP_Var key,
|
| + struct PP_Var value,
|
| + void* user_data) {
|
| + uint32_t key_len;
|
| + const char* key_str = PSInterfaceVar()->VarToUtf8(key, &key_len);
|
| +
|
| + const char* filename = NULL;
|
| + if (strncmp(key_str, s_tty_prefix, key_len) == 0) {
|
| + filename = "/dev/tty";
|
| + } else if (strncmp(key_str, "jspipe1", key_len) == 0) {
|
| + filename = "/dev/jspipe1";
|
| + } else if (strncmp(key_str, "jspipe2", key_len) == 0) {
|
| + filename = "/dev/jspipe2";
|
| + } else if (strncmp(key_str, "jspipe3", key_len) == 0) {
|
| + filename = "/dev/jspipe3";
|
| + } else {
|
| + PSInstanceError("unexpected input key: %s", key_str);
|
| + return;
|
| + }
|
| +
|
| + int fd = open(filename, O_RDONLY);
|
| + if (fd < 0) {
|
| + PSInstanceError("error opening file: %s (%s)", filename, strerror(errno));
|
| + return;
|
| + }
|
| +
|
| + int ret = ioctl(fd, NACL_IOC_HANDLEMESSAGE, &value);
|
| + if (ret != 0) {
|
| + PSInstanceError("ioctl on %s failed: %d.\n", filename, ret);
|
| + close(fd);
|
| + return;
|
| + }
|
| +
|
| + close(fd);
|
| +}
|
| +
|
| +void MessageHandlerResize(struct PP_Var key,
|
| + struct PP_Var value,
|
| + void* user_data) {
|
| + assert(value.type == PP_VARTYPE_ARRAY);
|
| + assert(PSInterfaceVarArray()->GetLength(value) == 2);
|
| +
|
| + struct PP_Var width_var = PSInterfaceVarArray()->Get(value, 0);
|
| + struct PP_Var height_var = PSInterfaceVarArray()->Get(value, 1);
|
| +
|
| + assert(width_var.type == PP_VARTYPE_INT32);
|
| + assert(height_var.type == PP_VARTYPE_INT32);
|
| +
|
| + int width = width_var.value.as_int;
|
| + int height = height_var.value.as_int;
|
| +
|
| + HandleResize(width, height);
|
| +}
|
| +
|
| +void HandleResize(int width, int height) {
|
| + struct winsize size;
|
| + memset(&size, 0, sizeof(size));
|
| + size.ws_col = width;
|
| + size.ws_row = height;
|
| + ioctl(s_tty_fd, TIOCSWINSZ, &size);
|
| +}
|
| +
|
| +void* MainThread(void* info) {
|
| + int ret;
|
| + uint32_t i;
|
| + PSInstanceTrace("Running MainThread.\n");
|
| + struct StartInfo* si = (struct StartInfo*)info;
|
| +
|
| + PP_Resource message_loop = PSInterfaceMessageLoop()->Create(g_ps_instance);
|
| + if (PSInterfaceMessageLoop()->AttachToCurrentThread(message_loop) != PP_OK) {
|
| + PSInstanceError("Unable to attach message loop to thread.\n");
|
| + return NULL;
|
| + }
|
| +
|
| + if (!g_ps_main_cb) {
|
| + PSInstanceError("No main defined.\n");
|
| + return 0;
|
| + }
|
| +
|
| + PSInstanceTrace("Starting MAIN.\n");
|
| + ret = g_ps_main_cb(si->argc_, si->argv_);
|
| + PSInstanceLog("Main thread returned with %d.\n", ret);
|
| +
|
| + /* Clean up StartInfo. */
|
| + for (i = 0; i < si->argc_; i++) {
|
| + free(si->argv_[i]);
|
| + }
|
| + free(si->argv_);
|
| + free(si);
|
| +
|
| + /* Exit the entire process once the 'main' thread returns. The error code
|
| + * will be available to javascript via the exitcode parameter of the crash
|
| + * event. */
|
| +#ifdef __native_client__
|
| + exit(ret);
|
| +#else
|
| + ExitHandshake(ret, NULL);
|
| +#endif
|
| + return NULL;
|
| +}
|
| +
|
| +void ExitHandshake(int status, void* user_data) {
|
| + if (s_exit_message == NULL)
|
| + return;
|
| +
|
| + PSEventRegisterMessageHandler(s_exit_message, MessageHandlerExit, NULL);
|
| +
|
| + /* exit message + ':' + num + \0 */
|
| + size_t message_len = strlen(s_exit_message) + 1 + 11 + 1;
|
| + char* message = alloca(message_len);
|
| + snprintf(message, message_len, "%s:%d", s_exit_message, status);
|
| +
|
| + pthread_mutex_lock(&s_exit_lock);
|
| + PostMessageString(message);
|
| + pthread_cond_wait(&s_exit_cond, &s_exit_lock);
|
| + pthread_mutex_unlock(&s_exit_lock);
|
| +}
|
| +
|
| +static void Instance_DidDestroy(PP_Instance instance) {
|
| +}
|
| +
|
| +static void Instance_DidChangeView(PP_Instance instance, PP_Resource view) {
|
| + struct PP_Rect rect;
|
| + if (PSInterfaceView()->GetRect(view, &rect)) {
|
| + PSInstanceLog("Got View change: %d,%d\n", rect.size.width,
|
| + rect.size.height);
|
| + PSEventPostResource(PSE_INSTANCE_DIDCHANGEVIEW, view);
|
| + }
|
| +}
|
| +
|
| +static void Instance_DidChangeFocus(PP_Instance instance, PP_Bool has_focus) {
|
| + PSInstanceLog("Got Focus change: %s\n", has_focus ? "FOCUS ON" : "FOCUS OFF");
|
| + PSEventPostBool(PSE_INSTANCE_DIDCHANGEFOCUS, has_focus ? PP_TRUE : PP_FALSE);
|
| +}
|
| +
|
| +static PP_Bool Instance_HandleDocumentLoad(PP_Instance instance,
|
| + PP_Resource url_loader) {
|
| + return PP_FALSE;
|
| +}
|
| +
|
| +static void Messaging_HandleMessage(PP_Instance instance,
|
| + struct PP_Var message) {
|
| + PSInstanceTrace("Got Message\n");
|
| + PSEventPostVar(PSE_INSTANCE_HANDLEMESSAGE, message);
|
| +}
|
| +
|
| +static PP_Bool InputEvent_HandleInputEvent(PP_Instance instance,
|
| + PP_Resource input_event) {
|
| + PSEventPostResource(PSE_INSTANCE_HANDLEINPUT, input_event);
|
| + return PP_TRUE;
|
| +}
|
| +
|
| +static void MouseLock_MouseLockLost(PP_Instance instance) {
|
| + PSInstanceLog("MouseLockLost\n");
|
| + PSEventPost(PSE_MOUSELOCK_MOUSELOCKLOST);
|
| +}
|
| +
|
| +static void Graphics3D_Graphics3DContextLost(PP_Instance instance) {
|
| + PSInstanceLog("Graphics3DContextLost\n");
|
| + PSEventPost(PSE_GRAPHICS3D_GRAPHICS3DCONTEXTLOST);
|
| +}
|
| +
|
| +void PSInstanceSetVerbosity(enum PSVerbosity verbosity) {
|
| + s_verbosity = verbosity;
|
| +}
|
| +
|
| +static void VALog(enum PSVerbosity verbosity, const char* fmt, va_list args) {
|
| + if (verbosity <= s_verbosity) {
|
| + fprintf(stderr, "ps: ");
|
| + vfprintf(stderr, fmt, args);
|
| + }
|
| +}
|
| +
|
| +void PSInstanceTrace(const char* fmt, ...) {
|
| + va_list ap;
|
| + va_start(ap, fmt);
|
| + VALog(PSV_TRACE, fmt, ap);
|
| + va_end(ap);
|
| +}
|
| +
|
| +void PSInstanceLog(const char* fmt, ...) {
|
| + va_list ap;
|
| + va_start(ap, fmt);
|
| + VALog(PSV_LOG, fmt, ap);
|
| + va_end(ap);
|
| +}
|
| +
|
| +void PSInstanceWarn(const char* fmt, ...) {
|
| + va_list ap;
|
| + va_start(ap, fmt);
|
| + VALog(PSV_WARN, fmt, ap);
|
| + va_end(ap);
|
| +}
|
| +
|
| +void PSInstanceError(const char* fmt, ...) {
|
| + va_list ap;
|
| + va_start(ap, fmt);
|
| + VALog(PSV_ERROR, fmt, ap);
|
| + va_end(ap);
|
| +}
|
| +
|
| +const void* PSGetInterfaceImplementation(const char* interface_name) {
|
| + if (strcmp(interface_name, PPP_INSTANCE_INTERFACE_1_1) == 0) {
|
| + static struct PPP_Instance_1_1 interface = {
|
| + &Instance_DidCreate,
|
| + &Instance_DidDestroy,
|
| + &Instance_DidChangeView,
|
| + &Instance_DidChangeFocus,
|
| + &Instance_HandleDocumentLoad,
|
| + };
|
| + return &interface;
|
| + } else if (strcmp(interface_name, PPP_MESSAGING_INTERFACE_1_0) == 0) {
|
| + static struct PPP_Messaging_1_0 interface = {
|
| + &Messaging_HandleMessage,
|
| + };
|
| + return &interface;
|
| + } else if (strcmp(interface_name, PPP_INPUT_EVENT_INTERFACE_0_1) == 0) {
|
| + static struct PPP_InputEvent_0_1 interface = {
|
| + &InputEvent_HandleInputEvent,
|
| + };
|
| + return &interface;
|
| + } else if (strcmp(interface_name, PPP_MOUSELOCK_INTERFACE_1_0) == 0) {
|
| + static struct PPP_MouseLock_1_0 interface = {
|
| + &MouseLock_MouseLockLost,
|
| + };
|
| + return &interface;
|
| + } else if (strcmp(interface_name, PPP_GRAPHICS_3D_INTERFACE_1_0) == 0) {
|
| + static struct PPP_Graphics3D_1_0 interface = {
|
| + &Graphics3D_Graphics3DContextLost,
|
| + };
|
| + return &interface;
|
| + }
|
| +
|
| + return NULL;
|
| +}
|
|
|