Run the validator on dynamically loaded code
[nativeclient.git] / common / standalone.cc
blob44733c35f3ac514d14d4e581e04572463a403700
1 /*
2 * Copyright 2008, Google Inc.
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions are
7 * met:
8 *
9 * * Redistributions of source code must retain the above copyright
10 * notice, this list of conditions and the following disclaimer.
11 * * Redistributions in binary form must reproduce the above
12 * copyright notice, this list of conditions and the following disclaimer
13 * in the documentation and/or other materials provided with the
14 * distribution.
15 * * Neither the name of Google Inc. nor the names of its
16 * contributors may be used to endorse or promote products derived from
17 * this software without specific prior written permission.
19 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
20 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
21 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
22 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
23 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
24 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
25 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
26 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
27 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
28 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
29 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
32 // Standalone helper functions
33 // Mimic barebones nacl multimedia interface for standalone
34 // non-nacl applications. Mainly used as a debugging aid.
35 // * Currently standalone is only supported under Linux
37 #if defined(STANDALONE)
39 #include <errno.h>
40 #include <memory.h>
41 #include <pthread.h>
42 #include <stdint.h>
43 #include <stdio.h>
44 #include <stdlib.h>
45 #include "./standalone.h"
47 const int kMinScreenSize = 32;
48 const int kMaxScreenSize = 4096;
50 struct InfoVideo {
51 int32_t width;
52 int32_t height;
53 int32_t format;
54 int32_t bits_per_pixel;
55 int32_t bytes_per_pixel;
56 int32_t rmask;
57 int32_t gmask;
58 int32_t bmask;
59 SDL_Surface *screen;
63 struct InfoAudio {
64 SDL_AudioSpec *audio_spec;
65 pthread_mutex_t mutex;
66 pthread_cond_t condvar;
67 int32_t ready;
68 size_t size;
69 unsigned char *stream;
70 int32_t first;
71 int32_t shutdown;
72 SDL_AudioSpec obtained;
73 pthread_t thread_self;
77 struct InfoMultimedia {
78 // do not move begin (initialization dep)
79 volatile int32_t sdl_init_flags;
80 volatile int32_t initialized;
81 volatile int32_t have_video;
82 volatile int32_t have_audio;
83 // do not move end
84 InfoVideo video;
85 InfoAudio audio;
88 static InfoMultimedia nacl_multimedia;
89 static pthread_mutex_t nacl_multimedia_mutex;
91 static void Fatal(const char *msg) {
92 printf("Fatal: %s\n", msg);
93 exit(-1);
96 static int Error(const char *msg) {
97 printf("Error: %s\n", msg);
98 return -1;
102 struct MultimediaModuleInit {
103 MultimediaModuleInit();
104 ~MultimediaModuleInit();
107 MultimediaModuleInit::MultimediaModuleInit() {
108 memset(&nacl_multimedia, 0, sizeof(nacl_multimedia));
109 int r = pthread_mutex_init(&nacl_multimedia_mutex, NULL);
110 if (0 != r)
111 Fatal("MultimediaModuleInit: mutex init failed\n");
114 MultimediaModuleInit::~MultimediaModuleInit() {
115 int r = pthread_mutex_destroy(&nacl_multimedia_mutex);
116 if (0 != r)
117 Fatal("~MultimediaModuleInit: mutex destroy failed\n");
118 memset(&nacl_multimedia, 0, sizeof(nacl_multimedia));
121 static MultimediaModuleInit multimedia_module;
124 int nacl_multimedia_init(int subsys) {
125 int32_t r;
126 int32_t retval = -1;
128 r = pthread_mutex_lock(&nacl_multimedia_mutex);
129 if (0 != r)
130 Fatal("nacl_multimedia_init: mutex lock failed");
132 // don't allow nested init
133 if (0 != nacl_multimedia.initialized)
134 Fatal("nacl_multimedia_init: Already initialized!");
136 if (0 != nacl_multimedia.have_video)
137 Fatal("nacl_multimedia_init: video initialized?");
139 if (0 != nacl_multimedia.have_audio)
140 Fatal("nacl_multimedia_init: audio initialized?");
142 // map nacl a/v to sdl subsystem init
143 if (NACL_SUBSYSTEM_VIDEO == (subsys & NACL_SUBSYSTEM_VIDEO)) {
144 nacl_multimedia.sdl_init_flags |= SDL_INIT_VIDEO;
146 if (NACL_SUBSYSTEM_AUDIO == (subsys & NACL_SUBSYSTEM_AUDIO)) {
147 nacl_multimedia.sdl_init_flags |= SDL_INIT_AUDIO;
149 if (SDL_Init(nacl_multimedia.sdl_init_flags)) {
150 Error("nacl_multimeida_init: SDL_Init failed");
151 errno = EIO;
152 goto done;
155 nacl_multimedia.initialized = 1;
156 retval = 0;
158 done:
159 r = pthread_mutex_unlock(&nacl_multimedia_mutex);
160 if (0 != r)
161 Fatal("multimedia_init: mutex unlock failed");
163 return retval;
167 int nacl_multimedia_shutdown() {
168 int32_t r;
170 r = pthread_mutex_lock(&nacl_multimedia_mutex);
171 if (0 != r)
172 Fatal("nacl_multimedia_shutdown: mutex lock failed");
173 if (0 == nacl_multimedia.initialized)
174 Fatal("nacl_multimedia_shutdown: not initialized!");
175 if (0 != nacl_multimedia.have_video)
176 Fatal("nacl_multimedia_shutdown: video subsystem not shutdown!");
177 if (0 != nacl_multimedia.have_audio)
178 Fatal("nacl_multimedia_shutdown: audio subsystem not shutdown!");
180 SDL_Quit();
181 nacl_multimedia.sdl_init_flags = 0;
182 nacl_multimedia.initialized = 0;
184 r = pthread_mutex_unlock(&nacl_multimedia_mutex);
185 if (0 != r)
186 Fatal("nacl_multimedia_shutdown: mutex unlock failed");
187 return 0;
191 int nacl_video_init(enum NaClVideoFormat format,
192 int width,
193 int height) {
194 int32_t r;
195 int32_t retval = -1;
196 uint32_t sdl_video_flags = SDL_DOUBLEBUF | SDL_HWSURFACE;
198 r = pthread_mutex_lock(&nacl_multimedia_mutex);
199 if (0 != r)
200 Fatal("nacl_video_init: mutex lock failed");
202 if (0 == nacl_multimedia.initialized)
203 Fatal("nacl_video_init: multimedia not initialized");
205 // for now don't allow app to have more than one SDL window or resize
206 if (0 != nacl_multimedia.have_video)
207 Fatal("nacl_video_init: already initialized!");
209 if (SDL_INIT_VIDEO != (nacl_multimedia.sdl_init_flags & SDL_INIT_VIDEO))
210 Fatal("nacl_video_init: video not originally requested");
212 // make sure width & height are not insane
213 if ((width < kMinScreenSize) || (width > kMaxScreenSize) ||
214 (height < kMinScreenSize) || (height > kMaxScreenSize)) {
215 Error("nacl_video_init: invalid screen size!");
216 errno = EINVAL;
217 goto done;
220 // width and height must also be multiples of 4
221 if ((0 != (width & 0x3)) || (0 != (height & 0x3))) {
222 Error("nacl_video_init: width & height must be a multiple of 4!");
223 errno = EINVAL;
224 goto done;
227 // make sure video format is valid
228 if ((format != NACL_VIDEO_FORMAT_RGB) &&
229 (format != NACL_VIDEO_FORMAT_RGBA) &&
230 (format != NACL_VIDEO_FORMAT_BGRA)) {
231 Error("nacl_video_init: invalid format parameter\n");
232 errno = EINVAL;
233 goto done;
236 nacl_multimedia.have_video = 1;
237 nacl_multimedia.video.screen = SDL_SetVideoMode(width, height,
238 0, sdl_video_flags);
239 if (!nacl_multimedia.video.screen) {
240 Error("nacl_video_init: SDL_SetVideoMode failed");
241 nacl_multimedia.have_video = 0;
242 errno = EIO;
243 goto done;
246 // width, height and format validated in top half
247 nacl_multimedia.video.format = format;
248 nacl_multimedia.video.width = width;
249 nacl_multimedia.video.height = height;
250 if (NACL_VIDEO_FORMAT_RGB == format) {
251 nacl_multimedia.video.rmask = 0x000000FF;
252 nacl_multimedia.video.gmask = 0x0000FF00;
253 nacl_multimedia.video.bmask = 0x00FF0000;
254 nacl_multimedia.video.bits_per_pixel = 24;
255 nacl_multimedia.video.bytes_per_pixel = 3;
256 } else if (NACL_VIDEO_FORMAT_RGBA == format) {
257 nacl_multimedia.video.rmask = 0x000000FF;
258 nacl_multimedia.video.gmask = 0x0000FF00;
259 nacl_multimedia.video.bmask = 0x00FF0000;
260 nacl_multimedia.video.bits_per_pixel = 32;
261 nacl_multimedia.video.bytes_per_pixel = 4;
262 } else if (NACL_VIDEO_FORMAT_BGRA == format) {
263 nacl_multimedia.video.rmask = 0x00FF0000;
264 nacl_multimedia.video.gmask = 0x0000FF00;
265 nacl_multimedia.video.bmask = 0x000000FF;
266 nacl_multimedia.video.bits_per_pixel = 32;
267 nacl_multimedia.video.bytes_per_pixel = 4;
268 } else {
269 Fatal("nacl_video_init: Panic, unrecognized format!");
272 /* set the window caption */
273 /* todo: as parameter to nacl_video_init? */
274 SDL_WM_SetCaption("Standalone Application", "Standalone Application");
276 retval = 0;
277 done:
278 r = pthread_mutex_unlock(&nacl_multimedia_mutex);
279 if (0 != r)
280 Fatal("nacl_video_init: mutex unlock failed");
281 return retval;
285 int nacl_video_shutdown() {
286 int32_t r;
287 int32_t retval = -1;
289 r = pthread_mutex_lock(&nacl_multimedia_mutex);
290 if (0 != r)
291 Fatal("nacl_video_shutdown: mutex lock failed");
292 if (0 == nacl_multimedia.initialized)
293 Fatal("nacl_video_shutdown: multimedia not initialized!");
294 if (0 == nacl_multimedia.have_video) {
295 Error("nacl_video_shutdown: video already shutdown!");
296 errno = EPERM;
297 goto done;
299 nacl_multimedia.have_video = 0;
300 retval = 0;
301 done:
302 r = pthread_mutex_unlock(&nacl_multimedia_mutex);
303 if (0 != r)
304 Fatal("nacl_video_shutdown: mutex unlock failed");
305 return retval;
309 int nacl_video_update(const void *data) {
310 int32_t r;
311 int32_t retval = -1;
312 SDL_Surface *image;
314 if (0 == nacl_multimedia.have_video)
315 Fatal("nacl_video_update: video not initialized");
317 image = SDL_CreateRGBSurfaceFrom((unsigned char*) data,
318 nacl_multimedia.video.width,
319 nacl_multimedia.video.height,
320 nacl_multimedia.video.bits_per_pixel,
321 nacl_multimedia.video.width *
322 nacl_multimedia.video.bytes_per_pixel,
323 nacl_multimedia.video.rmask,
324 nacl_multimedia.video.gmask,
325 nacl_multimedia.video.bmask, 0);
326 if (NULL == image) {
327 Error("nacl_video_update: SDL_CreateRGBSurfaceFrom failed");
328 errno = EPERM;
329 goto done;
332 r = SDL_SetAlpha(image, 0, 255);
333 if (0 != r) {
334 Error("nacl_video_update SDL_SetAlpha failed");
335 errno = EPERM;
336 goto done_free_image;
339 r = SDL_BlitSurface(image, NULL, nacl_multimedia.video.screen, NULL);
340 if (0 != r) {
341 Error("nacl_video_update: SDL_BlitSurface failed");
342 errno = EPERM;
343 goto done_free_image;
346 r = SDL_Flip(nacl_multimedia.video.screen);
347 if (0 != r) {
348 Error("nacl_video_update: SDL_Flip failed");
349 errno = EPERM;
350 goto done_free_image;
353 retval = 0;
354 // fall through to free image and closure
356 done_free_image:
357 SDL_FreeSurface(image);
358 done:
359 return retval;
363 int nacl_video_poll_event(union NaClMultimediaEvent *event) {
364 int retval;
365 int repoll;
366 int sdl_r;
367 SDL_Event sdl_event;
369 if (0 == nacl_multimedia.initialized)
370 Fatal("nacl_video_poll_event: multmedia not initialized!\n");
371 if (0 == nacl_multimedia.have_video)
372 Fatal("nacl_video_poll_event: video not initialized");
374 do {
375 sdl_r = SDL_PollEvent(&sdl_event);
376 repoll = 0;
377 if (sdl_r == 0) {
378 retval = -1;
379 errno = ENODATA;
380 } else {
381 retval = 0;
382 switch (sdl_event.type) {
383 case SDL_ACTIVEEVENT:
384 event->type = NACL_EVENT_ACTIVE;
385 event->active.gain = sdl_event.active.gain;
386 event->active.state = sdl_event.active.state;
387 break;
388 case SDL_KEYDOWN:
389 case SDL_KEYUP:
390 event->type = (SDL_KEYUP == sdl_event.type) ?
391 NACL_EVENT_KEY_UP : NACL_EVENT_KEY_DOWN;
392 event->key.which = sdl_event.key.which;
393 event->key.state = sdl_event.key.state;
394 event->key.keysym.scancode = sdl_event.key.keysym.scancode;
395 event->key.keysym.sym = sdl_event.key.keysym.sym;
396 event->key.keysym.mod = sdl_event.key.keysym.mod;
397 event->key.keysym.unicode = sdl_event.key.keysym.unicode;
398 break;
399 case SDL_MOUSEMOTION:
400 event->type = NACL_EVENT_MOUSE_MOTION;
401 event->motion.which = sdl_event.motion.which;
402 event->motion.state = sdl_event.motion.state;
403 event->motion.x = sdl_event.motion.x;
404 event->motion.y = sdl_event.motion.y;
405 event->motion.xrel = sdl_event.motion.xrel;
406 event->motion.yrel = sdl_event.motion.yrel;
407 break;
408 case SDL_MOUSEBUTTONDOWN:
409 case SDL_MOUSEBUTTONUP:
410 event->type = (SDL_MOUSEBUTTONUP == sdl_event.type) ?
411 NACL_EVENT_MOUSE_BUTTON_UP :
412 NACL_EVENT_MOUSE_BUTTON_DOWN;
413 event->button.which = sdl_event.button.which;
414 event->button.button = sdl_event.button.button;
415 event->button.state = sdl_event.button.state;
416 event->button.x = sdl_event.button.x;
417 event->button.y = sdl_event.button.y;
418 break;
419 case SDL_QUIT:
420 event->type = NACL_EVENT_QUIT;
421 break;
422 default:
423 // an sdl event happened, but we don't support it
424 // so move along and repoll for another event
425 repoll = 1;
426 break;
429 } while (0 != repoll);
431 return retval;
435 void __NaCl_InternalAudioCallback(void *unused, Uint8 *stream, int size) {
436 int r;
438 r = pthread_mutex_lock(&nacl_multimedia.audio.mutex);
439 if (0 != r)
440 Fatal("__NaCl_InternalAudioCallback: mutex lock failed");
441 if (0 == nacl_multimedia.audio.ready) {
442 nacl_multimedia.audio.stream = stream;
443 nacl_multimedia.audio.size = size;
444 nacl_multimedia.audio.ready = 1;
445 r = pthread_cond_signal(&nacl_multimedia.audio.condvar);
446 if (0 != r)
447 Fatal("__NaCl_InternalAudioCallback: cond var signal failed");
448 // wait for return signal
449 while ((1 == nacl_multimedia.audio.ready) &&
450 (0 == nacl_multimedia.audio.shutdown)) {
451 r = pthread_cond_wait(&nacl_multimedia.audio.condvar,
452 &nacl_multimedia.audio.mutex);
453 if (0 != r)
454 Fatal("__NaCl_InternalAudioCallback: cond var wait failed");
457 r = pthread_mutex_unlock(&nacl_multimedia.audio.mutex);
458 if (0 != r)
459 Fatal("__NaCl_InternalAudioCallback: mutex unlock failed");
463 int nacl_audio_init(enum NaClAudioFormat format,
464 int desired_samples,
465 int *obtained_samples) {
466 SDL_AudioSpec desired;
467 int32_t retval = -1;
468 int r;
470 r = pthread_mutex_lock(&nacl_multimedia_mutex);
471 if (0 != r)
472 Fatal("nacl_audio_init: mutex lock failed");
474 if (0 == nacl_multimedia.initialized)
475 Fatal("nacl_audio_init: multimedia not initialized!");
477 // for now, don't allow an app to open more than one SDL audio device
478 if (0 != nacl_multimedia.have_audio)
479 Fatal("nacl_audio_init: already initialized!");
481 if ((desired_samples < 128) || (desired_samples > 8192)) {
482 Error("nacl_audio_init: desired sample value out of range");
483 errno = ERANGE;
484 goto done;
487 memset(&nacl_multimedia.audio, 0, sizeof(nacl_multimedia.audio));
488 memset(&desired, 0, sizeof(desired));
490 r = pthread_mutex_init(&nacl_multimedia.audio.mutex, NULL);
491 if (0 != r)
492 Fatal("nacl_audio_init: mutex init failed");
493 r = pthread_cond_init(&nacl_multimedia.audio.condvar, NULL);
494 if (0 != r)
495 Fatal("nacl_audio_init: cond var ctor failed");
497 nacl_multimedia.audio.thread_self = 0;
498 nacl_multimedia.audio.size = 0;
499 nacl_multimedia.audio.ready = 0;
500 nacl_multimedia.audio.first = 1;
501 nacl_multimedia.audio.shutdown = 0;
503 desired.format = AUDIO_S16LSB;
504 desired.channels = 2;
505 desired.samples = desired_samples;
506 desired.callback = __NaCl_InternalAudioCallback;
508 if (NACL_AUDIO_FORMAT_STEREO_44K == format) {
509 desired.freq = 44100;
510 } else if (NACL_AUDIO_FORMAT_STEREO_48K == format) {
511 desired.freq = 48000;
512 } else {
513 // we only support two simple high quality stereo formats
514 Error("nacl_audio_init: unsupported format");
515 errno = EINVAL;
516 goto done;
519 if (SDL_OpenAudio(&desired, &nacl_multimedia.audio.obtained) < 0) {
520 Error("nacl_audio_init: Couldn't open SDL audio");
521 Error(SDL_GetError());
522 errno = EIO;
523 goto done;
526 if (nacl_multimedia.audio.obtained.format != desired.format) {
527 Error("nacl_audio_init: Couldn't get desired format");
528 errno = EIO;
529 goto done_close;
532 *obtained_samples = nacl_multimedia.audio.obtained.samples;
534 nacl_multimedia.have_audio = 1;
535 retval = 0;
536 goto done;
538 done_close:
539 SDL_CloseAudio();
541 done:
542 r = pthread_mutex_unlock(&nacl_multimedia_mutex);
543 if (0 != r)
544 Fatal("nacl_audio_init: mutex unlock failed");
546 return retval;
550 int nacl_audio_shutdown() {
551 int r;
553 r = pthread_mutex_lock(&nacl_multimedia_mutex);
554 if (0 != r)
555 Fatal("nacl_audio_shutdown: mutex lock failed");
556 if (0 == nacl_multimedia.initialized)
557 Fatal("nacl_audio_shutdown: multimedia not initialized");
558 if (0 == nacl_multimedia.have_audio)
559 Fatal("nacl_audio_shutdown: audio not initialized!");
560 // tell audio thread we're shutting down
561 r = pthread_mutex_lock(&nacl_multimedia.audio.mutex);
562 if (0 != r)
563 Fatal("nacl_audio_shutdown: mutex lock failed");
564 SDL_PauseAudio(1);
565 nacl_multimedia.audio.shutdown = 1;
566 r = pthread_cond_broadcast(&nacl_multimedia.audio.condvar);
567 if (0 != r)
568 Fatal("nacl_audio_shutdown: cond var broadcast failed");
569 r = pthread_mutex_unlock(&nacl_multimedia.audio.mutex);
570 if (0 != r)
571 Fatal("nacl_audio_shutdown: mutex unlock failed");
572 // close out audio
573 SDL_CloseAudio();
574 // no more callbacks at this point
575 nacl_multimedia.have_audio = 0;
576 r = pthread_mutex_unlock(&nacl_multimedia_mutex);
577 if (0 != r)
578 Fatal("nacl_audio_shutdown: mutex unlock failed");
579 return 0;
584 // returns 0 during normal operation.
585 // returns -1 indicating that it is time to exit the audio thread
586 int32_t nacl_audio_stream(const void *data,
587 size_t *size) {
588 int32_t r;
589 int32_t retval = -1;
591 r = pthread_mutex_lock(&nacl_multimedia_mutex);
592 if (0 != r)
593 Fatal("nacl_audio_stream: global mutex lock failed");
595 if (0 == nacl_multimedia.have_audio) {
596 goto done;
599 if (nacl_multimedia.audio.first) {
600 // don't copy data on first call...
601 nacl_multimedia.audio.thread_self = pthread_self();
602 } else {
603 if (nacl_multimedia.audio.thread_self != pthread_self()) {
604 Fatal("nacl_audio_stream: called from different thread");
607 // copy the audio data into the sdl audio buffer
608 memcpy(nacl_multimedia.audio.stream, data, nacl_multimedia.audio.size);
611 // callback synchronization
612 r = pthread_mutex_lock(&nacl_multimedia.audio.mutex);
613 if (0 != r)
614 Fatal("nacl_audio_stream: audio mutex lock failed");
615 // unpause audio on first, start callbacks
616 if (nacl_multimedia.audio.first) {
617 SDL_PauseAudio(0);
618 nacl_multimedia.audio.first = 0;
621 // signal callback (if it is waiting)
622 if (nacl_multimedia.audio.ready != 0) {
623 nacl_multimedia.audio.ready = 0;
624 r = pthread_cond_signal(&nacl_multimedia.audio.condvar);
625 if (0 != r)
626 Fatal("nacl_audio_stream: cond var signal failed");
629 nacl_multimedia.audio.size = 0;
631 // wait for next callback
632 while ((0 == nacl_multimedia.audio.ready) &&
633 (0 == nacl_multimedia.audio.shutdown)) {
634 r = pthread_cond_wait(&nacl_multimedia.audio.condvar,
635 &nacl_multimedia.audio.mutex);
636 if (0 != r)
637 Fatal("nacl_audio_stream: cond var wait failed");
640 if (0 != nacl_multimedia.audio.shutdown) {
641 nacl_multimedia.audio.size = 0;
642 *size = 0;
643 goto done;
645 // return size of next audio block
646 *size = (size_t)nacl_multimedia.audio.size;
647 r = pthread_mutex_unlock(&nacl_multimedia.audio.mutex);
648 if (0 != r)
649 Fatal("nacl_audio_stream: audio mutex unlock failed");
650 retval = 0;
652 done:
653 r = pthread_mutex_unlock(&nacl_multimedia_mutex);
654 if (0 != r)
655 Fatal("nacl_audio_stream: global mutex unlock failed");
656 return retval;
660 #endif // STANDALONE