imcplugin demo: Extend to support stat() call
[nativeclient.git] / common / standalone.cc
blob6bea1a9fbf33e762aecf9ee8675e3dcad32c7e6b
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"
48 struct InfoVideo {
49 int32_t width;
50 int32_t height;
51 int32_t format;
52 int32_t bits_per_pixel;
53 int32_t bytes_per_pixel;
54 int32_t rmask;
55 int32_t gmask;
56 int32_t bmask;
57 SDL_Surface *screen;
61 struct InfoAudio {
62 SDL_AudioSpec *audio_spec;
63 pthread_mutex_t mutex;
64 pthread_cond_t condvar;
65 int32_t ready;
66 size_t size;
67 unsigned char *stream;
68 int32_t first;
69 int32_t shutdown;
70 SDL_AudioSpec obtained;
71 pthread_t thread_self;
75 struct InfoMultimedia {
76 // do not move begin (initialization dep)
77 volatile int32_t sdl_init_flags;
78 volatile int32_t initialized;
79 volatile int32_t have_video;
80 volatile int32_t have_audio;
81 // do not move end
82 InfoVideo video;
83 InfoAudio audio;
86 static InfoMultimedia nacl_multimedia;
87 static pthread_mutex_t nacl_multimedia_mutex;
89 static void Fatal(const char *msg) {
90 printf("Fatal: %s\n", msg);
91 exit(-1);
94 static int Error(const char *msg) {
95 printf("Error: %s\n", msg);
96 return -1;
100 struct MultimediaModuleInit {
101 MultimediaModuleInit();
102 ~MultimediaModuleInit();
105 MultimediaModuleInit::MultimediaModuleInit() {
106 memset(&nacl_multimedia, 0, sizeof(nacl_multimedia));
107 int r = pthread_mutex_init(&nacl_multimedia_mutex, NULL);
108 if (0 != r)
109 Fatal("MultimediaModuleInit: mutex init failed\n");
112 MultimediaModuleInit::~MultimediaModuleInit() {
113 int r = pthread_mutex_destroy(&nacl_multimedia_mutex);
114 if (0 != r)
115 Fatal("~MultimediaModuleInit: mutex destroy failed\n");
116 memset(&nacl_multimedia, 0, sizeof(nacl_multimedia));
119 static MultimediaModuleInit multimedia_module;
122 int nacl_multimedia_init(int subsys) {
123 int32_t r;
124 int32_t retval = -1;
126 r = pthread_mutex_lock(&nacl_multimedia_mutex);
127 if (0 != r)
128 Fatal("nacl_multimedia_init: mutex lock failed");
130 // don't allow nested init
131 if (0 != nacl_multimedia.initialized)
132 Fatal("nacl_multimedia_init: Already initialized!");
134 if (0 != nacl_multimedia.have_video)
135 Fatal("nacl_multimedia_init: video initialized?");
137 if (0 != nacl_multimedia.have_audio)
138 Fatal("nacl_multimedia_init: audio initialized?");
140 // map nacl a/v to sdl subsystem init
141 if (NACL_SUBSYSTEM_VIDEO == (subsys & NACL_SUBSYSTEM_VIDEO)) {
142 nacl_multimedia.sdl_init_flags |= SDL_INIT_VIDEO;
144 if (NACL_SUBSYSTEM_AUDIO == (subsys & NACL_SUBSYSTEM_AUDIO)) {
145 nacl_multimedia.sdl_init_flags |= SDL_INIT_AUDIO;
147 if (SDL_Init(nacl_multimedia.sdl_init_flags)) {
148 Error("nacl_multimeida_init: SDL_Init failed");
149 errno = EIO;
150 goto done;
153 nacl_multimedia.initialized = 1;
154 retval = 0;
156 done:
157 r = pthread_mutex_unlock(&nacl_multimedia_mutex);
158 if (0 != r)
159 Fatal("multimedia_init: mutex unlock failed");
161 return retval;
165 int nacl_multimedia_is_embedded(int *embedded) {
166 // standalone is never embedded in browser
167 *embedded = 0;
168 return 0;
172 int nacl_multimedia_get_embed_size(int *width, int *height) {
173 // standalone is never embedded in browser
174 // do not modify width, height
175 errno = EIO;
176 return -1;
180 int nacl_multimedia_shutdown() {
181 int32_t r;
183 r = pthread_mutex_lock(&nacl_multimedia_mutex);
184 if (0 != r)
185 Fatal("nacl_multimedia_shutdown: mutex lock failed");
186 if (0 == nacl_multimedia.initialized)
187 Fatal("nacl_multimedia_shutdown: not initialized!");
188 if (0 != nacl_multimedia.have_video)
189 Fatal("nacl_multimedia_shutdown: video subsystem not shutdown!");
190 if (0 != nacl_multimedia.have_audio)
191 Fatal("nacl_multimedia_shutdown: audio subsystem not shutdown!");
193 SDL_Quit();
194 nacl_multimedia.sdl_init_flags = 0;
195 nacl_multimedia.initialized = 0;
197 r = pthread_mutex_unlock(&nacl_multimedia_mutex);
198 if (0 != r)
199 Fatal("nacl_multimedia_shutdown: mutex unlock failed");
200 return 0;
204 int nacl_video_init(int width,
205 int height) {
206 int32_t r;
207 int32_t retval = -1;
208 uint32_t sdl_video_flags = SDL_DOUBLEBUF | SDL_HWSURFACE;
210 r = pthread_mutex_lock(&nacl_multimedia_mutex);
211 if (0 != r)
212 Fatal("nacl_video_init: mutex lock failed");
214 if (0 == nacl_multimedia.initialized)
215 Fatal("nacl_video_init: multimedia not initialized");
217 // for now don't allow app to have more than one SDL window or resize
218 if (0 != nacl_multimedia.have_video)
219 Fatal("nacl_video_init: already initialized!");
221 if (SDL_INIT_VIDEO != (nacl_multimedia.sdl_init_flags & SDL_INIT_VIDEO))
222 Fatal("nacl_video_init: video not originally requested");
224 // make sure width & height are not insane
225 if ((width < kNaClVideoMinWindowSize) ||
226 (width > kNaClVideoMaxWindowSize) ||
227 (height < kNaClVideoMinWindowSize) ||
228 (height > kNaClVideoMaxWindowSize)) {
229 Error("nacl_video_init: invalid window size!");
230 errno = EINVAL;
231 goto done;
234 // width and height must also be multiples of 4
235 if ((0 != (width & 0x3)) || (0 != (height & 0x3))) {
236 Error("nacl_video_init: width & height must be a multiple of 4!");
237 errno = EINVAL;
238 goto done;
241 nacl_multimedia.have_video = 1;
242 nacl_multimedia.video.screen = SDL_SetVideoMode(width, height,
243 0, sdl_video_flags);
244 if (!nacl_multimedia.video.screen) {
245 Error("nacl_video_init: SDL_SetVideoMode failed");
246 nacl_multimedia.have_video = 0;
247 errno = EIO;
248 goto done;
251 // width, height and format validated in top half
252 nacl_multimedia.video.width = width;
253 nacl_multimedia.video.height = height;
254 nacl_multimedia.video.rmask = 0x00FF0000;
255 nacl_multimedia.video.gmask = 0x0000FF00;
256 nacl_multimedia.video.bmask = 0x000000FF;
257 nacl_multimedia.video.bits_per_pixel = 32;
258 nacl_multimedia.video.bytes_per_pixel = 4;
260 /* set the window caption */
261 /* todo: as parameter to nacl_video_init? */
262 SDL_WM_SetCaption("Standalone Application", "Standalone Application");
264 retval = 0;
265 done:
266 r = pthread_mutex_unlock(&nacl_multimedia_mutex);
267 if (0 != r)
268 Fatal("nacl_video_init: mutex unlock failed");
269 return retval;
273 int nacl_video_shutdown() {
274 int32_t r;
275 int32_t retval = -1;
277 r = pthread_mutex_lock(&nacl_multimedia_mutex);
278 if (0 != r)
279 Fatal("nacl_video_shutdown: mutex lock failed");
280 if (0 == nacl_multimedia.initialized)
281 Fatal("nacl_video_shutdown: multimedia not initialized!");
282 if (0 == nacl_multimedia.have_video) {
283 Error("nacl_video_shutdown: video already shutdown!");
284 errno = EPERM;
285 goto done;
287 nacl_multimedia.have_video = 0;
288 retval = 0;
289 done:
290 r = pthread_mutex_unlock(&nacl_multimedia_mutex);
291 if (0 != r)
292 Fatal("nacl_video_shutdown: mutex unlock failed");
293 return retval;
297 int nacl_video_update(const void *data) {
298 int32_t r;
299 int32_t retval = -1;
300 SDL_Surface *image;
302 if (0 == nacl_multimedia.have_video)
303 Fatal("nacl_video_update: video not initialized");
305 image = SDL_CreateRGBSurfaceFrom((unsigned char*) data,
306 nacl_multimedia.video.width,
307 nacl_multimedia.video.height,
308 nacl_multimedia.video.bits_per_pixel,
309 nacl_multimedia.video.width *
310 nacl_multimedia.video.bytes_per_pixel,
311 nacl_multimedia.video.rmask,
312 nacl_multimedia.video.gmask,
313 nacl_multimedia.video.bmask, 0);
314 if (NULL == image) {
315 Error("nacl_video_update: SDL_CreateRGBSurfaceFrom failed");
316 errno = EPERM;
317 goto done;
320 r = SDL_SetAlpha(image, 0, 255);
321 if (0 != r) {
322 Error("nacl_video_update SDL_SetAlpha failed");
323 errno = EPERM;
324 goto done_free_image;
327 r = SDL_BlitSurface(image, NULL, nacl_multimedia.video.screen, NULL);
328 if (0 != r) {
329 Error("nacl_video_update: SDL_BlitSurface failed");
330 errno = EPERM;
331 goto done_free_image;
334 r = SDL_Flip(nacl_multimedia.video.screen);
335 if (0 != r) {
336 Error("nacl_video_update: SDL_Flip failed");
337 errno = EPERM;
338 goto done_free_image;
341 retval = 0;
342 // fall through to free image and closure
344 done_free_image:
345 SDL_FreeSurface(image);
346 done:
347 return retval;
351 int nacl_video_poll_event(union NaClMultimediaEvent *event) {
352 int retval;
353 int repoll;
354 int sdl_r;
355 SDL_Event sdl_event;
357 if (0 == nacl_multimedia.initialized)
358 Fatal("nacl_video_poll_event: multmedia not initialized!\n");
359 if (0 == nacl_multimedia.have_video)
360 Fatal("nacl_video_poll_event: video not initialized");
362 do {
363 sdl_r = SDL_PollEvent(&sdl_event);
364 repoll = 0;
365 if (sdl_r == 0) {
366 retval = -1;
367 errno = ENODATA;
368 } else {
369 retval = 0;
370 switch (sdl_event.type) {
371 case SDL_ACTIVEEVENT:
372 event->type = NACL_EVENT_ACTIVE;
373 event->active.gain = sdl_event.active.gain;
374 event->active.state = sdl_event.active.state;
375 break;
376 case SDL_KEYDOWN:
377 case SDL_KEYUP:
378 event->type = (SDL_KEYUP == sdl_event.type) ?
379 NACL_EVENT_KEY_UP : NACL_EVENT_KEY_DOWN;
380 event->key.which = sdl_event.key.which;
381 event->key.state = sdl_event.key.state;
382 event->key.keysym.scancode = sdl_event.key.keysym.scancode;
383 event->key.keysym.sym = sdl_event.key.keysym.sym;
384 event->key.keysym.mod = sdl_event.key.keysym.mod;
385 event->key.keysym.unicode = sdl_event.key.keysym.unicode;
386 break;
387 case SDL_MOUSEMOTION:
388 event->type = NACL_EVENT_MOUSE_MOTION;
389 event->motion.which = sdl_event.motion.which;
390 event->motion.state = sdl_event.motion.state;
391 event->motion.x = sdl_event.motion.x;
392 event->motion.y = sdl_event.motion.y;
393 event->motion.xrel = sdl_event.motion.xrel;
394 event->motion.yrel = sdl_event.motion.yrel;
395 break;
396 case SDL_MOUSEBUTTONDOWN:
397 case SDL_MOUSEBUTTONUP:
398 event->type = (SDL_MOUSEBUTTONUP == sdl_event.type) ?
399 NACL_EVENT_MOUSE_BUTTON_UP :
400 NACL_EVENT_MOUSE_BUTTON_DOWN;
401 event->button.which = sdl_event.button.which;
402 event->button.button = sdl_event.button.button;
403 event->button.state = sdl_event.button.state;
404 event->button.x = sdl_event.button.x;
405 event->button.y = sdl_event.button.y;
406 break;
407 case SDL_QUIT:
408 event->type = NACL_EVENT_QUIT;
409 break;
410 default:
411 // an sdl event happened, but we don't support it
412 // so move along and repoll for another event
413 repoll = 1;
414 break;
417 } while (0 != repoll);
419 return retval;
423 void __NaCl_InternalAudioCallback(void *unused, Uint8 *stream, int size) {
424 int r;
426 r = pthread_mutex_lock(&nacl_multimedia.audio.mutex);
427 if (0 != r)
428 Fatal("__NaCl_InternalAudioCallback: mutex lock failed");
429 if (0 == nacl_multimedia.audio.ready) {
430 nacl_multimedia.audio.stream = stream;
431 nacl_multimedia.audio.size = size;
432 nacl_multimedia.audio.ready = 1;
433 r = pthread_cond_signal(&nacl_multimedia.audio.condvar);
434 if (0 != r)
435 Fatal("__NaCl_InternalAudioCallback: cond var signal failed");
436 // wait for return signal
437 while ((1 == nacl_multimedia.audio.ready) &&
438 (0 == nacl_multimedia.audio.shutdown)) {
439 r = pthread_cond_wait(&nacl_multimedia.audio.condvar,
440 &nacl_multimedia.audio.mutex);
441 if (0 != r)
442 Fatal("__NaCl_InternalAudioCallback: cond var wait failed");
445 r = pthread_mutex_unlock(&nacl_multimedia.audio.mutex);
446 if (0 != r)
447 Fatal("__NaCl_InternalAudioCallback: mutex unlock failed");
451 int nacl_audio_init(enum NaClAudioFormat format,
452 int desired_samples,
453 int *obtained_samples) {
454 SDL_AudioSpec desired;
455 int32_t retval = -1;
456 int r;
458 r = pthread_mutex_lock(&nacl_multimedia_mutex);
459 if (0 != r)
460 Fatal("nacl_audio_init: mutex lock failed");
462 if (0 == nacl_multimedia.initialized)
463 Fatal("nacl_audio_init: multimedia not initialized!");
465 // for now, don't allow an app to open more than one SDL audio device
466 if (0 != nacl_multimedia.have_audio)
467 Fatal("nacl_audio_init: already initialized!");
469 if ((desired_samples < 128) || (desired_samples > 8192)) {
470 Error("nacl_audio_init: desired sample value out of range");
471 errno = ERANGE;
472 goto done;
475 memset(&nacl_multimedia.audio, 0, sizeof(nacl_multimedia.audio));
476 memset(&desired, 0, sizeof(desired));
478 r = pthread_mutex_init(&nacl_multimedia.audio.mutex, NULL);
479 if (0 != r)
480 Fatal("nacl_audio_init: mutex init failed");
481 r = pthread_cond_init(&nacl_multimedia.audio.condvar, NULL);
482 if (0 != r)
483 Fatal("nacl_audio_init: cond var ctor failed");
485 nacl_multimedia.audio.thread_self = 0;
486 nacl_multimedia.audio.size = 0;
487 nacl_multimedia.audio.ready = 0;
488 nacl_multimedia.audio.first = 1;
489 nacl_multimedia.audio.shutdown = 0;
491 desired.format = AUDIO_S16LSB;
492 desired.channels = 2;
493 desired.samples = desired_samples;
494 desired.callback = __NaCl_InternalAudioCallback;
496 if (NACL_AUDIO_FORMAT_STEREO_44K == format) {
497 desired.freq = 44100;
498 } else if (NACL_AUDIO_FORMAT_STEREO_48K == format) {
499 desired.freq = 48000;
500 } else {
501 // we only support two simple high quality stereo formats
502 Error("nacl_audio_init: unsupported format");
503 errno = EINVAL;
504 goto done;
507 if (SDL_OpenAudio(&desired, &nacl_multimedia.audio.obtained) < 0) {
508 Error("nacl_audio_init: Couldn't open SDL audio");
509 Error(SDL_GetError());
510 errno = EIO;
511 goto done;
514 if (nacl_multimedia.audio.obtained.format != desired.format) {
515 Error("nacl_audio_init: Couldn't get desired format");
516 errno = EIO;
517 goto done_close;
520 *obtained_samples = nacl_multimedia.audio.obtained.samples;
522 nacl_multimedia.have_audio = 1;
523 retval = 0;
524 goto done;
526 done_close:
527 SDL_CloseAudio();
529 done:
530 r = pthread_mutex_unlock(&nacl_multimedia_mutex);
531 if (0 != r)
532 Fatal("nacl_audio_init: mutex unlock failed");
534 return retval;
538 int nacl_audio_shutdown() {
539 int r;
541 r = pthread_mutex_lock(&nacl_multimedia_mutex);
542 if (0 != r)
543 Fatal("nacl_audio_shutdown: mutex lock failed");
544 if (0 == nacl_multimedia.initialized)
545 Fatal("nacl_audio_shutdown: multimedia not initialized");
546 if (0 == nacl_multimedia.have_audio)
547 Fatal("nacl_audio_shutdown: audio not initialized!");
548 // tell audio thread we're shutting down
549 r = pthread_mutex_lock(&nacl_multimedia.audio.mutex);
550 if (0 != r)
551 Fatal("nacl_audio_shutdown: mutex lock failed");
552 SDL_PauseAudio(1);
553 nacl_multimedia.audio.shutdown = 1;
554 r = pthread_cond_broadcast(&nacl_multimedia.audio.condvar);
555 if (0 != r)
556 Fatal("nacl_audio_shutdown: cond var broadcast failed");
557 r = pthread_mutex_unlock(&nacl_multimedia.audio.mutex);
558 if (0 != r)
559 Fatal("nacl_audio_shutdown: mutex unlock failed");
560 // close out audio
561 SDL_CloseAudio();
562 // no more callbacks at this point
563 nacl_multimedia.have_audio = 0;
564 r = pthread_mutex_unlock(&nacl_multimedia_mutex);
565 if (0 != r)
566 Fatal("nacl_audio_shutdown: mutex unlock failed");
567 return 0;
572 // returns 0 during normal operation.
573 // returns -1 indicating that it is time to exit the audio thread
574 int32_t nacl_audio_stream(const void *data,
575 size_t *size) {
576 int32_t r;
577 int32_t retval = -1;
579 r = pthread_mutex_lock(&nacl_multimedia_mutex);
580 if (0 != r)
581 Fatal("nacl_audio_stream: global mutex lock failed");
583 if (0 == nacl_multimedia.have_audio) {
584 goto done;
587 if (nacl_multimedia.audio.first) {
588 // don't copy data on first call...
589 nacl_multimedia.audio.thread_self = pthread_self();
590 } else {
591 if (nacl_multimedia.audio.thread_self != pthread_self()) {
592 Fatal("nacl_audio_stream: called from different thread");
595 // copy the audio data into the sdl audio buffer
596 memcpy(nacl_multimedia.audio.stream, data, nacl_multimedia.audio.size);
599 // callback synchronization
600 r = pthread_mutex_lock(&nacl_multimedia.audio.mutex);
601 if (0 != r)
602 Fatal("nacl_audio_stream: audio mutex lock failed");
603 // unpause audio on first, start callbacks
604 if (nacl_multimedia.audio.first) {
605 SDL_PauseAudio(0);
606 nacl_multimedia.audio.first = 0;
609 // signal callback (if it is waiting)
610 if (nacl_multimedia.audio.ready != 0) {
611 nacl_multimedia.audio.ready = 0;
612 r = pthread_cond_signal(&nacl_multimedia.audio.condvar);
613 if (0 != r)
614 Fatal("nacl_audio_stream: cond var signal failed");
617 nacl_multimedia.audio.size = 0;
619 // wait for next callback
620 while ((0 == nacl_multimedia.audio.ready) &&
621 (0 == nacl_multimedia.audio.shutdown)) {
622 r = pthread_cond_wait(&nacl_multimedia.audio.condvar,
623 &nacl_multimedia.audio.mutex);
624 if (0 != r)
625 Fatal("nacl_audio_stream: cond var wait failed");
628 if (0 != nacl_multimedia.audio.shutdown) {
629 nacl_multimedia.audio.size = 0;
630 *size = 0;
631 goto done;
633 // return size of next audio block
634 *size = (size_t)nacl_multimedia.audio.size;
635 r = pthread_mutex_unlock(&nacl_multimedia.audio.mutex);
636 if (0 != r)
637 Fatal("nacl_audio_stream: audio mutex unlock failed");
638 retval = 0;
640 done:
641 r = pthread_mutex_unlock(&nacl_multimedia_mutex);
642 if (0 != r)
643 Fatal("nacl_audio_stream: global mutex unlock failed");
644 return retval;
648 #endif // STANDALONE