# HG changeset patch # User Michael Pavone # Date 1524884927 25200 # Node ID a051d8ee4528b2048bbe36414aad4b365b21598b # Parent 5efeca06d94280d7827185c4b182957924d32766 Only save config file if something has changed. Re-initialize audio and video with new settings if config has changed diff -r 5efeca06d942 -r a051d8ee4528 blastem.c --- a/blastem.c Tue Apr 24 20:31:18 2018 -0700 +++ b/blastem.c Fri Apr 27 20:08:47 2018 -0700 @@ -277,6 +277,11 @@ } } +void apply_updated_config(void) +{ + render_config_updated(); +} + static void on_drag_drop(const char *filename) { if (current_system->next_rom) { @@ -377,11 +382,6 @@ update_title(info.name); } -static void save_config(void) -{ - persist_config(config); -} - int main(int argc, char ** argv) { set_exe_str(argv[0]); @@ -617,8 +617,6 @@ } } - atexit(save_config); - #ifndef DISABLE_NUKLEAR if (use_nuklear) { blastem_nuklear_init(!menu); diff -r 5efeca06d942 -r a051d8ee4528 blastem.h --- a/blastem.h Tue Apr 24 20:31:18 2018 -0700 +++ b/blastem.h Fri Apr 27 20:08:47 2018 -0700 @@ -18,5 +18,6 @@ void reload_media(void); void lockon_media(char *lock_on_path); void init_system_with_media(char *path, system_type force_stype); +void apply_updated_config(void); #endif //BLASTEM_H_ diff -r 5efeca06d942 -r a051d8ee4528 nuklear_ui/blastem_nuklear.c --- a/nuklear_ui/blastem_nuklear.c Tue Apr 24 20:31:18 2018 -0700 +++ b/nuklear_ui/blastem_nuklear.c Fri Apr 27 20:08:47 2018 -0700 @@ -23,6 +23,7 @@ static uint32_t view_storage; static uint32_t num_prev; static struct nk_font *def_font; +static uint8_t config_dirty; static void push_view(view_fun new_view) { @@ -486,6 +487,7 @@ memcpy(path + prefix_len, name, suffix_len); path[prefix_len + suffix_len] = 0; + config_dirty = 1; config = tern_insert_path(config, path, (tern_val){.ptrval = strdup(set_binding)}, TVAL_PTR); free(path); free(name); @@ -598,6 +600,7 @@ nk_label(context, label, NK_TEXT_LEFT); uint8_t newval = nk_check_label(context, "", curval); if (newval != curval) { + config_dirty = 1; config = tern_insert_path(config, path, (tern_val){.ptrval = strdup(newval ? "on" : "off")}, TVAL_PTR); } } @@ -616,6 +619,7 @@ nk_edit_string(context, NK_EDIT_SIMPLE, buffer, &len, sizeof(buffer)-1, nk_filter_decimal); buffer[len] = 0; if (strcmp(buffer, curstr)) { + config_dirty = 1; config = tern_insert_path(config, path, (tern_val){.ptrval = strdup(buffer)}, TVAL_PTR); } } @@ -630,6 +634,7 @@ if (val != curval) { char buffer[12]; sprintf(buffer, "%d", val); + config_dirty = 1; config = tern_insert_path(config, path, (tern_val){.ptrval = strdup(buffer)}, TVAL_PTR); } } @@ -743,6 +748,7 @@ nk_label(context, label, NK_TEXT_LEFT); int32_t next = nk_combo(context, opt_display, num_options, current, 30, nk_vec2(300, 300)); if (next != current) { + config_dirty = 1; config = tern_insert_path(config, path, (tern_val){.ptrval = strdup(options[next])}, TVAL_PTR); } return next; @@ -804,6 +810,7 @@ uint32_t next_selected = nk_combo(context, (const char **)prog_names, num_progs, selected_prog, 30, nk_vec2(300, 300)); if (next_selected != selected_prog) { selected_prog = next_selected; + config_dirty = 1; config = tern_insert_path(config, "video\0fragment_shader\0", (tern_val){.ptrval = strdup(progs[next_selected].fragment)}, TVAL_PTR); config = tern_insert_path(config, "video\0vertex_shader\0", (tern_val){.ptrval = strdup(progs[next_selected].vertex)}, TVAL_PTR); } @@ -1024,6 +1031,11 @@ last = current; render_update_display(); } + if (config_dirty) { + apply_updated_config(); + persist_config(config); + config_dirty = 0; + } } static void handle_event(SDL_Event *event) { @@ -1035,7 +1047,7 @@ static void context_destroyed(void) { - nk_sdl_device_destroy(); + nk_sdl_shutdown(); } static uint32_t *controller_360_buf; @@ -1067,7 +1079,7 @@ static void context_created(void) { - nk_sdl_device_create(); + context = nk_sdl_init(render_get_window()); texture_init(); } diff -r 5efeca06d942 -r a051d8ee4528 render.h --- a/render.h Tue Apr 24 20:31:18 2018 -0700 +++ b/render.h Fri Apr 27 20:08:47 2018 -0700 @@ -124,6 +124,7 @@ void render_pause_source(audio_source *src); void render_resume_source(audio_source *src); void render_free_source(audio_source *src); +void render_config_updated(void); #endif //RENDER_H_ diff -r 5efeca06d942 -r a051d8ee4528 render_sdl.c --- a/render_sdl.c Tue Apr 24 20:31:18 2018 -0700 +++ b/render_sdl.c Fri Apr 27 20:08:47 2018 -0700 @@ -48,6 +48,7 @@ SDL_cond *cond; int16_t *front; int16_t *back; + double dt; uint64_t buffer_fraction; uint64_t buffer_inc; uint32_t buffer_pos; @@ -257,8 +258,8 @@ render_audio_adjust_clock(ret, master_clock, sample_divider); double lowpass_cutoff = get_lowpass_cutoff(config); double rc = (1.0 / lowpass_cutoff) / (2.0 * M_PI); - double dt = 1.0 / ((double)master_clock / (double)(sample_divider)); - double alpha = dt / (dt + rc); + ret->dt = 1.0 / ((double)master_clock / (double)(sample_divider)); + double alpha = ret->dt / (ret->dt + rc); ret->lowpass_alpha = (int32_t)(((double)0x10000) * alpha); ret->buffer_pos = 0; ret->buffer_fraction = 0; @@ -524,10 +525,9 @@ } #endif +static uint8_t texture_init; static void render_alloc_surfaces() { - static uint8_t texture_init; - if (texture_init) { return; } @@ -550,18 +550,26 @@ #endif } +static void free_surfaces(void) +{ + for (int i = 0; i < num_textures; i++) + { + if (sdl_textures[i]) { + SDL_DestroyTexture(sdl_textures[i]); + } + } + free(sdl_textures); + sdl_textures = NULL; + texture_init = 0; +} + static char * caption = NULL; static char * fps_caption = NULL; static void render_quit() { render_close_audio(); - for (int i = 0; i < num_textures; i++) - { - if (sdl_textures[i]) { - SDL_DestroyTexture(sdl_textures[i]); - } - } + free_surfaces(); } static float config_aspect() @@ -621,55 +629,317 @@ } } +static ui_render_fun on_context_destroyed, on_context_created; +void render_set_gl_context_handlers(ui_render_fun destroy, ui_render_fun create) +{ + on_context_destroyed = destroy; + on_context_created = create; +} + +static uint8_t scancode_map[SDL_NUM_SCANCODES] = { + [SDL_SCANCODE_A] = 0x1C, + [SDL_SCANCODE_B] = 0x32, + [SDL_SCANCODE_C] = 0x21, + [SDL_SCANCODE_D] = 0x23, + [SDL_SCANCODE_E] = 0x24, + [SDL_SCANCODE_F] = 0x2B, + [SDL_SCANCODE_G] = 0x34, + [SDL_SCANCODE_H] = 0x33, + [SDL_SCANCODE_I] = 0x43, + [SDL_SCANCODE_J] = 0x3B, + [SDL_SCANCODE_K] = 0x42, + [SDL_SCANCODE_L] = 0x4B, + [SDL_SCANCODE_M] = 0x3A, + [SDL_SCANCODE_N] = 0x31, + [SDL_SCANCODE_O] = 0x44, + [SDL_SCANCODE_P] = 0x4D, + [SDL_SCANCODE_Q] = 0x15, + [SDL_SCANCODE_R] = 0x2D, + [SDL_SCANCODE_S] = 0x1B, + [SDL_SCANCODE_T] = 0x2C, + [SDL_SCANCODE_U] = 0x3C, + [SDL_SCANCODE_V] = 0x2A, + [SDL_SCANCODE_W] = 0x1D, + [SDL_SCANCODE_X] = 0x22, + [SDL_SCANCODE_Y] = 0x35, + [SDL_SCANCODE_Z] = 0x1A, + [SDL_SCANCODE_1] = 0x16, + [SDL_SCANCODE_2] = 0x1E, + [SDL_SCANCODE_3] = 0x26, + [SDL_SCANCODE_4] = 0x25, + [SDL_SCANCODE_5] = 0x2E, + [SDL_SCANCODE_6] = 0x36, + [SDL_SCANCODE_7] = 0x3D, + [SDL_SCANCODE_8] = 0x3E, + [SDL_SCANCODE_9] = 0x46, + [SDL_SCANCODE_0] = 0x45, + [SDL_SCANCODE_RETURN] = 0x5A, + [SDL_SCANCODE_ESCAPE] = 0x76, + [SDL_SCANCODE_SPACE] = 0x29, + [SDL_SCANCODE_TAB] = 0x0D, + [SDL_SCANCODE_BACKSPACE] = 0x66, + [SDL_SCANCODE_MINUS] = 0x4E, + [SDL_SCANCODE_EQUALS] = 0x55, + [SDL_SCANCODE_LEFTBRACKET] = 0x54, + [SDL_SCANCODE_RIGHTBRACKET] = 0x5B, + [SDL_SCANCODE_BACKSLASH] = 0x5D, + [SDL_SCANCODE_SEMICOLON] = 0x4C, + [SDL_SCANCODE_APOSTROPHE] = 0x52, + [SDL_SCANCODE_GRAVE] = 0x0E, + [SDL_SCANCODE_COMMA] = 0x41, + [SDL_SCANCODE_PERIOD] = 0x49, + [SDL_SCANCODE_SLASH] = 0x4A, + [SDL_SCANCODE_CAPSLOCK] = 0x58, + [SDL_SCANCODE_F1] = 0x05, + [SDL_SCANCODE_F2] = 0x06, + [SDL_SCANCODE_F3] = 0x04, + [SDL_SCANCODE_F4] = 0x0C, + [SDL_SCANCODE_F5] = 0x03, + [SDL_SCANCODE_F6] = 0x0B, + [SDL_SCANCODE_F7] = 0x83, + [SDL_SCANCODE_F8] = 0x0A, + [SDL_SCANCODE_F9] = 0x01, + [SDL_SCANCODE_F10] = 0x09, + [SDL_SCANCODE_F11] = 0x78, + [SDL_SCANCODE_F12] = 0x07, + [SDL_SCANCODE_LCTRL] = 0x14, + [SDL_SCANCODE_LSHIFT] = 0x12, + [SDL_SCANCODE_LALT] = 0x11, + [SDL_SCANCODE_RCTRL] = 0x18, + [SDL_SCANCODE_RSHIFT] = 0x59, + [SDL_SCANCODE_RALT] = 0x17, + [SDL_SCANCODE_INSERT] = 0x81, + [SDL_SCANCODE_PAUSE] = 0x82, + [SDL_SCANCODE_PRINTSCREEN] = 0x84, + [SDL_SCANCODE_SCROLLLOCK] = 0x7E, + [SDL_SCANCODE_DELETE] = 0x85, + [SDL_SCANCODE_LEFT] = 0x86, + [SDL_SCANCODE_HOME] = 0x87, + [SDL_SCANCODE_END] = 0x88, + [SDL_SCANCODE_UP] = 0x89, + [SDL_SCANCODE_DOWN] = 0x8A, + [SDL_SCANCODE_PAGEUP] = 0x8B, + [SDL_SCANCODE_PAGEDOWN] = 0x8C, + [SDL_SCANCODE_RIGHT] = 0x8D, + [SDL_SCANCODE_NUMLOCKCLEAR] = 0x77, + [SDL_SCANCODE_KP_DIVIDE] = 0x80, + [SDL_SCANCODE_KP_MULTIPLY] = 0x7C, + [SDL_SCANCODE_KP_MINUS] = 0x7B, + [SDL_SCANCODE_KP_PLUS] = 0x79, + [SDL_SCANCODE_KP_ENTER] = 0x19, + [SDL_SCANCODE_KP_1] = 0x69, + [SDL_SCANCODE_KP_2] = 0x72, + [SDL_SCANCODE_KP_3] = 0x7A, + [SDL_SCANCODE_KP_4] = 0x6B, + [SDL_SCANCODE_KP_5] = 0x73, + [SDL_SCANCODE_KP_6] = 0x74, + [SDL_SCANCODE_KP_7] = 0x6C, + [SDL_SCANCODE_KP_8] = 0x75, + [SDL_SCANCODE_KP_9] = 0x7D, + [SDL_SCANCODE_KP_0] = 0x70, + [SDL_SCANCODE_KP_PERIOD] = 0x71, +}; + +static drop_handler drag_drop_handler; +void render_set_drag_drop_handler(drop_handler handler) +{ + drag_drop_handler = handler; +} + +static event_handler custom_event_handler; +void render_set_event_handler(event_handler handler) +{ + custom_event_handler = handler; +} + +static int find_joystick_index(SDL_JoystickID instanceID) +{ + for (int i = 0; i < MAX_JOYSTICKS; i++) { + if (joysticks[i] && SDL_JoystickInstanceID(joysticks[i]) == instanceID) { + return i; + } + } + return -1; +} + +static int lowest_unused_joystick_index() +{ + for (int i = 0; i < MAX_JOYSTICKS; i++) { + if (!joysticks[i]) { + return i; + } + } + return -1; +} + static uint32_t overscan_top[NUM_VID_STD] = {2, 21}; static uint32_t overscan_bot[NUM_VID_STD] = {1, 17}; static uint32_t overscan_left[NUM_VID_STD] = {13, 13}; static uint32_t overscan_right[NUM_VID_STD] = {14, 14}; static vid_std video_standard = VID_NTSC; + +static int32_t handle_event(SDL_Event *event) +{ + if (custom_event_handler) { + custom_event_handler(event); + } + switch (event->type) { + case SDL_KEYDOWN: + handle_keydown(event->key.keysym.sym, scancode_map[event->key.keysym.scancode]); + break; + case SDL_KEYUP: + handle_keyup(event->key.keysym.sym, scancode_map[event->key.keysym.scancode]); + break; + case SDL_JOYBUTTONDOWN: + handle_joydown(find_joystick_index(event->jbutton.which), event->jbutton.button); + break; + case SDL_JOYBUTTONUP: + handle_joyup(find_joystick_index(event->jbutton.which), event->jbutton.button); + break; + case SDL_JOYHATMOTION: + handle_joy_dpad(find_joystick_index(event->jbutton.which), event->jhat.hat, event->jhat.value); + break; + case SDL_JOYAXISMOTION: + handle_joy_axis(find_joystick_index(event->jaxis.which), event->jaxis.axis, event->jaxis.value); + break; + case SDL_JOYDEVICEADDED: + if (event->jdevice.which < MAX_JOYSTICKS) { + int index = lowest_unused_joystick_index(); + if (index >= 0) { + SDL_Joystick * joy = joysticks[index] = SDL_JoystickOpen(event->jdevice.which); + joystick_sdl_index[index] = event->jdevice.which; + if (joy) { + printf("Joystick %d added: %s\n", index, SDL_JoystickName(joy)); + printf("\tNum Axes: %d\n\tNum Buttons: %d\n\tNum Hats: %d\n", SDL_JoystickNumAxes(joy), SDL_JoystickNumButtons(joy), SDL_JoystickNumHats(joy)); + handle_joy_added(index); + } + } + } + break; + case SDL_JOYDEVICEREMOVED: { + int index = find_joystick_index(event->jdevice.which); + if (index >= 0) { + SDL_JoystickClose(joysticks[index]); + joysticks[index] = NULL; + printf("Joystick %d removed\n", index); + } else { + printf("Failed to find removed joystick with instance ID: %d\n", index); + } + break; + } + case SDL_MOUSEMOTION: + handle_mouse_moved(event->motion.which, event->motion.x, event->motion.y + overscan_top[video_standard], event->motion.xrel, event->motion.yrel); + break; + case SDL_MOUSEBUTTONDOWN: + handle_mousedown(event->button.which, event->button.button); + break; + case SDL_MOUSEBUTTONUP: + handle_mouseup(event->button.which, event->button.button); + break; + case SDL_WINDOWEVENT: + switch (event->window.event) + { + case SDL_WINDOWEVENT_SIZE_CHANGED: + main_width = event->window.data1; + main_height = event->window.data2; + update_aspect(); +#ifndef DISABLE_OPENGL + if (render_gl) { + if (on_context_destroyed) { + on_context_destroyed(); + } + SDL_GL_DeleteContext(main_context); + main_context = SDL_GL_CreateContext(main_window); + gl_setup(); + if (on_context_created) { + on_context_created(); + } + } +#endif + break; + } + break; + case SDL_DROPFILE: + if (drag_drop_handler) { + drag_drop_handler(event->drop.file); + } + SDL_free(event->drop.file); + break; + case SDL_QUIT: + puts(""); + exit(0); + } + return 0; +} + +static void drain_events() +{ + SDL_Event event; + while(SDL_PollEvent(&event)) + { + handle_event(&event); + } +} + static char *vid_std_names[NUM_VID_STD] = {"ntsc", "pal"}; static int display_hz; static int source_hz; static int source_frame; static int source_frame_count; static int frame_repeat[60]; -void render_init(int width, int height, char * title, uint8_t fullscreen) + +static void init_audio() { - if (SDL_Init(SDL_INIT_VIDEO | SDL_INIT_AUDIO | SDL_INIT_JOYSTICK | SDL_INIT_GAMECONTROLLER) < 0) { - fatal_error("Unable to init SDL: %s\n", SDL_GetError()); + SDL_AudioSpec desired, actual; + char * rate_str = tern_find_path(config, "audio\0rate\0", TVAL_PTR).ptrval; + int rate = rate_str ? atoi(rate_str) : 0; + if (!rate) { + rate = 48000; + } + desired.freq = rate; + desired.format = AUDIO_S16SYS; + desired.channels = 2; + char * samples_str = tern_find_path(config, "audio\0buffer\0", TVAL_PTR).ptrval; + int samples = samples_str ? atoi(samples_str) : 0; + if (!samples) { + samples = 512; + } + printf("config says: %d\n", samples); + desired.samples = samples*2; + desired.callback = sync_to_audio ? audio_callback : audio_callback_drc; + desired.userdata = NULL; + + if (SDL_OpenAudio(&desired, &actual) < 0) { + fatal_error("Unable to open SDL audio: %s\n", SDL_GetError()); } - atexit(SDL_Quit); - if (height <= 0) { - float aspect = config_aspect() > 0.0f ? config_aspect() : 4.0f/3.0f; - height = ((float)width / aspect) + 0.5f; + buffer_samples = actual.samples; + sample_rate = actual.freq; + printf("Initialized audio at frequency %d with a %d sample buffer, ", actual.freq, actual.samples); + if (actual.format == AUDIO_S16SYS) { + puts("signed 16-bit int format"); + mix = mix_s16; + } else if (actual.format == AUDIO_F32SYS) { + puts("32-bit float format"); + mix = mix_f32; + } else { + printf("unsupported format %X\n", actual.format); + warning("Unsupported audio sample format: %X\n", actual.format); + mix = mix_null; } - printf("width: %d, height: %d\n", width, height); - windowed_width = width; - windowed_height = height; - +} + +void window_setup(void) +{ uint32_t flags = SDL_WINDOW_RESIZABLE; - - SDL_DisplayMode mode; - //TODO: Explicit multiple monitor support - SDL_GetCurrentDisplayMode(0, &mode); - display_hz = mode.refresh_rate; - - if (fullscreen) { + if (is_fullscreen) { flags |= SDL_WINDOW_FULLSCREEN_DESKTOP; - //the SDL2 migration guide suggests setting width and height to 0 when using SDL_WINDOW_FULLSCREEN_DESKTOP - //but that doesn't seem to work right when using OpenGL, at least on Linux anyway - width = mode.w; - height = mode.h; } - main_width = width; - main_height = height; - is_fullscreen = fullscreen; tern_val def = {.ptrval = "video"}; char *sync_src = tern_find_path_default(config, "system\0sync_source\0", def, TVAL_PTR).ptrval; sync_to_audio = !strcmp(sync_src, "audio"); - - render_gl = 0; - char *vsync; + + const char *vsync; if (sync_to_audio) { def.ptrval = "off"; vsync = tern_find_path_default(config, "video\0vsync\0", def, TVAL_PTR).ptrval; @@ -703,7 +973,7 @@ } } } - + #ifndef DISABLE_OPENGL char *gl_enabled_str = tern_find_path_default(config, "video\0gl\0", def, TVAL_PTR).ptrval; uint8_t gl_enabled = strcmp(gl_enabled_str, "off") != 0; @@ -717,7 +987,7 @@ SDL_GL_SetAttribute(SDL_GL_DOUBLEBUFFER, 1); } #endif - main_window = SDL_CreateWindow(title, SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED, width, height, flags); + main_window = SDL_CreateWindow(caption, SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED, main_width, main_height, flags); if (!main_window) { fatal_error("Unable to create SDL window: %s\n", SDL_GetError()); } @@ -761,8 +1031,8 @@ fatal_error("unable to create SDL renderer: %s\n", SDL_GetError()); } main_clip.x = main_clip.y = 0; - main_clip.w = width; - main_clip.h = height; + main_clip.w = main_width; + main_clip.h = main_height; #ifndef DISABLE_OPENGL } #endif @@ -773,48 +1043,45 @@ render_alloc_surfaces(); def.ptrval = "off"; scanlines = !strcmp(tern_find_path_default(config, "video\0scanlines\0", def, TVAL_PTR).ptrval, "on"); +} +void render_init(int width, int height, char * title, uint8_t fullscreen) +{ + if (SDL_Init(SDL_INIT_VIDEO | SDL_INIT_AUDIO | SDL_INIT_JOYSTICK | SDL_INIT_GAMECONTROLLER) < 0) { + fatal_error("Unable to init SDL: %s\n", SDL_GetError()); + } + atexit(SDL_Quit); + if (height <= 0) { + float aspect = config_aspect() > 0.0f ? config_aspect() : 4.0f/3.0f; + height = ((float)width / aspect) + 0.5f; + } + printf("width: %d, height: %d\n", width, height); + windowed_width = width; + windowed_height = height; + + SDL_DisplayMode mode; + //TODO: Explicit multiple monitor support + SDL_GetCurrentDisplayMode(0, &mode); + display_hz = mode.refresh_rate; + + if (fullscreen) { + //the SDL2 migration guide suggests setting width and height to 0 when using SDL_WINDOW_FULLSCREEN_DESKTOP + //but that doesn't seem to work right when using OpenGL, at least on Linux anyway + width = mode.w; + height = mode.h; + } + main_width = width; + main_height = height; + is_fullscreen = fullscreen; + caption = title; + + window_setup(); audio_mutex = SDL_CreateMutex(); audio_ready = SDL_CreateCond(); - - SDL_AudioSpec desired, actual; - char * rate_str = tern_find_path(config, "audio\0rate\0", TVAL_PTR).ptrval; - int rate = rate_str ? atoi(rate_str) : 0; - if (!rate) { - rate = 48000; - } - desired.freq = rate; - desired.format = AUDIO_S16SYS; - desired.channels = 2; - char * samples_str = tern_find_path(config, "audio\0buffer\0", TVAL_PTR).ptrval; - int samples = samples_str ? atoi(samples_str) : 0; - if (!samples) { - samples = 512; - } - printf("config says: %d\n", samples); - desired.samples = samples*2; - desired.callback = sync_to_audio ? audio_callback : audio_callback_drc; - desired.userdata = NULL; - - if (SDL_OpenAudio(&desired, &actual) < 0) { - fatal_error("Unable to open SDL audio: %s\n", SDL_GetError()); - } - buffer_samples = actual.samples; - sample_rate = actual.freq; - printf("Initialized audio at frequency %d with a %d sample buffer, ", actual.freq, actual.samples); - if (actual.format == AUDIO_S16SYS) { - puts("signed 16-bit int format"); - mix = mix_s16; - } else if (actual.format == AUDIO_F32SYS) { - puts("32-bit float format"); - mix = mix_f32; - } else { - printf("unsupported format %X\n", actual.format); - warning("Unsupported audio sample format: %X\n", actual.format); - mix = mix_null; - } + + init_audio(); uint32_t db_size; char *db_data = read_bundled_file("gamecontrollerdb.txt", &db_size); @@ -830,6 +1097,78 @@ atexit(render_quit); } +#include +static int in_toggle; +void render_config_updated(void) +{ + uint8_t old_sync_to_audio = sync_to_audio; + + free_surfaces(); +#ifndef DISABLE_OPENGL + if (render_gl) { + if (on_context_destroyed) { + on_context_destroyed(); + } + SDL_GL_DeleteContext(main_context); + } else { +#endif + SDL_DestroyRenderer(main_renderer); +#ifndef DISABLE_OPENGL + } +#endif + in_toggle = 1; + SDL_DestroyWindow(main_window); + drain_events(); + + char *config_width = tern_find_path(config, "video\0width\0", TVAL_PTR).ptrval; + if (config_width) { + windowed_width = atoi(config_width); + } + char *config_height = tern_find_path(config, "video\0height\0", TVAL_PTR).ptrval; + if (config_height) { + windowed_height = atoi(config_height); + } else { + float aspect = config_aspect() > 0.0f ? config_aspect() : 4.0f/3.0f; + windowed_height = ((float)windowed_width / aspect) + 0.5f; + } + char *config_fullscreen = tern_find_path(config, "video\0fullscreen\0", TVAL_PTR).ptrval; + is_fullscreen = config_fullscreen && !strcmp("on", config_fullscreen); + if (is_fullscreen) { + SDL_DisplayMode mode; + //TODO: Multiple monitor support + SDL_GetCurrentDisplayMode(0, &mode); + main_width = mode.w; + main_height = mode.h; + } else { + main_width = windowed_width; + main_height = windowed_height; + } + + window_setup(); + update_aspect(); +#ifndef DISABLE_OPENGL + //need to check render_gl again after window_setup as render option could have changed + if (render_gl && on_context_created) { + on_context_created(); + } +#endif + + SDL_CloseAudio(); + init_audio(); + + double lowpass_cutoff = get_lowpass_cutoff(config); + double rc = (1.0 / lowpass_cutoff) / (2.0 * M_PI); + lock_audio(); + for (uint8_t i = 0; i < num_audio_sources; i++) + { + double alpha = audio_sources[i]->dt / (audio_sources[i]->dt + rc); + int32_t lowpass_alpha = (int32_t)(((double)0x10000) * alpha); + audio_sources[i]->lowpass_alpha = lowpass_alpha; + } + unlock_audio(); + drain_events(); + in_toggle = 0; +} SDL_Window *render_get_window(void) { @@ -1211,26 +1550,6 @@ } } -static int find_joystick_index(SDL_JoystickID instanceID) -{ - for (int i = 0; i < MAX_JOYSTICKS; i++) { - if (joysticks[i] && SDL_JoystickInstanceID(joysticks[i]) == instanceID) { - return i; - } - } - return -1; -} - -static int lowest_unused_joystick_index() -{ - for (int i = 0; i < MAX_JOYSTICKS; i++) { - if (!joysticks[i]) { - return i; - } - } - return -1; -} - int32_t render_translate_input_name(int32_t controller, char *name, uint8_t is_axis) { static tern_node *button_lookup, *axis_lookup; @@ -1318,232 +1637,6 @@ return input & 0xFFFFFFF; } -static uint8_t scancode_map[SDL_NUM_SCANCODES] = { - [SDL_SCANCODE_A] = 0x1C, - [SDL_SCANCODE_B] = 0x32, - [SDL_SCANCODE_C] = 0x21, - [SDL_SCANCODE_D] = 0x23, - [SDL_SCANCODE_E] = 0x24, - [SDL_SCANCODE_F] = 0x2B, - [SDL_SCANCODE_G] = 0x34, - [SDL_SCANCODE_H] = 0x33, - [SDL_SCANCODE_I] = 0x43, - [SDL_SCANCODE_J] = 0x3B, - [SDL_SCANCODE_K] = 0x42, - [SDL_SCANCODE_L] = 0x4B, - [SDL_SCANCODE_M] = 0x3A, - [SDL_SCANCODE_N] = 0x31, - [SDL_SCANCODE_O] = 0x44, - [SDL_SCANCODE_P] = 0x4D, - [SDL_SCANCODE_Q] = 0x15, - [SDL_SCANCODE_R] = 0x2D, - [SDL_SCANCODE_S] = 0x1B, - [SDL_SCANCODE_T] = 0x2C, - [SDL_SCANCODE_U] = 0x3C, - [SDL_SCANCODE_V] = 0x2A, - [SDL_SCANCODE_W] = 0x1D, - [SDL_SCANCODE_X] = 0x22, - [SDL_SCANCODE_Y] = 0x35, - [SDL_SCANCODE_Z] = 0x1A, - [SDL_SCANCODE_1] = 0x16, - [SDL_SCANCODE_2] = 0x1E, - [SDL_SCANCODE_3] = 0x26, - [SDL_SCANCODE_4] = 0x25, - [SDL_SCANCODE_5] = 0x2E, - [SDL_SCANCODE_6] = 0x36, - [SDL_SCANCODE_7] = 0x3D, - [SDL_SCANCODE_8] = 0x3E, - [SDL_SCANCODE_9] = 0x46, - [SDL_SCANCODE_0] = 0x45, - [SDL_SCANCODE_RETURN] = 0x5A, - [SDL_SCANCODE_ESCAPE] = 0x76, - [SDL_SCANCODE_SPACE] = 0x29, - [SDL_SCANCODE_TAB] = 0x0D, - [SDL_SCANCODE_BACKSPACE] = 0x66, - [SDL_SCANCODE_MINUS] = 0x4E, - [SDL_SCANCODE_EQUALS] = 0x55, - [SDL_SCANCODE_LEFTBRACKET] = 0x54, - [SDL_SCANCODE_RIGHTBRACKET] = 0x5B, - [SDL_SCANCODE_BACKSLASH] = 0x5D, - [SDL_SCANCODE_SEMICOLON] = 0x4C, - [SDL_SCANCODE_APOSTROPHE] = 0x52, - [SDL_SCANCODE_GRAVE] = 0x0E, - [SDL_SCANCODE_COMMA] = 0x41, - [SDL_SCANCODE_PERIOD] = 0x49, - [SDL_SCANCODE_SLASH] = 0x4A, - [SDL_SCANCODE_CAPSLOCK] = 0x58, - [SDL_SCANCODE_F1] = 0x05, - [SDL_SCANCODE_F2] = 0x06, - [SDL_SCANCODE_F3] = 0x04, - [SDL_SCANCODE_F4] = 0x0C, - [SDL_SCANCODE_F5] = 0x03, - [SDL_SCANCODE_F6] = 0x0B, - [SDL_SCANCODE_F7] = 0x83, - [SDL_SCANCODE_F8] = 0x0A, - [SDL_SCANCODE_F9] = 0x01, - [SDL_SCANCODE_F10] = 0x09, - [SDL_SCANCODE_F11] = 0x78, - [SDL_SCANCODE_F12] = 0x07, - [SDL_SCANCODE_LCTRL] = 0x14, - [SDL_SCANCODE_LSHIFT] = 0x12, - [SDL_SCANCODE_LALT] = 0x11, - [SDL_SCANCODE_RCTRL] = 0x18, - [SDL_SCANCODE_RSHIFT] = 0x59, - [SDL_SCANCODE_RALT] = 0x17, - [SDL_SCANCODE_INSERT] = 0x81, - [SDL_SCANCODE_PAUSE] = 0x82, - [SDL_SCANCODE_PRINTSCREEN] = 0x84, - [SDL_SCANCODE_SCROLLLOCK] = 0x7E, - [SDL_SCANCODE_DELETE] = 0x85, - [SDL_SCANCODE_LEFT] = 0x86, - [SDL_SCANCODE_HOME] = 0x87, - [SDL_SCANCODE_END] = 0x88, - [SDL_SCANCODE_UP] = 0x89, - [SDL_SCANCODE_DOWN] = 0x8A, - [SDL_SCANCODE_PAGEUP] = 0x8B, - [SDL_SCANCODE_PAGEDOWN] = 0x8C, - [SDL_SCANCODE_RIGHT] = 0x8D, - [SDL_SCANCODE_NUMLOCKCLEAR] = 0x77, - [SDL_SCANCODE_KP_DIVIDE] = 0x80, - [SDL_SCANCODE_KP_MULTIPLY] = 0x7C, - [SDL_SCANCODE_KP_MINUS] = 0x7B, - [SDL_SCANCODE_KP_PLUS] = 0x79, - [SDL_SCANCODE_KP_ENTER] = 0x19, - [SDL_SCANCODE_KP_1] = 0x69, - [SDL_SCANCODE_KP_2] = 0x72, - [SDL_SCANCODE_KP_3] = 0x7A, - [SDL_SCANCODE_KP_4] = 0x6B, - [SDL_SCANCODE_KP_5] = 0x73, - [SDL_SCANCODE_KP_6] = 0x74, - [SDL_SCANCODE_KP_7] = 0x6C, - [SDL_SCANCODE_KP_8] = 0x75, - [SDL_SCANCODE_KP_9] = 0x7D, - [SDL_SCANCODE_KP_0] = 0x70, - [SDL_SCANCODE_KP_PERIOD] = 0x71, -}; - -static drop_handler drag_drop_handler; -void render_set_drag_drop_handler(drop_handler handler) -{ - drag_drop_handler = handler; -} - -static ui_render_fun on_context_destroyed, on_context_created; -void render_set_gl_context_handlers(ui_render_fun destroy, ui_render_fun create) -{ - on_context_destroyed = destroy; - on_context_created = create; -} - -static event_handler custom_event_handler; -void render_set_event_handler(event_handler handler) -{ - custom_event_handler = handler; -} - -static int32_t handle_event(SDL_Event *event) -{ - if (custom_event_handler) { - custom_event_handler(event); - } - switch (event->type) { - case SDL_KEYDOWN: - handle_keydown(event->key.keysym.sym, scancode_map[event->key.keysym.scancode]); - break; - case SDL_KEYUP: - handle_keyup(event->key.keysym.sym, scancode_map[event->key.keysym.scancode]); - break; - case SDL_JOYBUTTONDOWN: - handle_joydown(find_joystick_index(event->jbutton.which), event->jbutton.button); - break; - case SDL_JOYBUTTONUP: - handle_joyup(find_joystick_index(event->jbutton.which), event->jbutton.button); - break; - case SDL_JOYHATMOTION: - handle_joy_dpad(find_joystick_index(event->jbutton.which), event->jhat.hat, event->jhat.value); - break; - case SDL_JOYAXISMOTION: - handle_joy_axis(find_joystick_index(event->jaxis.which), event->jaxis.axis, event->jaxis.value); - break; - case SDL_JOYDEVICEADDED: - if (event->jdevice.which < MAX_JOYSTICKS) { - int index = lowest_unused_joystick_index(); - if (index >= 0) { - SDL_Joystick * joy = joysticks[index] = SDL_JoystickOpen(event->jdevice.which); - joystick_sdl_index[index] = event->jdevice.which; - if (joy) { - printf("Joystick %d added: %s\n", index, SDL_JoystickName(joy)); - printf("\tNum Axes: %d\n\tNum Buttons: %d\n\tNum Hats: %d\n", SDL_JoystickNumAxes(joy), SDL_JoystickNumButtons(joy), SDL_JoystickNumHats(joy)); - handle_joy_added(index); - } - } - } - break; - case SDL_JOYDEVICEREMOVED: { - int index = find_joystick_index(event->jdevice.which); - if (index >= 0) { - SDL_JoystickClose(joysticks[index]); - joysticks[index] = NULL; - printf("Joystick %d removed\n", index); - } else { - printf("Failed to find removed joystick with instance ID: %d\n", index); - } - break; - } - case SDL_MOUSEMOTION: - handle_mouse_moved(event->motion.which, event->motion.x, event->motion.y + overscan_top[video_standard], event->motion.xrel, event->motion.yrel); - break; - case SDL_MOUSEBUTTONDOWN: - handle_mousedown(event->button.which, event->button.button); - break; - case SDL_MOUSEBUTTONUP: - handle_mouseup(event->button.which, event->button.button); - break; - case SDL_WINDOWEVENT: - switch (event->window.event) - { - case SDL_WINDOWEVENT_SIZE_CHANGED: - main_width = event->window.data1; - main_height = event->window.data2; - update_aspect(); -#ifndef DISABLE_OPENGL - if (render_gl) { - if (on_context_destroyed) { - on_context_destroyed(); - } - SDL_GL_DeleteContext(main_context); - main_context = SDL_GL_CreateContext(main_window); - gl_setup(); - if (on_context_created) { - on_context_created(); - } - } -#endif - break; - } - break; - case SDL_DROPFILE: - if (drag_drop_handler) { - drag_drop_handler(event->drop.file); - } - SDL_free(event->drop.file); - break; - case SDL_QUIT: - puts(""); - exit(0); - } - return 0; -} - -static void drain_events() -{ - SDL_Event event; - while(SDL_PollEvent(&event)) - { - handle_event(&event); - } -} - void process_events() { if (events_processed > MAX_EVENT_POLL_PER_FRAME) { @@ -1556,7 +1649,6 @@ #define TOGGLE_MIN_DELAY 250 void render_toggle_fullscreen() { - static int in_toggle; //protect against event processing causing us to attempt to toggle while still toggling if (in_toggle) { return;