Exemplo n.º 1
0
static int queue_picture(VideoState * is, AVFrame * pFrame, double pts)
{
   VideoPicture *vp;
   
   if (!is->first) {
      is->first = true;
      is->external_clock_start = av_gettime();
   }

   /* wait until we have space for a new pic */
   al_lock_mutex(is->pictq_mutex);
   while (is->pictq_size >= VIDEO_PICTURE_QUEUE_SIZE && !is->quit) {
      al_wait_cond(is->pictq_cond, is->pictq_mutex);
   }
   al_unlock_mutex(is->pictq_mutex);

   if (is->quit)
      return -1;

   // windex is set to 0 initially
   vp = &is->pictq[is->pictq_windex];
   vp->dropped = false;

   {
      ALLEGRO_EVENT event;

      vp->frame = pFrame;
      vp->pts = pts;
      vp->allocated = 0;
      /* we have to do it in the main thread */
      //printf("allocate %d (%4.1f ms)\n", is->pictq_windex,
      //   get_master_clock(is) * 1000);
      event.type = ALLEGRO_EVENT_VIDEO_FRAME_ALLOC;
      event.user.data1 = (intptr_t)is->video;
      al_emit_user_event(&is->video->es, &event, NULL);

      /* wait until we have a picture allocated */
      al_lock_mutex(is->pictq_mutex);
      is->got_picture++;
      while (!vp->allocated && !is->quit) {
         al_wait_cond(is->pictq_cond, is->pictq_mutex);
      }
      al_unlock_mutex(is->pictq_mutex);
      if (is->quit) {
         return -1;
      }
   }

   return 0;
}
Exemplo n.º 2
0
/* This is called from the termination message - it has to return soon as the
 * user expects the app to close when it is closed.
 */
void _al_iphone_await_termination(void)
{
    ALLEGRO_INFO("Application awaiting termination.\n");
    al_lock_mutex(iphone->mutex);
    while (!iphone->has_shutdown) {
        al_wait_cond(iphone->cond, iphone->mutex);
    }
    al_unlock_mutex(iphone->mutex);
}
Exemplo n.º 3
0
static void *thread_func(ALLEGRO_THREAD *thr, void *arg)
{
   ThreadInfo *info = (ThreadInfo *) arg;
   Viewport viewport;
   unsigned char palette[256][3];
   int y, w, h;

   y = 0;
   w = al_get_bitmap_width(info->bitmap);
   h = al_get_bitmap_height(info->bitmap);

   viewport.centre_x = info->target_x;
   viewport.centre_y = info->target_y;
   viewport.x_extent = 3.0;
   viewport.y_extent = 3.0;
   viewport.zoom = 1.0;
   info->target_x = 0;
   info->target_y = 0;

   while (!al_get_thread_should_stop(thr)) {
      al_lock_mutex(info->mutex);

      while (info->is_paused) {
         al_wait_cond(info->cond, info->mutex);

         /* We might be awoken because the program is terminating. */
         if (al_get_thread_should_stop(thr)) {
            break;
         }
      }

      if (!info->is_paused) {
         if (y == 0) {
            random_palette(palette, &info->random_seed);
         }

         draw_mandel_line(info->bitmap, &viewport, palette, y);

         y++;
         if (y >= h) {
            double z = viewport.zoom;
            y = 0;
            viewport.centre_x += z * viewport.x_extent * info->target_x;
            viewport.centre_y += z * viewport.y_extent * info->target_y;
            info->target_x = 0;
            info->target_y = 0;
            viewport.zoom *= 0.99;
         }
      }

      al_unlock_mutex(info->mutex);
      al_rest(0);
   }

   return NULL;
}
/* Function: al_open_native_text_log
 */
ALLEGRO_TEXTLOG *al_open_native_text_log(char const *title, int flags)
{
   ALLEGRO_NATIVE_DIALOG *textlog = NULL;

   /* Avoid warnings when log windows are unimplemented. */
   (void)title;
   (void)flags;

   textlog = al_calloc(1, sizeof *textlog);
   textlog->title = al_ustr_new(title);
   textlog->flags = flags;
   if (TEXT_LOG_EXTRA_THREAD) {
      textlog->tl_thread = al_create_thread(text_log_thread_proc, textlog);
   }
   textlog->tl_text_cond = al_create_cond();
   textlog->tl_text_mutex = al_create_mutex();
   textlog->tl_pending_text = al_ustr_new("");
   al_init_user_event_source(&textlog->tl_events);

   textlog->tl_init_error = false;
   textlog->tl_done = false;

   if (TEXT_LOG_EXTRA_THREAD) {
      /* Unlike the other dialogs, this one never blocks as the intended
       * use case is a log window running in the background for debugging
       * purposes when no console can be used. Therefore we have it run
       * in a separate thread.
       */
      al_start_thread(textlog->tl_thread);
      al_lock_mutex(textlog->tl_text_mutex);
      while (!textlog->tl_done && !textlog->tl_init_error) {
         al_wait_cond(textlog->tl_text_cond, textlog->tl_text_mutex);
      }
      al_unlock_mutex(textlog->tl_text_mutex);
   }
   else {
      textlog->tl_init_error = !_al_open_native_text_log(textlog);
   }

   if (textlog->tl_init_error) {
      al_close_native_text_log((ALLEGRO_TEXTLOG *)textlog);
      return NULL;
   }

   _al_register_destructor(_al_dtor_list, textlog,
      (void (*)(void *))al_close_native_text_log);

   return (ALLEGRO_TEXTLOG *)textlog;
}
Exemplo n.º 5
0
static int pulseaudio_stop_voice(ALLEGRO_VOICE *voice)
{
   PULSEAUDIO_VOICE *pv = voice->extra;

   /* We hold the voice->mutex already. */

   if (pv->status == PV_PLAYING) {
      pv->status = PV_STOPPING;
      al_broadcast_cond(pv->status_cond);
   }

   while (pv->status != PV_IDLE) {
      al_wait_cond(pv->status_cond, voice->mutex);
   }

   return 0;
}
Exemplo n.º 6
0
/* The stop_voice method should stop playback. For non-streaming voices, it
   should leave the data loaded, and reset the voice position to 0. */
