ozone: evdev: Sync caps lock LED state to evdev
[chromium-blink-merge.git] / native_client_sdk / src / libraries / ppapi_simple / ps_instance.c
blob77628b5ab0bceb0aca27eef7f07f7a5d99d2a3a5
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_main.h"
39 struct StartInfo {
40 uint32_t argc_;
41 char** argv_;
44 PP_Instance g_ps_instance;
45 PPB_GetInterface g_ps_get_interface;
46 PSMainFunc_t g_ps_main_cb;
48 static enum PSVerbosity s_verbosity;
50 /* TTY handling */
51 static int s_tty_fd;
52 static const char* s_tty_prefix;
54 /* Condition variable and lock used to wait for exit confirmation from
55 * JavaScript. */
56 static pthread_cond_t s_exit_cond;
57 static pthread_mutex_t s_exit_lock;
59 /* A message to Post to JavaScript instead of exiting, or NULL if exit() should
60 * be called instead. */
61 static char* s_exit_message;
63 static int ProcessProperties(void);
64 ssize_t TtyOutputHandler(const char* buf, size_t count, void* user_data);
65 static void MessageHandlerExit(struct PP_Var key,
66 struct PP_Var value,
67 void* user_data);
68 static void MessageHandlerInput(struct PP_Var key,
69 struct PP_Var value,
70 void* user_data);
71 static void MessageHandlerResize(struct PP_Var key,
72 struct PP_Var value,
73 void* user_data);
74 static void HandleResize(int width, int height);
75 static void* MainThread(void* info);
76 static void ExitHandshake(int status, void* user_data);
78 static void PostMessageString(const char* message) {
79 struct PP_Var message_var =
80 PSInterfaceVar()->VarFromUtf8(message, strlen(message));
81 PSInterfaceMessaging()->PostMessage(g_ps_instance, message_var);
82 PSInterfaceVar()->Release(message_var);
85 static PP_Bool Instance_DidCreate(PP_Instance instance,
86 uint32_t argc,
87 const char* argn[],
88 const char* argv[]) {
89 g_ps_instance = instance;
90 g_ps_main_cb = PSUserMainGet();
91 s_verbosity = PSV_LOG;
92 PSInterfaceInputEvent()->RequestInputEvents(
93 g_ps_instance, PP_INPUTEVENT_CLASS_MOUSE | PP_INPUTEVENT_CLASS_KEYBOARD |
94 PP_INPUTEVENT_CLASS_WHEEL | PP_INPUTEVENT_CLASS_TOUCH);
96 uint32_t i;
97 struct StartInfo* si = malloc(sizeof(struct StartInfo));
99 si->argc_ = 0;
100 si->argv_ = calloc(argc + 1, sizeof(char*));
101 si->argv_[0] = NULL;
103 /* Process embed attributes into the environment.
104 * Convert the attribute names to uppercase as environment variables are case
105 * sensitive but are almost universally uppercase in practice. */
106 for (i = 0; i < argc; i++) {
107 char* key = strdup(argn[i]);
108 char* c = key;
109 while (*c) {
110 *c = toupper((int)*c);
111 ++c;
113 setenv(key, argv[i], 1);
114 free(key);
117 /* Set a default value for SRC. */
118 setenv("SRC", "NMF?", 0);
119 /* Use the src tag name if ARG0 is not explicitly specified. */
120 setenv("ARG0", getenv("SRC"), 0);
122 /* Walk ARG0..ARGn populating argv until an argument is missing. */
123 for (;;) {
124 char arg_name[32];
125 snprintf(arg_name, 32, "ARG%d", si->argc_);
126 const char* next_arg = getenv(arg_name);
127 if (NULL == next_arg)
128 break;
130 si->argv_[si->argc_++] = strdup(next_arg);
133 int props_processed = ProcessProperties();
135 /* Log arg values only once ProcessProperties has been called so that the
136 * PS_VERBOSITY attribute will be in effect. */
137 for (i = 0; i < argc; i++) {
138 if (argv[i]) {
139 PSInstanceTrace("attribs[%d] '%s=%s'\n", i, argn[i], argv[i]);
140 } else {
141 PSInstanceTrace("attribs[%d] '%s'\n", i, argn[i]);
145 for (i = 0; i < si->argc_; i++) {
146 PSInstanceTrace("argv[%d] '%s'\n", i, si->argv_[i]);
149 if (!props_processed) {
150 PSInstanceWarn("Skipping create thread.\n");
151 return PP_FALSE;
154 pthread_t main_thread;
155 int ret = pthread_create(&main_thread, NULL, MainThread, si);
156 PSInstanceTrace("Created thread: %d.\n", ret);
157 return ret == 0 ? PP_TRUE : PP_FALSE;
160 int ProcessProperties(void) {
161 /* Reset verbosity if passed in */
162 const char* verbosity = getenv("PS_VERBOSITY");
163 if (verbosity)
164 PSInstanceSetVerbosity(atoi(verbosity));
166 /* Enable NaCl IO to map STDIN, STDOUT, and STDERR */
167 nacl_io_init_ppapi(PSGetInstanceId(), PSGetInterface);
169 s_tty_prefix = getenv("PS_TTY_PREFIX");
170 if (s_tty_prefix) {
171 s_tty_fd = open("/dev/tty", O_WRONLY);
172 if (s_tty_fd >= 0) {
173 PSEventRegisterMessageHandler(s_tty_prefix, MessageHandlerInput, NULL);
174 const char* tty_resize = getenv("PS_TTY_RESIZE");
175 if (tty_resize)
176 PSEventRegisterMessageHandler(tty_resize, MessageHandlerResize, NULL);
178 char* tty_rows = getenv("PS_TTY_ROWS");
179 char* tty_cols = getenv("PS_TTY_COLS");
180 if (tty_rows && tty_cols) {
181 char* end = tty_rows;
182 int rows = strtol(tty_rows, &end, 10);
183 if (*end != '\0' || rows < 0) {
184 PSInstanceError("Invalid value for PS_TTY_ROWS: %s\n", tty_rows);
185 } else {
186 end = tty_cols;
187 int cols = strtol(tty_cols, &end, 10);
188 if (*end != '\0' || cols < 0)
189 PSInstanceError("Invalid value for PS_TTY_COLS: %s\n", tty_cols);
190 else
191 HandleResize(cols, rows);
193 } else if (tty_rows || tty_cols) {
194 PSInstanceError("PS_TTY_ROWS and PS_TTY_COLS must be set together\n");
197 struct tioc_nacl_output handler;
198 handler.handler = TtyOutputHandler;
199 handler.user_data = NULL;
200 ioctl(s_tty_fd, TIOCNACLOUTPUT, &handler);
201 } else {
202 PSInstanceError("Failed to open /dev/tty.\n");
206 /* Set default values */
207 setenv("PS_STDIN", "/dev/stdin", 0);
208 setenv("PS_STDOUT", "/dev/stdout", 0);
209 setenv("PS_STDERR", "/dev/console3", 0);
211 int fd0 = open(getenv("PS_STDIN"), O_RDONLY);
212 dup2(fd0, 0);
214 int fd1 = open(getenv("PS_STDOUT"), O_WRONLY);
215 dup2(fd1, 1);
217 int fd2 = open(getenv("PS_STDERR"), O_WRONLY);
218 dup2(fd2, 2);
220 PSEventRegisterMessageHandler("jspipe1", MessageHandlerInput, NULL);
221 PSEventRegisterMessageHandler("jspipe2", MessageHandlerInput, NULL);
222 PSEventRegisterMessageHandler("jspipe3", MessageHandlerInput, NULL);
224 s_exit_message = getenv("PS_EXIT_MESSAGE");
226 /* If PS_EXIT_MESSAGE is set in the environment then we perform a handshake
227 * with JavaScript when program exits. */
228 if (s_exit_message != NULL)
229 nacl_io_set_exit_callback(ExitHandshake, NULL);
231 /* Set line buffering on stdout and stderr */
232 #if !defined(WIN32)
233 setvbuf(stderr, NULL, _IOLBF, 0);
234 setvbuf(stdout, NULL, _IOLBF, 0);
235 #endif
236 return 1;
239 ssize_t TtyOutputHandler(const char* data, size_t count, void* user_data) {
240 /* We prepend the s_tty_prefix to the data in buf, then package it up and
241 * post it as a message to javascript. */
242 size_t tty_prefix_len = strlen(s_tty_prefix);
243 char* message = alloca(tty_prefix_len + count + 1);
244 memcpy(message, s_tty_prefix, tty_prefix_len);
245 memcpy(message + tty_prefix_len, data, count);
246 message[tty_prefix_len + count] = 0;
247 PostMessageString(message);
248 return count;
251 void MessageHandlerExit(struct PP_Var key,
252 struct PP_Var value,
253 void* user_data) {
254 pthread_mutex_lock(&s_exit_lock);
255 pthread_cond_signal(&s_exit_cond);
256 pthread_mutex_unlock(&s_exit_lock);
259 void MessageHandlerInput(struct PP_Var key,
260 struct PP_Var value,
261 void* user_data) {
262 uint32_t key_len;
263 const char* key_str = PSInterfaceVar()->VarToUtf8(key, &key_len);
265 const char* filename = NULL;
266 if (strncmp(key_str, s_tty_prefix, key_len) == 0) {
267 filename = "/dev/tty";
268 } else if (strncmp(key_str, "jspipe1", key_len) == 0) {
269 filename = "/dev/jspipe1";
270 } else if (strncmp(key_str, "jspipe2", key_len) == 0) {
271 filename = "/dev/jspipe2";
272 } else if (strncmp(key_str, "jspipe3", key_len) == 0) {
273 filename = "/dev/jspipe3";
274 } else {
275 PSInstanceError("unexpected input key: %s", key_str);
276 return;
279 int fd = open(filename, O_RDONLY);
280 if (fd < 0) {
281 PSInstanceError("error opening file: %s (%s)", filename, strerror(errno));
282 return;
285 int ret = ioctl(fd, NACL_IOC_HANDLEMESSAGE, &value);
286 if (ret != 0) {
287 PSInstanceError("ioctl on %s failed: %d.\n", filename, ret);
288 close(fd);
289 return;
292 close(fd);
295 void MessageHandlerResize(struct PP_Var key,
296 struct PP_Var value,
297 void* user_data) {
298 assert(value.type == PP_VARTYPE_ARRAY);
299 assert(PSInterfaceVarArray()->GetLength(value) == 2);
301 struct PP_Var width_var = PSInterfaceVarArray()->Get(value, 0);
302 struct PP_Var height_var = PSInterfaceVarArray()->Get(value, 1);
304 assert(width_var.type == PP_VARTYPE_INT32);
305 assert(height_var.type == PP_VARTYPE_INT32);
307 int width = width_var.value.as_int;
308 int height = height_var.value.as_int;
310 HandleResize(width, height);
313 void HandleResize(int width, int height) {
314 struct winsize size;
315 memset(&size, 0, sizeof(size));
316 size.ws_col = width;
317 size.ws_row = height;
318 ioctl(s_tty_fd, TIOCSWINSZ, &size);
321 void* MainThread(void* info) {
322 int ret;
323 uint32_t i;
324 PSInstanceTrace("Running MainThread.\n");
325 struct StartInfo* si = (struct StartInfo*)info;
327 PP_Resource message_loop = PSInterfaceMessageLoop()->Create(g_ps_instance);
328 if (PSInterfaceMessageLoop()->AttachToCurrentThread(message_loop) != PP_OK) {
329 PSInstanceError("Unable to attach message loop to thread.\n");
330 return NULL;
333 if (!g_ps_main_cb) {
334 PSInstanceError("No main defined.\n");
335 return 0;
338 PSInstanceTrace("Starting MAIN.\n");
339 ret = g_ps_main_cb(si->argc_, si->argv_);
340 PSInstanceLog("Main thread returned with %d.\n", ret);
342 /* Clean up StartInfo. */
343 for (i = 0; i < si->argc_; i++) {
344 free(si->argv_[i]);
346 free(si->argv_);
347 free(si);
349 /* Exit the entire process once the 'main' thread returns. The error code
350 * will be available to javascript via the exitcode parameter of the crash
351 * event. */
352 #ifdef __native_client__
353 exit(ret);
354 #else
355 ExitHandshake(ret, NULL);
356 #endif
357 return NULL;
360 void ExitHandshake(int status, void* user_data) {
361 if (s_exit_message == NULL)
362 return;
364 PSEventRegisterMessageHandler(s_exit_message, MessageHandlerExit, NULL);
366 /* exit message + ':' + num + \0 */
367 size_t message_len = strlen(s_exit_message) + 1 + 11 + 1;
368 char* message = alloca(message_len);
369 snprintf(message, message_len, "%s:%d", s_exit_message, status);
371 pthread_mutex_lock(&s_exit_lock);
372 PostMessageString(message);
373 pthread_cond_wait(&s_exit_cond, &s_exit_lock);
374 pthread_mutex_unlock(&s_exit_lock);
377 static void Instance_DidDestroy(PP_Instance instance) {
380 static void Instance_DidChangeView(PP_Instance instance, PP_Resource view) {
381 struct PP_Rect rect;
382 if (PSInterfaceView()->GetRect(view, &rect)) {
383 PSInstanceLog("Got View change: %d,%d\n", rect.size.width,
384 rect.size.height);
385 PSEventPostResource(PSE_INSTANCE_DIDCHANGEVIEW, view);
389 static void Instance_DidChangeFocus(PP_Instance instance, PP_Bool has_focus) {
390 PSInstanceLog("Got Focus change: %s\n", has_focus ? "FOCUS ON" : "FOCUS OFF");
391 PSEventPostBool(PSE_INSTANCE_DIDCHANGEFOCUS, has_focus ? PP_TRUE : PP_FALSE);
394 static PP_Bool Instance_HandleDocumentLoad(PP_Instance instance,
395 PP_Resource url_loader) {
396 return PP_FALSE;
399 static void Messaging_HandleMessage(PP_Instance instance,
400 struct PP_Var message) {
401 PSInstanceTrace("Got Message\n");
402 PSEventPostVar(PSE_INSTANCE_HANDLEMESSAGE, message);
405 static PP_Bool InputEvent_HandleInputEvent(PP_Instance instance,
406 PP_Resource input_event) {
407 PSEventPostResource(PSE_INSTANCE_HANDLEINPUT, input_event);
408 return PP_TRUE;
411 static void MouseLock_MouseLockLost(PP_Instance instance) {
412 PSInstanceLog("MouseLockLost\n");
413 PSEventPost(PSE_MOUSELOCK_MOUSELOCKLOST);
416 static void Graphics3D_Graphics3DContextLost(PP_Instance instance) {
417 PSInstanceLog("Graphics3DContextLost\n");
418 PSEventPost(PSE_GRAPHICS3D_GRAPHICS3DCONTEXTLOST);
421 void PSInstanceSetVerbosity(enum PSVerbosity verbosity) {
422 s_verbosity = verbosity;
425 static void VALog(enum PSVerbosity verbosity, const char* fmt, va_list args) {
426 if (verbosity <= s_verbosity) {
427 fprintf(stderr, "ps: ");
428 vfprintf(stderr, fmt, args);
432 void PSInstanceTrace(const char* fmt, ...) {
433 va_list ap;
434 va_start(ap, fmt);
435 VALog(PSV_TRACE, fmt, ap);
436 va_end(ap);
439 void PSInstanceLog(const char* fmt, ...) {
440 va_list ap;
441 va_start(ap, fmt);
442 VALog(PSV_LOG, fmt, ap);
443 va_end(ap);
446 void PSInstanceWarn(const char* fmt, ...) {
447 va_list ap;
448 va_start(ap, fmt);
449 VALog(PSV_WARN, fmt, ap);
450 va_end(ap);
453 void PSInstanceError(const char* fmt, ...) {
454 va_list ap;
455 va_start(ap, fmt);
456 VALog(PSV_ERROR, fmt, ap);
457 va_end(ap);
460 const void* PSGetInterfaceImplementation(const char* interface_name) {
461 if (strcmp(interface_name, PPP_INSTANCE_INTERFACE_1_1) == 0) {
462 static struct PPP_Instance_1_1 interface = {
463 &Instance_DidCreate,
464 &Instance_DidDestroy,
465 &Instance_DidChangeView,
466 &Instance_DidChangeFocus,
467 &Instance_HandleDocumentLoad,
469 return &interface;
470 } else if (strcmp(interface_name, PPP_MESSAGING_INTERFACE_1_0) == 0) {
471 static struct PPP_Messaging_1_0 interface = {
472 &Messaging_HandleMessage,
474 return &interface;
475 } else if (strcmp(interface_name, PPP_INPUT_EVENT_INTERFACE_0_1) == 0) {
476 static struct PPP_InputEvent_0_1 interface = {
477 &InputEvent_HandleInputEvent,
479 return &interface;
480 } else if (strcmp(interface_name, PPP_MOUSELOCK_INTERFACE_1_0) == 0) {
481 static struct PPP_MouseLock_1_0 interface = {
482 &MouseLock_MouseLockLost,
484 return &interface;
485 } else if (strcmp(interface_name, PPP_GRAPHICS_3D_INTERFACE_1_0) == 0) {
486 static struct PPP_Graphics3D_1_0 interface = {
487 &Graphics3D_Graphics3DContextLost,
489 return &interface;
492 return NULL;