summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--blastem.c12
-rw-r--r--blastem.h1
-rw-r--r--nuklear_ui/blastem_nuklear.c16
-rw-r--r--render.h1
-rwxr-xr-xrender_sdl.c742
5 files changed, 438 insertions, 334 deletions
diff --git a/blastem.c b/blastem.c
index a5fbcfe..48172b2 100644
--- a/blastem.c
+++ b/blastem.c
@@ -277,6 +277,11 @@ void setup_saves(system_media *media, rom_info *info, system_header *context)
}
}
+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 @@ void init_system_with_media(char *path, system_type force_stype)
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 @@ int main(int argc, char ** argv)
}
}
- atexit(save_config);
-
#ifndef DISABLE_NUKLEAR
if (use_nuklear) {
blastem_nuklear_init(!menu);
diff --git a/blastem.h b/blastem.h
index 00ea7b1..597d2c2 100644
--- a/blastem.h
+++ b/blastem.h
@@ -18,5 +18,6 @@ extern uint8_t use_native_states;
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 --git a/nuklear_ui/blastem_nuklear.c b/nuklear_ui/blastem_nuklear.c
index c7b1e70..91b454e 100644
--- a/nuklear_ui/blastem_nuklear.c
+++ b/nuklear_ui/blastem_nuklear.c
@@ -23,6 +23,7 @@ static view_fun *previous_views;
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 @@ void view_key_bindings(struct nk_context *context)
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 @@ void settings_toggle(struct nk_context *context, char *label, char *path, uint8_
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 @@ void settings_int_input(struct nk_context *context, char *label, char *path, cha
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 @@ void settings_int_property(struct nk_context *context, char *label, char *name,
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 @@ int32_t settings_dropdown_ex(struct nk_context *context, char *label, const char
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 @@ void view_video_settings(struct nk_context *context)
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 @@ void ui_idle_loop(void)
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 handle_event(SDL_Event *event)
static void context_destroyed(void)
{
- nk_sdl_device_destroy();
+ nk_sdl_shutdown();
}
static uint32_t *controller_360_buf;
@@ -1067,7 +1079,7 @@ static void texture_init(void)
static void context_created(void)
{
- nk_sdl_device_create();
+ context = nk_sdl_init(render_get_window());
texture_init();
}
diff --git a/render.h b/render.h
index de788f1..307e0ba 100644
--- a/render.h
+++ b/render.h
@@ -124,6 +124,7 @@ void render_put_stereo_sample(audio_source *src, int16_t left, int16_t right);
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 --git a/render_sdl.c b/render_sdl.c
index 629b6bb..3406675 100755
--- a/render_sdl.c
+++ b/render_sdl.c
@@ -48,6 +48,7 @@ struct audio_source {
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 @@ audio_source *render_audio_source(uint64_t master_clock, uint64_t sample_divider
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 @@ static void gl_setup()
}
#endif
+static uint8_t texture_init;
static void render_alloc_surfaces()
{
- static uint8_t texture_init;
-
if (texture_init) {
return;
}
@@ -550,18 +550,26 @@ static void render_alloc_surfaces()
#endif
}
-static char * caption = NULL;
-static char * fps_caption = NULL;
-
-static void render_quit()
+static void free_surfaces(void)
{
- render_close_audio();
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();
+ free_surfaces();
}
static float config_aspect()
@@ -621,55 +629,317 @@ static void update_aspect()
}
}
+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;
-
- 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) {
+void window_setup(void)
+{
+ uint32_t flags = SDL_WINDOW_RESIZABLE;
+ 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 @@ void render_init(int width, int height, char * title, uint8_t fullscreen)
}
}
}
-
+
#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 @@ void render_init(int width, int height, char * title, uint8_t fullscreen)
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 @@ void render_init(int width, int height, char * title, uint8_t fullscreen)
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 @@ void render_init(int width, int height, char * title, uint8_t fullscreen)
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 @@ void render_init(int width, int height, char * title, uint8_t fullscreen)
atexit(render_quit);
}
+#include<unistd.h>
+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 @@ void render_wait_quit(vdp_context * context)
}
}
-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 @@ int32_t render_axis_part(int32_t input)
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 @@ void process_events()
#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;