static int _dsound_stop_voice(ALLEGRO_VOICE* voice)
{
   ALLEGRO_DS_DATA *ex_data = (ALLEGRO_DS_DATA *)voice->extra;

   ALLEGRO_DEBUG("Stopping voice\n");

   if (!ex_data->ds8_buffer) {
      ALLEGRO_ERROR("Trying to stop empty voice buffer\n");
      return 1;
   }

   /* if playing a sample */
   if (!voice->is_streaming) {
      ALLEGRO_DEBUG("Stopping non-streaming voice\n");
      ex_data->ds8_buffer->Stop();
      ex_data->ds8_buffer->SetCurrentPosition(0);
      ALLEGRO_INFO("Non-streaming voice stopped\n");
      return 0;
   }

   if (ex_data->stop_voice == 0) {
      ALLEGRO_DEBUG("Joining thread\n");
      ex_data->stop_voice = 1;
      while (ex_data->stop_voice == 1) {
	  al_wait_cond(voice->cond, voice->mutex);
      }
      al_join_thread(ex_data->thread, NULL);
      ALLEGRO_DEBUG("Joined thread\n");

      ALLEGRO_DEBUG("Destroying thread\n");
      al_destroy_thread(ex_data->thread);
      ALLEGRO_DEBUG("Thread destroyed\n");
      /* This is required to restart the background thread when the voice
       * restarts.
       */
      ex_data->stop_voice = 1;
   }

   ALLEGRO_DEBUG("Releasing buffer\n");
   ex_data->ds8_buffer->Release();
   ex_data->ds8_buffer = NULL;

   ALLEGRO_INFO("Voice stopped\n");
   return 0;
}
Exemplo n.º 7
0
/* [user thread] */
bool _al_gtk_wait_for_args(GSourceFunc func, void *data)
{
   ARGS_BASE *args = (ARGS_BASE *) data;
   bool response;

   al_lock_mutex(args->mutex);
   g_timeout_add(0, func, data);
   while (args->done == false) {
      al_wait_cond(args->cond, args->mutex);
   }
   al_unlock_mutex(args->mutex);

   response = args->response;

   al_destroy_mutex(args->mutex);
   al_destroy_cond(args->cond);

   return response;
}
Exemplo n.º 8
0
/* The stop_voice method should stop playback. For non-streaming voices, it
   should leave the data loaded, and reset the voice position to 0. */
static int _openal_stop_voice(ALLEGRO_VOICE* voice)
{
   ALLEGRO_AL_DATA *ex_data = voice->extra;
   ALenum openal_err;

   if (!ex_data->buffers) {
      ALLEGRO_WARN("Trying to stop empty voice buffer\n");
      return 1;
   }

   /* if playing a sample */
   if (!voice->is_streaming) {
      alSourceStop(ex_data->source);
      if ((openal_err = alGetError()) != AL_NO_ERROR) {
         ALLEGRO_ERROR("Could not stop voice: %s\n",
            openal_get_err_str(openal_err));
         return 1;
      }
      return 0;
   }

   if (ex_data->thread) {
      al_set_thread_should_stop(ex_data->thread);
      while (!ex_data->stopped) {
         al_wait_cond(voice->cond, voice->mutex);
      }
      al_join_thread(ex_data->thread, NULL);
      ex_data->thread = NULL;
      ex_data->stopped = false;
   }
   
   alSourcei(ex_data->source, AL_BUFFER, 0);
   alDeleteSources(1, &ex_data->source);
   alDeleteBuffers(ex_data->num_buffers, ex_data->buffers);
   al_free(ex_data->buffers);
   ex_data->buffers = NULL;
   
   alGetError(); /* required! */
   return 0;
}
/* Function: al_close_native_text_log
 */
