Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(230)

Side by Side Diff: native_client_sdk/src/libraries/ppapi_simple/ps_instance.c

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

Powered by Google App Engine
This is Rietveld 408576698