# HG changeset patch # User Michael Pavone # Date 1485420902 28800 # Node ID a6ae693974e01cff2a4c2c5165b094684cade440 # Parent aee2177a16306f937a9e3399eae71863b7908a02 Allow toggling full screen mode at runtime. Allow resizing the window in windowed mode. Allow specifying the aspect ratio in the config file. diff -r aee2177a1630 -r a6ae693974e0 default.cfg --- a/default.cfg Wed Jan 25 09:41:12 2017 -0800 +++ b/default.cfg Thu Jan 26 00:55:02 2017 -0800 @@ -30,6 +30,7 @@ 7 ui.set_speed.7 = ui.next_speed - ui.prev_speed + f11 ui.toggle_fullscreen } pads { 0 { @@ -108,6 +109,8 @@ } video { + #special value "stretch" will cause aspect to match window aspect ratio + aspect 4:3 width 640 vertex_shader default.v.glsl fragment_shader default.f.glsl diff -r aee2177a1630 -r a6ae693974e0 io.c --- a/io.c Wed Jan 25 09:41:12 2017 -0800 +++ b/io.c Thu Jan 26 00:55:02 2017 -0800 @@ -70,6 +70,7 @@ UI_NEXT_SPEED, UI_PREV_SPEED, UI_RELEASE_MOUSE, + UI_TOGGLE_FULLSCREEN, UI_EXIT } ui_action; @@ -415,6 +416,9 @@ render_relative_mouse(0); } break; + case UI_TOGGLE_FULLSCREEN: + render_toggle_fullscreen(); + break; case UI_EXIT: current_system->request_exit(current_system); break; @@ -560,6 +564,8 @@ *ui_out = UI_PREV_SPEED; } else if(!strcmp(target + 3, "release_mouse")) { *ui_out = UI_RELEASE_MOUSE; + } else if (!strcmp(target + 3, "toggle_fullscreen")) { + *ui_out = UI_TOGGLE_FULLSCREEN; } else if(!strcmp(target + 3, "exit")) { *ui_out = UI_EXIT; } else { diff -r aee2177a1630 -r a6ae693974e0 render.h --- a/render.h Wed Jan 25 09:41:12 2017 -0800 +++ b/render.h Thu Jan 26 00:55:02 2017 -0800 @@ -74,6 +74,7 @@ void render_framebuffer_updated(uint8_t which, int width); void render_init(int width, int height, char * title, uint8_t fullscreen); void render_set_video_standard(vid_std std); +void render_toggle_fullscreen(); void render_update_caption(char *title); void render_wait_quit(vdp_context * context); void render_wait_psg(psg_context * context); diff -r aee2177a1630 -r a6ae693974e0 render_sdl.c --- a/render_sdl.c Wed Jan 25 09:41:12 2017 -0800 +++ b/render_sdl.c Thu Jan 26 00:55:02 2017 -0800 @@ -26,7 +26,7 @@ static SDL_Rect main_clip; static SDL_GLContext *main_context; -static int main_width, main_height, is_fullscreen; +static int main_width, main_height, windowed_width, windowed_height, is_fullscreen; static uint8_t render_gl = 1; static uint8_t scanlines = 0; @@ -136,13 +136,15 @@ #ifndef DISABLE_OPENGL static GLuint textures[3], buffers[2], vshader, fshader, program, un_textures[2], un_width, un_height, at_pos; -static GLfloat vertex_data[] = { +static GLfloat vertex_data_default[] = { -1.0f, -1.0f, 1.0f, -1.0f, -1.0f, 1.0f, 1.0f, 1.0f }; +static GLfloat vertex_data[8]; + static const GLushort element_data[] = {0, 1, 2, 3}; static GLuint load_shader(char * fname, GLenum shader_type) @@ -189,6 +191,52 @@ #endif static uint32_t texture_buf[512 * 513]; +#ifndef DISABLE_OPENGL +static void gl_setup() +{ + glGenTextures(3, textures); + for (int i = 0; i < 3; i++) + { + glBindTexture(GL_TEXTURE_2D, textures[i]); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); + if (i < 2) { + //TODO: Fixme for PAL + invalid display mode + glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, 512, 512, 0, GL_BGRA, GL_UNSIGNED_BYTE, texture_buf); + } else { + uint32_t blank = 255 << 24; + glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, 1, 1, 0, GL_BGRA, GL_UNSIGNED_BYTE, &blank); + } + } + glGenBuffers(2, buffers); + glBindBuffer(GL_ARRAY_BUFFER, buffers[0]); + glBufferData(GL_ARRAY_BUFFER, sizeof(vertex_data), vertex_data, GL_STATIC_DRAW); + glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, buffers[1]); + glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(element_data), element_data, GL_STATIC_DRAW); + tern_val def = {.ptrval = "default.v.glsl"}; + vshader = load_shader(tern_find_path_default(config, "video\0vertex_shader\0", def).ptrval, GL_VERTEX_SHADER); + def.ptrval = "default.f.glsl"; + fshader = load_shader(tern_find_path_default(config, "video\0fragment_shader\0", def).ptrval, GL_FRAGMENT_SHADER); + program = glCreateProgram(); + glAttachShader(program, vshader); + glAttachShader(program, fshader); + glLinkProgram(program); + GLint link_status; + glGetProgramiv(program, GL_LINK_STATUS, &link_status); + if (!link_status) { + fputs("Failed to link shader program\n", stderr); + exit(1); + } + un_textures[0] = glGetUniformLocation(program, "textures[0]"); + un_textures[1] = glGetUniformLocation(program, "textures[1]"); + un_width = glGetUniformLocation(program, "width"); + un_height = glGetUniformLocation(program, "height"); + at_pos = glGetAttribLocation(program, "pos"); +} +#endif + static void render_alloc_surfaces() { static uint8_t texture_init; @@ -202,46 +250,7 @@ #ifndef DISABLE_OPENGL if (render_gl) { sdl_textures[0] = sdl_textures[1] = NULL; - glGenTextures(3, textures); - for (int i = 0; i < 3; i++) - { - glBindTexture(GL_TEXTURE_2D, textures[i]); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); - if (i < 2) { - //TODO: Fixme for PAL + invalid display mode - glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, 512, 512, 0, GL_BGRA, GL_UNSIGNED_BYTE, texture_buf); - } else { - uint32_t blank = 255 << 24; - glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, 1, 1, 0, GL_BGRA, GL_UNSIGNED_BYTE, &blank); - } - } - glGenBuffers(2, buffers); - glBindBuffer(GL_ARRAY_BUFFER, buffers[0]); - glBufferData(GL_ARRAY_BUFFER, sizeof(vertex_data), vertex_data, GL_STATIC_DRAW); - glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, buffers[1]); - glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(element_data), element_data, GL_STATIC_DRAW); - tern_val def = {.ptrval = "default.v.glsl"}; - vshader = load_shader(tern_find_path_default(config, "video\0vertex_shader\0", def).ptrval, GL_VERTEX_SHADER); - def.ptrval = "default.f.glsl"; - fshader = load_shader(tern_find_path_default(config, "video\0fragment_shader\0", def).ptrval, GL_FRAGMENT_SHADER); - program = glCreateProgram(); - glAttachShader(program, vshader); - glAttachShader(program, fshader); - glLinkProgram(program); - GLint link_status; - glGetProgramiv(program, GL_LINK_STATUS, &link_status); - if (!link_status) { - fputs("Failed to link shader program\n", stderr); - exit(1); - } - un_textures[0] = glGetUniformLocation(program, "textures[0]"); - un_textures[1] = glGetUniformLocation(program, "textures[1]"); - un_width = glGetUniformLocation(program, "width"); - un_height = glGetUniformLocation(program, "height"); - at_pos = glGetAttribLocation(program, "pos"); + gl_setup(); } else { #endif @@ -266,6 +275,52 @@ } } +static void update_aspect() +{ + //reset default values + memcpy(vertex_data, vertex_data_default, sizeof(vertex_data)); + main_clip.w = main_width; + main_clip.h = main_height; + main_clip.x = main_clip.y = 0; + //calculate configured aspect ratio + char *config_aspect = tern_find_path_default(config, "video\0aspect\0", (tern_val){.ptrval = "4:3"}).ptrval; + if (strcmp("stretch", config_aspect)) { + float src_aspect = 4.0f/3.0f; + char *end; + float aspect_numerator = strtof(config_aspect, &end); + if (aspect_numerator > 0.0f && *end == ':') { + float aspect_denominator = strtof(end+1, &end); + if (aspect_denominator > 0.0f && !*end) { + src_aspect = aspect_numerator / aspect_denominator; + } + } + float aspect = (float)main_width / main_height; + if (fabs(aspect - src_aspect) < 0.01f) { + //close enough for government work + return; + } +#ifndef DISABLE_OPENGL + if (render_gl) { + for (int i = 0; i < 4; i++) + { + if (aspect > src_aspect) { + vertex_data[i*2] *= src_aspect/aspect; + } else { + vertex_data[i*2+1] *= aspect/src_aspect; + } + } + } else { +#endif + main_clip.w = aspect > src_aspect ? src_aspect * (float)main_height : main_width; + main_clip.h = aspect > src_aspect ? main_height : main_width / src_aspect; + main_clip.x = (main_width - main_clip.w) / 2; + main_clip.y = (main_height - main_clip.h) / 2; +#ifndef DISABLE_OPENGL + } +#endif + } +} + static uint32_t overscan_top[NUM_VID_STD] = {2, 21}; static uint32_t overscan_bot[NUM_VID_STD] = {1, 17}; static vid_std video_standard = VID_NTSC; @@ -277,8 +332,10 @@ } atexit(SDL_Quit); printf("width: %d, height: %d\n", width, height); - - uint32_t flags = 0; + windowed_width = width; + windowed_height = height; + + uint32_t flags = SDL_WINDOW_RESIZABLE; if (fullscreen) { flags |= SDL_WINDOW_FULLSCREEN_DESKTOP; @@ -374,37 +431,9 @@ } #endif - SDL_GetWindowSize(main_window, &width, &height); - printf("Window created with size: %d x %d\n", width, height); - float src_aspect = 4.0/3.0; - float aspect = (float)width / height; - def.ptrval = "normal"; - int stretch = fabs(aspect - src_aspect) > 0.01 && !strcmp(tern_find_path_default(config, "video\0aspect\0", def).ptrval, "stretch"); - - if (!stretch) { -#ifndef DISABLE_OPENGL - if (render_gl) { - for (int i = 0; i < 4; i++) - { - if (aspect > src_aspect) { - vertex_data[i*2] *= src_aspect/aspect; - } else { - vertex_data[i*2+1] *= aspect/src_aspect; - } - } - } else { -#endif - float scale_x = (float)width / 320.0; - float scale_y = (float)height / 240.0; - float scale = scale_x > scale_y ? scale_y : scale_x; - main_clip.w = 320.0 * scale; - main_clip.h = 240.0 * scale; - main_clip.x = (width - main_clip.w) / 2; - main_clip.y = (height - main_clip.h) / 2; -#ifndef DISABLE_OPENGL - } -#endif - } + SDL_GetWindowSize(main_window, &main_width, &main_height); + printf("Window created with size: %d x %d\n", main_width, main_height); + update_aspect(); render_alloc_surfaces(); def.ptrval = "off"; scanlines = !strcmp(tern_find_path_default(config, "video\0scanlines\0", def).ptrval, "on"); @@ -843,6 +872,23 @@ 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) { + SDL_GL_DeleteContext(main_context); + main_context = SDL_GL_CreateContext(main_window); + gl_setup(); + } +#endif + break; + } + break; case SDL_QUIT: puts(""); exit(0); @@ -850,16 +896,65 @@ 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) { return; } - SDL_Event event; - while(SDL_PollEvent(&event)) { - handle_event(&event); + drain_events(); + events_processed++; +} + +#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; + } + in_toggle = 1; + + //toggling too fast seems to cause a deadlock + static uint32_t last_toggle; + uint32_t cur = SDL_GetTicks(); + if (last_toggle && cur - last_toggle < TOGGLE_MIN_DELAY) { + in_toggle = 0; + return; } - events_processed++; + last_toggle = cur; + + drain_events(); + is_fullscreen = !is_fullscreen; + if (is_fullscreen) { + SDL_DisplayMode mode; + //TODO: Multiple monitor support + SDL_GetCurrentDisplayMode(0, &mode); + //In theory, the SDL2 docs suggest this is unnecessary + //but without it the OpenGL context remains the original size + //This needs to happen before the fullscreen transition to have any effect + //because SDL does not apply window size changes in fullscreen + SDL_SetWindowSize(main_window, mode.w, mode.h); + } + SDL_SetWindowFullscreen(main_window, is_fullscreen ? SDL_WINDOW_FULLSCREEN_DESKTOP : 0); + //Since we change the window size on transition to full screen + //we need to set it back to normal so we can also go back to windowed mode + //normally you would think that this should only be done when actually transitioning + //but something is screwy in the guts of SDL (at least on Linux) and setting it each time + //is the only thing that seems to work reliably + //when we've just switched to fullscreen mode this should be harmless though + SDL_SetWindowSize(main_window, windowed_width, windowed_height); + drain_events(); + in_toggle = 0; } void render_wait_psg(psg_context * context)