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; }
/* 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); }
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; }
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; }
/* 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; }
/* [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; }
/* 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); }
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; }
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; }
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; }
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; }
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; }
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; }
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; }