void al_close_native_text_log(ALLEGRO_TEXTLOG *textlog)
{
   ALLEGRO_NATIVE_DIALOG *dialog = (ALLEGRO_NATIVE_DIALOG *)textlog;

   if (!dialog)
      return;

   if (!dialog->tl_init_error) {
      dialog->tl_done = false;

      if (TEXT_LOG_EXTRA_THREAD) {
         al_lock_mutex(dialog->tl_text_mutex);
         _al_close_native_text_log(dialog);
         while (!dialog->tl_done) {
            al_wait_cond(dialog->tl_text_cond, dialog->tl_text_mutex);
         }
      }
      else {
         _al_close_native_text_log(dialog);
         al_lock_mutex(dialog->tl_text_mutex);
      }

      _al_unregister_destructor(_al_dtor_list, dialog);
   }

   al_ustr_free(dialog->title);
   al_ustr_free(dialog->tl_pending_text);

   al_destroy_user_event_source(&dialog->tl_events);

   al_unlock_mutex(dialog->tl_text_mutex);

   if (TEXT_LOG_EXTRA_THREAD) {
      al_destroy_thread(dialog->tl_thread);
   }
   al_destroy_cond(dialog->tl_text_cond);
   al_destroy_mutex(dialog->tl_text_mutex);
   al_free(dialog);
}
Exemplo n.º 10
0
static void *hotplug_proc(ALLEGRO_THREAD *thread, void *data)
{
    (void)data;

    while (!al_get_thread_should_stop(thread)) {
        if (!hotplug_ended) {
            al_wait_cond(hotplug_cond, hotplug_mutex);
        }
        if (hotplug_ended) {
            break;
        }

        al_rest(1);

        al_lock_mutex(config_mutex);
        ljoy_scan(true);
        al_unlock_mutex(config_mutex);
    }

    hotplug_ended = false;

    return NULL;
}
Exemplo n.º 11
0
static int packet_queue_get(VideoState *is, PacketQueue * q, AVPacket * pkt, int block)
{
   AVPacketList *pkt1;
   int ret;

   al_lock_mutex(q->mutex);

   for (;;) {

      if (is->quit) {
         ret = -1;
         break;
      }

      pkt1 = q->first_pkt;
      if (pkt1) {
         q->first_pkt = pkt1->next;
         if (!q->first_pkt)
            q->last_pkt = NULL;
         q->nb_packets--;
         q->size -= pkt1->pkt.size;
         *pkt = pkt1->pkt;
         av_free(pkt1);
         ret = 1;
         break;
      }
      else if (!block) {
         ret = 0;
         break;
      }
      else {
         al_wait_cond(q->cond, q->mutex);
      }
   }
   al_unlock_mutex(q->mutex);
   return ret;
}
Exemplo n.º 12
0
static void *decode_thread(ALLEGRO_THREAD *t, void *arg)
{
   VideoState *is = (VideoState *) arg;
   AVFormatContext *format_context = is->format_context;
   AVPacket pkt1, *packet = &pkt1;

   is->videoStream = -1;
   is->audioStream = -1;

   if (is->audio_index >= 0) {
      stream_component_open(is, is->audio_index);
   }
   if (is->video_index >= 0) {
      stream_component_open(is, is->video_index);
   }

   if (is->videoStream < 0 && is->audioStream < 0) {
      ALLEGRO_ERROR("%s: could not open codecs\n", is->filename);
      goto fail;
   }

   for (;;) {
      if (is->quit) {
         break;
      }

      if (is->seek_req) {
         int stream_index = -1;
         int64_t seek_target = is->seek_pos;

         if (is->videoStream >= 0)
            stream_index = is->videoStream;
         else if (is->audioStream >= 0)
            stream_index = is->audioStream;

         if (stream_index >= 0) {
            seek_target =
                av_rescale_q(seek_target, AV_TIME_BASE_Q,
                             format_context->streams[stream_index]->time_base);
         }
         
         if (av_seek_frame(is->format_context, stream_index, seek_target,
            is->seek_flags) < 0) {
            ALLEGRO_WARN("%s: error while seeking (%d, %lu)\n",
                    is->format_context->filename, stream_index, seek_target);
         }
         else {
            if (is->audioStream >= 0) {
               packet_queue_flush(&is->audioq);
               packet_queue_put(&is->audioq, &flush_pkt);
            }
            if (is->videoStream >= 0) {
               packet_queue_flush(&is->videoq);
               packet_queue_put(&is->videoq, &flush_pkt);
            }
         }
         is->seek_req = 0;
         is->after_seek_sync = true;
         
      }
      if (is->audioq.size > MAX_AUDIOQ_SIZE ||
          is->videoq.size > MAX_VIDEOQ_SIZE) {
         al_rest(0.01);
         continue;
      }
      if (av_read_frame(is->format_context, packet) < 0) {
         #ifdef FFMPEG_0_8
         if (!format_context->pb->eof_reached && !format_context->pb->error) {
         #else
         if (url_ferror((void *)&format_context->pb) == 0) {
         #endif
            al_rest(0.01);
            continue;
         }
         else {
            break;
         }
      }
      // Is this a packet from the video stream?
      if (packet->stream_index == is->videoStream) {
         packet_queue_put(&is->videoq, packet);
      }
      else if (packet->stream_index == is->audioStream) {
         packet_queue_put(&is->audioq, packet);
      }
      else {
         av_free_packet(packet);
      }
   }
   /* all done - wait for it */
   while (!is->quit) {
      al_rest(0.1);
   }

 fail:
   return t;
}

/* We want to be able to send an event to the user exactly at the time
 * a new video frame should be displayed.
 */
static void *timer_thread(ALLEGRO_THREAD *t, void *arg)
{
   VideoState *is = (VideoState *) arg;
   double ot = 0, nt = 0;
   while (!is->quit) {
      ALLEGRO_EVENT event;
      double d;

      /* Wait here until someone signals to us when a new frame was
       * scheduled at is->show_next.
       */
      al_lock_mutex(is->timer_mutex);
      al_wait_cond(is->timer_cond, is->timer_mutex);
      al_unlock_mutex(is->timer_mutex);
      
      if (is->quit) break;
      
      /* Wait until that time. This wait is why we have our own thread
       * here so the user doesn't need to do it.
       */
      while (1) {
         d = is->show_next - get_master_clock(is);
         if (d <= 0) break;
         //printf("waiting %4.1f ms\n", d * 1000);
         al_rest(d);
      }

      nt = get_master_clock(is);
      //printf("event after %4.1f ms\n", (nt - ot) * 1000);
      ot = nt;
      /* Now is the time. */
      event.type = ALLEGRO_EVENT_VIDEO_FRAME_SHOW;
      event.user.data1 = (intptr_t)is->video;
      al_emit_user_event(&is->video->es, &event, NULL);
   }
   return t;
}
Exemplo n.º 13
0
static void *pulseaudio_update(ALLEGRO_THREAD *self, void *data)
{
   ALLEGRO_VOICE *voice = data;
   PULSEAUDIO_VOICE *pv = voice->extra;
   (void)self;

   for (;;) {
      enum PULSEAUDIO_VOICE_STATUS status;

      al_lock_mutex(voice->mutex);
      while ((status = pv->status) == PV_IDLE) {
         al_wait_cond(pv->status_cond, voice->mutex);
      }
      al_unlock_mutex(voice->mutex);

      if (status == PV_JOIN) {
         break;
      }

      if (status == PV_PLAYING) {
         unsigned int frames = pv->buffer_size_in_frames;
         if (voice->is_streaming) { 
            // streaming audio           
            const void *data = _al_voice_update(voice, voice->mutex, &frames);
            if (data) {
               pa_simple_write(pv->s, data,
                  frames * pv->frame_size_in_bytes, NULL);
            }
         }
         else {
            // direct buffer audio
            al_lock_mutex(pv->buffer_mutex);
            const char *data = pv->buffer;
            unsigned int len = frames * pv->frame_size_in_bytes;
            pv->buffer += frames * pv->frame_size_in_bytes;
            if (pv->buffer > pv->buffer_end) {
               len = pv->buffer_end - data;
               pv->buffer = voice->attached_stream->spl_data.buffer.ptr;
               voice->attached_stream->pos = 0;
               if (voice->attached_stream->loop == ALLEGRO_PLAYMODE_ONCE) {
                  al_lock_mutex(voice->mutex);
                  pv->status = PV_STOPPING;
                  al_broadcast_cond(pv->status_cond);
                  al_unlock_mutex(voice->mutex);
               }
            }
            else {
               voice->attached_stream->pos += frames;
            }
            al_unlock_mutex(pv->buffer_mutex);

            pa_simple_write(pv->s, data, len, NULL);
         }
      }
      else if (status == PV_STOPPING) {
         pa_simple_drain(pv->s, NULL);
         al_lock_mutex(voice->mutex);
         pv->status = PV_IDLE;
         al_broadcast_cond(pv->status_cond);
         al_unlock_mutex(voice->mutex);
      }
   }

   return NULL;
}
Exemplo n.º 14
0
static inline void HandleEvent(struct Game* game, ALLEGRO_EVENT* ev) {
	switch (ev->type) {
		case ALLEGRO_EVENT_DISPLAY_HALT_DRAWING:
			PauseExecution(game);
			al_acknowledge_drawing_halt(game->display);
			break;

		case ALLEGRO_EVENT_DISPLAY_RESUME_DRAWING:
			al_acknowledge_drawing_resume(game->display);
			ReloadGamestates(game);
			ResumeExecution(game);
			break;

		case ALLEGRO_EVENT_DISPLAY_SWITCH_OUT:
			if (game->config.autopause) {
				PrintConsole(game, "Focus lost, autopausing...");
				PauseExecution(game);
			}
			break;

		case ALLEGRO_EVENT_DISPLAY_SWITCH_IN:
			if (game->config.autopause) {
				if (game->config.debug.enabled && game->config.debug.livereload) {
					ReloadCode(game);
				}
				ResumeExecution(game);
			}
			break;

		case ALLEGRO_EVENT_DISPLAY_RESIZE:
			PrintConsole(game, "Resize event: %dx%d", ev->display.width, ev->display.height);

#ifdef LIBSUPERDERPY_IMGUI
			ImGui_ImplAllegro5_InvalidateDeviceObjects();
#endif
			al_acknowledge_resize(game->display);
#ifdef LIBSUPERDERPY_IMGUI
			ImGui_ImplAllegro5_CreateDeviceObjects();
#endif

			// SetupViewport can be expensive, so don't do it when the resize event is already outdated or doesn't change anything
			if (((ev->display.width != game->_priv.window_width) || (ev->display.height != game->_priv.window_height)) &&
				(ev->display.width == al_get_display_width(game->display)) && (ev->display.height == al_get_display_height(game->display))) {
				SetupViewport(game);
			}

			break;
		case ALLEGRO_EVENT_KEY_DOWN:
#ifdef ALLEGRO_ANDROID
			if ((ev->keyboard.keycode == ALLEGRO_KEY_MENU) || (ev->keyboard.keycode == ALLEGRO_KEY_TILDE) || (ev->keyboard.keycode == ALLEGRO_KEY_BACKQUOTE)) {
#else
			if ((ev->keyboard.keycode == ALLEGRO_KEY_TILDE) || (ev->keyboard.keycode == ALLEGRO_KEY_BACKQUOTE)) {
#endif
				game->_priv.showconsole = !game->_priv.showconsole;
				if ((ev->keyboard.modifiers & ALLEGRO_KEYMOD_CTRL) && (game->config.debug.enabled)) {
					game->_priv.showtimeline = game->_priv.showconsole;
				}
			}

			if (ev->keyboard.keycode == ALLEGRO_KEY_F12) {
				DrawGamestates(game);
				int flags = al_get_new_bitmap_flags();
				al_set_new_bitmap_flags(ALLEGRO_MEMORY_BITMAP);
				ALLEGRO_BITMAP* bitmap = al_create_bitmap(al_get_display_width(game->display), al_get_display_height(game->display));
				al_set_new_bitmap_flags(flags);
				ALLEGRO_BITMAP* target = al_get_target_bitmap();
				al_set_target_bitmap(bitmap);
				al_draw_bitmap(al_get_backbuffer(game->display), 0, 0, 0);
				al_set_target_bitmap(target);
				PrintConsole(game, "Screenshot made! Storing...");

				struct ScreenshotThreadData* data = malloc(sizeof(struct ScreenshotThreadData));
				data->game = game;
				data->bitmap = bitmap;
#ifndef LIBSUPERDERPY_SINGLE_THREAD
				al_run_detached_thread(ScreenshotThread, data);
#else
				ScreenshotThread(data);
#endif
			}

			break;
		default:
			break;
	}

#ifdef MAEMO5
	// on Maemo we get mouse events instead of touch ones, so we'll rewrite them by ourselves

	if ((ev->type == ALLEGRO_EVENT_MOUSE_AXES) || (ev->type == ALLEGRO_EVENT_MOUSE_BUTTON_DOWN) || (ev->type == ALLEGRO_EVENT_MOUSE_BUTTON_UP)) {
		switch (ev->type) {
			case ALLEGRO_EVENT_MOUSE_BUTTON_DOWN:
				ev->type = ALLEGRO_EVENT_TOUCH_BEGIN;
				break;
			case ALLEGRO_EVENT_MOUSE_BUTTON_UP:
				ev->type = ALLEGRO_EVENT_TOUCH_END;
				break;
			case ALLEGRO_EVENT_MOUSE_AXES:
				ev->type = ALLEGRO_EVENT_TOUCH_MOVE;
				break;
			default:
				break;
		}
		ALLEGRO_DISPLAY* display = ev->mouse.display;
		float dx = ev->mouse.dx;
		float dy = ev->mouse.dy;
		float x = ev->mouse.x;
		float y = ev->mouse.y;
		double timestamp = ev->mouse.timestamp;

		ev->touch.display = display;
		ev->touch.dx = dx;
		ev->touch.dy = dy;
		ev->touch.id = 0;
		ev->touch.primary = true;
		ev->touch.source = (ALLEGRO_TOUCH_INPUT*)al_get_touch_input_event_source();
		ev->touch.timestamp = timestamp;
		ev->touch.x = x;
		ev->touch.y = y;
	}
#endif
}

static inline void HandleDebugEvent(struct Game* game, ALLEGRO_EVENT* ev) {
	switch (ev->type) {
		case ALLEGRO_EVENT_KEY_DOWN:
			switch (ev->keyboard.keycode) {
				case ALLEGRO_KEY_F1:
					if (!game->_priv.paused) {
						PauseExecution(game);
					} else {
						ReloadCode(game);
						ResumeExecution(game);
					}
					break;
				case ALLEGRO_KEY_F9:
					game->_priv.speed = ALLEGRO_BPS_TO_SECS(60.0);
					game->_priv.showconsole = true;
					PrintConsole(game, "DEBUG: Gameplay speed: 1.00x");
					break;
				case ALLEGRO_KEY_F10: {
					double speed = ALLEGRO_BPS_TO_SECS(game->_priv.speed); // inverting
					speed -= 10;
					if (speed < 10) { speed = 10; }
					game->_priv.speed = ALLEGRO_BPS_TO_SECS(speed);
					game->_priv.showconsole = true;
					PrintConsole(game, "DEBUG: Gameplay speed: %.2fx", speed / 60.0);
				} break;
				case ALLEGRO_KEY_F11: {
					double speed = ALLEGRO_BPS_TO_SECS(game->_priv.speed); // inverting
					speed += 10;
					if (speed > 600) { speed = 600; }
					game->_priv.speed = ALLEGRO_BPS_TO_SECS(speed);
					game->_priv.showconsole = true;
					PrintConsole(game, "DEBUG: Gameplay speed: %.2fx", speed / 60.0);
				} break;
			}

			break;
		default:
			break;
	}
}

static inline bool MainloopEvents(struct Game* game) {
	do {
		ALLEGRO_EVENT ev;

		if (game->_priv.paused && !IS_EMSCRIPTEN) {
			// there's no frame flipping when paused, so avoid pointless busylooping
			al_wait_for_event(game->_priv.event_queue, &ev);
		} else if (!al_get_next_event(game->_priv.event_queue, &ev)) {
			break;
		}

#ifdef LIBSUPERDERPY_IMGUI
		ImGui_ImplAllegro5_ProcessEvent(&ev);
		switch (ev.type) {
			case ALLEGRO_EVENT_KEY_CHAR:
			case ALLEGRO_EVENT_KEY_DOWN:
			case ALLEGRO_EVENT_KEY_UP:
				if (igGetIO()->WantCaptureKeyboard) {
					continue;
				}
				break;
			case ALLEGRO_EVENT_MOUSE_AXES:
			case ALLEGRO_EVENT_MOUSE_BUTTON_DOWN:
			case ALLEGRO_EVENT_MOUSE_BUTTON_UP:
			case ALLEGRO_EVENT_TOUCH_BEGIN:
			case ALLEGRO_EVENT_TOUCH_CANCEL:
			case ALLEGRO_EVENT_TOUCH_END:
			case ALLEGRO_EVENT_TOUCH_MOVE:
				if (igGetIO()->WantCaptureMouse) {
					continue;
				}
				break;
			default:
				break;
		}
#endif

		if (game->_priv.params.handlers.event) {
			if ((*game->_priv.params.handlers.event)(game, &ev)) {
				continue;
			}
		}

		if (ev.type == ALLEGRO_EVENT_DISPLAY_CLOSE) {
			EventGamestates(game, &ev);
			return false;
		}

		HandleEvent(game, &ev);

		if (game->config.debug.enabled) {
			HandleDebugEvent(game, &ev);
		}

		EventGamestates(game, &ev);

	} while (!al_is_event_queue_empty(game->_priv.event_queue));
	return true;
}

static inline bool MainloopTick(struct Game* game) {
	if (game->_priv.paused) {
		return true;
	}

	struct Gamestate* tmp = game->_priv.gamestates;

#ifdef __EMSCRIPTEN__
	emscripten_pause_main_loop();
#endif

	game->_priv.loading.to_load = 0;
	game->_priv.loading.loaded = 0;
	game->_priv.loading.lock = true;
	game->loading.progress = 0;

	// TODO: support gamestate dependences/ordering
	while (tmp) {
		if (tmp->pending_stop) {
			PrintConsole(game, "Stopping gamestate \"%s\"...", tmp->name);
			game->_priv.current_gamestate = tmp;
			(*tmp->api->stop)(game, tmp->data);
			tmp->started = false;
			tmp->pending_stop = false;
			PrintConsole(game, "Gamestate \"%s\" stopped successfully.", tmp->name);
		}

		if (tmp->pending_load) { game->_priv.loading.to_load++; }
		tmp = tmp->next;
	}

	tmp = game->_priv.gamestates;

	while (tmp) {
		if (tmp->pending_unload) {
#ifdef __EMSCRIPTEN__
			al_detach_voice(game->audio.v);
#endif
			PrintConsole(game, "Unloading gamestate \"%s\"...", tmp->name);
			tmp->loaded = false;
			tmp->pending_unload = false;
			game->_priv.current_gamestate = tmp;
			(*tmp->api->unload)(game, tmp->data);
			PrintConsole(game, "Gamestate \"%s\" unloaded successfully.", tmp->name);
#ifdef __EMSCRIPTEN__
			al_attach_mixer_to_voice(game->audio.mixer, game->audio.v);
#endif
		}
		if (tmp->pending_load) {
#ifdef __EMSCRIPTEN__
			al_detach_voice(game->audio.v);
#endif
			if (tmp->show_loading && game->_priv.loading.gamestate->open) {
				(*game->_priv.loading.gamestate->api->start)(game, game->_priv.loading.gamestate->data);
			}

			if (!tmp->api) {
				if (!OpenGamestate(game, tmp, true) || !LinkGamestate(game, tmp)) {
					tmp->pending_load = false;
					tmp->pending_start = false;
					continue;
				}
			}
			if (tmp->api) {
				PrintConsole(game, "Loading gamestate \"%s\"...", tmp->name);
				game->_priv.loading.progress = 0;

				game->_priv.loading.current = tmp;
				game->_priv.current_gamestate = tmp;

				struct GamestateLoadingThreadData data = {.game = game, .gamestate = tmp, .bitmap_flags = al_get_new_bitmap_flags()};
				game->_priv.loading.in_progress = true;
				double time = al_get_time();
				game->_priv.loading.time = time;

				CalculateProgress(game);
				if (tmp->show_loading) {
					game->loading.shown = true;
					DrawGamestates(game);
					DrawConsole(game);
					al_flip_display();
#ifdef __EMSCRIPTEN__
					emscripten_sleep(0);
#endif
				}
#ifndef LIBSUPERDERPY_SINGLE_THREAD
				al_run_detached_thread(GamestateLoadingThread, &data);
				while (game->_priv.loading.in_progress) {
					double delta = al_get_time() - game->_priv.loading.time;
					game->time += delta; // TODO: ability to disable passing time during loading
					game->_priv.loading.time += delta;
					if (game->loading.shown && game->_priv.loading.gamestate->open) {
						(*game->_priv.loading.gamestate->api->logic)(game, game->_priv.loading.gamestate->data, delta);
					}
					DrawGamestates(game);
					if (game->_priv.texture_sync) {
						al_convert_memory_bitmaps();
						game->_priv.texture_sync = false;
						al_signal_cond(game->_priv.texture_sync_cond);
						game->_priv.loading.time = al_get_time(); // TODO: rethink time management during loading
					}
					DrawConsole(game);
					al_flip_display();

					if (game->_priv.bsod_sync) {
						al_set_target_bitmap(NULL);
						game->_priv.bsod_sync = false;
						al_signal_cond(game->_priv.bsod_cond);
					}

					al_lock_mutex(game->_priv.bsod_mutex);
					while (game->_priv.in_bsod) {
						al_wait_cond(game->_priv.bsod_cond, game->_priv.bsod_mutex);
					}
					al_unlock_mutex(game->_priv.bsod_mutex);
				}
#else
				GamestateLoadingThread(&data);
				DrawGamestates(game);
				DrawConsole(game);
				al_flip_display();
#ifdef __EMSCRIPTEN__
				emscripten_sleep(0);
#endif
				al_convert_memory_bitmaps();
#endif

				al_set_new_bitmap_flags(data.bitmap_flags);

				ReloadShaders(game, false);

				if (tmp->api->post_load) {
					PrintConsole(game, "[%s] Post-loading...", tmp->name);
					tmp->api->post_load(game, tmp->data);
				}

				game->_priv.loading.progress++;
				CalculateProgress(game);
				PrintConsole(game, "Gamestate \"%s\" loaded successfully in %f seconds.", tmp->name, al_get_time() - time);
				game->_priv.loading.loaded++;

				DrawGamestates(game);
				DrawConsole(game);
				al_flip_display();
#ifdef __EMSCRIPTEN__
				emscripten_sleep(0);
#endif

				tmp->loaded = true;
				tmp->pending_load = false;
			}
			if (tmp->show_loading && game->_priv.loading.gamestate->open) {
				(*game->_priv.loading.gamestate->api->stop)(game, game->_priv.loading.gamestate->data);
			}
			tmp->show_loading = true;
			game->loading.shown = false;
			game->_priv.timestamp = al_get_time();
#ifdef __EMSCRIPTEN__
			al_attach_mixer_to_voice(game->audio.mixer, game->audio.v);
#endif
		}

		tmp = tmp->next;
	}
Exemplo n.º 15
0
static void *_dsound_update_recorder(ALLEGRO_THREAD *t, void *data)
{
   ALLEGRO_AUDIO_RECORDER *r = (ALLEGRO_AUDIO_RECORDER *) data;
   DSOUND_RECORD_DATA *extra = (DSOUND_RECORD_DATA *) r->extra;
   DWORD last_read_pos = 0;
   ALLEGRO_EVENT user_event;
   bool is_dsound_recording = false;

   size_t fragment_i = 0;
   size_t bytes_written = 0;

   ALLEGRO_INFO("Starting recorder thread\n");

   while (!al_get_thread_should_stop(t)) {
      al_lock_mutex(r->mutex);
      while (!r->is_recording) {
         if (is_dsound_recording) {
            extra->buffer8->Stop();
            is_dsound_recording = false;
         }
         al_wait_cond(r->cond, r->mutex);
         if (al_get_thread_should_stop(t))
            goto stop_recording;
      }

      if (!is_dsound_recording) {
         extra->buffer8->Start(DSCBSTART_LOOPING);
         is_dsound_recording = true;
         extra->buffer8->GetCurrentPosition(NULL, &last_read_pos);
      }

      void *buffer1, *buffer2;
      DWORD buffer1_size, buffer2_size;
      DWORD cap_pos, bytes_to_read;

      extra->buffer8->GetCurrentPosition(NULL, &cap_pos);

      /* never read past the end of the buffer; that way buffer2 is always NULL */
      if (last_read_pos <= cap_pos)
         bytes_to_read = cap_pos - last_read_pos;
      else
         bytes_to_read = extra->desc.dwBufferBytes - last_read_pos;

      if (bytes_to_read) {
         uint8_t *buffer;
         size_t buffer_size;

         extra->buffer8->Lock(last_read_pos, bytes_to_read, &buffer1, &buffer1_size, &buffer2, &buffer2_size, 0);

         ALLEGRO_ASSERT(buffer2 == NULL);

         buffer = (uint8_t *)buffer1;
         buffer_size = buffer1_size;

         while (buffer_size > 0) {
            if (bytes_written + buffer_size <= r->fragment_size) {
               memcpy((uint8_t*) r->fragments[fragment_i] + bytes_written, buffer, buffer_size);
               bytes_written += buffer_size;
               buffer_size = 0;
            }
            else {
               ALLEGRO_AUDIO_RECORDER_EVENT *e;
               size_t bytes_to_write = r->fragment_size - bytes_written;
               memcpy((uint8_t*) r->fragments[fragment_i] + bytes_written, buffer, bytes_to_write);

               buffer_size -= bytes_to_write;
               buffer += bytes_to_write;

               user_event.user.type = ALLEGRO_EVENT_AUDIO_RECORDER_FRAGMENT;
               e = al_get_audio_recorder_event(&user_event);
               e->buffer = r->fragments[fragment_i];
               e->samples = r->samples;
               al_emit_user_event(&r->source, &user_event, NULL);

               /* advance to the next fragment */
               if (++fragment_i == r->fragment_count) {
                  fragment_i = 0;
               }
               bytes_written = 0;
            }
         }

         extra->buffer8->Unlock(buffer1, buffer1_size, buffer2, buffer2_size);

         /* advanced the last read position */
         last_read_pos += bytes_to_read;
         if (last_read_pos >= extra->desc.dwBufferBytes)
            last_read_pos -= extra->desc.dwBufferBytes;
      }
      
      al_unlock_mutex(r->mutex);
      al_rest(0.10);
   }

stop_recording:

   if (is_dsound_recording) {
      extra->buffer8->Stop();
   }

   ALLEGRO_INFO("Leaving recorder thread\n");

   return NULL;
}
Exemplo n.º 16
0
int main(int argc, char **argv){
 
   ALLEGRO_DISPLAY     *display     = NULL;
   ALLEGRO_EVENT_QUEUE *event_queue = NULL;
   ALLEGRO_TIMER       *timer       = NULL;
   ALLEGRO_BITMAP      *bouncer     = NULL;
   ALLEGRO_THREAD      *thread_1    = NULL;
   ALLEGRO_THREAD      *thread_2    = NULL;
 
   bool redraw = true;
 
   if(!al_init()) {
      fprintf(stderr, "failed to initialize allegro!\n");
      return -1;
   }
 
   if(!al_install_mouse()) {
      fprintf(stderr, "failed to initialize the mouse!\n");
      return -1;
   }
 
   timer = al_create_timer(1.0 / FPS);
   if(!timer) {
      fprintf(stderr, "failed to create timer!\n");
      return -1;
   }
 
   display = al_create_display(SCREEN_W, SCREEN_H);
   if(!display) {
      fprintf(stderr, "failed to create display!\n");
      al_destroy_timer(timer);
      return -1;
   }
 
   bouncer = al_create_bitmap(BOUNCER_SIZE, BOUNCER_SIZE);
   if(!bouncer) {
      fprintf(stderr, "failed to create bouncer bitmap!\n");
      al_destroy_display(display);
      al_destroy_timer(timer);
      return -1;
   }
 
   al_set_target_bitmap(bouncer);
   al_clear_to_color(al_map_rgb(255, 0, 255));
   al_set_target_bitmap(al_get_backbuffer(display));
   event_queue = al_create_event_queue();
 
   if(!event_queue) {
      fprintf(stderr, "failed to create event_queue!\n");
      al_destroy_bitmap(bouncer);
      al_destroy_display(display);
      al_destroy_timer(timer);
      return -1;
   }
 
   al_register_event_source(event_queue, al_get_display_event_source(display));
   al_register_event_source(event_queue, al_get_timer_event_source(timer));
   al_register_event_source(event_queue, al_get_mouse_event_source());
   al_clear_to_color(al_map_rgb(0,0,0));
   al_flip_display();
   al_start_timer(timer);
 
   DATA data;
 
   thread_1 = al_create_thread(Func_Thread, &data);
   al_start_thread(thread_1);
 
   al_lock_mutex(data.mutex);
   while (!data.ready){
 
      al_wait_cond(data.cond, data.mutex);
 
   }
   al_unlock_mutex(data.mutex);
 
   al_lock_mutex(data.mutex);
   data.modi_X = true;
   data.ready  = false;
   al_unlock_mutex(data.mutex);
 
   thread_2 = al_create_thread(Func_Thread, &data);
   al_start_thread(thread_2);
 
   al_lock_mutex(data.mutex);
   while (!data.ready){
 
      al_wait_cond(data.cond, data.mutex);
 
   }
   al_unlock_mutex(data.mutex);
 
 
   while(1)
   {
      ALLEGRO_EVENT ev;
      al_wait_for_event(event_queue, &ev);
 
      if(ev.type == ALLEGRO_EVENT_TIMER) {
         redraw = true;
      }
      else if(ev.type == ALLEGRO_EVENT_DISPLAY_CLOSE) {
         break;
      }
      else if(ev.type == ALLEGRO_EVENT_MOUSE_BUTTON_DOWN) {
         break;
      }
      if(redraw && al_is_event_queue_empty(event_queue)) {
         redraw = false;
 
         al_lock_mutex(data.mutex);
         float X = data.posiX;
         float Y = data.posiY;
         al_unlock_mutex(data.mutex);
 
         al_draw_bitmap(bouncer, X, Y, 0);
 
         al_flip_display();
      }
   }
   al_destroy_thread(thread_1);
   al_destroy_thread(thread_2);
 
   al_destroy_bitmap(bouncer);
   al_destroy_timer(timer);
   al_destroy_display(display);
   al_destroy_event_queue(event_queue);
 
   return 0;
}