Implement full screen

This commit is contained in:
markmental 2026-03-28 00:45:10 -04:00
commit c840dc0134
6 changed files with 73 additions and 25 deletions

View file

@ -137,6 +137,21 @@ static void tune_relative(App *app, int delta) {
player_tune(&app->player, next_index); player_tune(&app->player, next_index);
} }
static void toggle_fullscreen(App *app) {
Uint32 flags;
if (!app || !app->window) {
return;
}
flags = app->is_fullscreen ? 0 : SDL_WINDOW_FULLSCREEN_DESKTOP;
if (SDL_SetWindowFullscreen(app->window, flags) == 0) {
app->is_fullscreen = !app->is_fullscreen;
} else {
fprintf(stderr, "Fullscreen toggle failed: %s\n", SDL_GetError());
}
}
static void handle_event(App *app, const SDL_Event *event) { static void handle_event(App *app, const SDL_Event *event) {
if (event->type == SDL_QUIT) { if (event->type == SDL_QUIT) {
app->running = 0; app->running = 0;
@ -158,6 +173,9 @@ static void handle_event(App *app, const SDL_Event *event) {
case SDLK_TAB: case SDLK_TAB:
app->mode = app->mode == MODE_FULLSCREEN ? MODE_GUIDE : MODE_FULLSCREEN; app->mode = app->mode == MODE_FULLSCREEN ? MODE_GUIDE : MODE_FULLSCREEN;
break; break;
case SDLK_f:
toggle_fullscreen(app);
break;
case SDLK_UP: case SDLK_UP:
tune_relative(app, -1); tune_relative(app, -1);
break; break;
@ -243,6 +261,8 @@ void app_run(App *app) {
while (app->running) { while (app->running) {
int in_blackout; int in_blackout;
int output_width = WINDOW_WIDTH;
int output_height = WINDOW_HEIGHT;
Uint64 now_ticks; Uint64 now_ticks;
time_t now_wall; time_t now_wall;
@ -255,6 +275,11 @@ void app_run(App *app) {
break; break;
} }
if (SDL_GetRendererOutputSize(app->renderer, &output_width, &output_height) != 0) {
output_width = WINDOW_WIDTH;
output_height = WINDOW_HEIGHT;
}
in_blackout = player_is_in_blackout(&app->player); in_blackout = player_is_in_blackout(&app->player);
now_ticks = SDL_GetTicks64(); now_ticks = SDL_GetTicks64();
now_wall = time(NULL); now_wall = time(NULL);
@ -266,7 +291,7 @@ void app_run(App *app) {
app->last_blackout_state = in_blackout; app->last_blackout_state = in_blackout;
if (app->channels.count == 0) { if (app->channels.count == 0) {
ui_render_no_media(app->renderer, &app->ui_cache); ui_render_no_media(app->renderer, &app->ui_cache, output_width, output_height);
} else if (app->mode == MODE_GUIDE) { } else if (app->mode == MODE_GUIDE) {
if (!in_blackout) { if (!in_blackout) {
player_resume_audio(&app->player); player_resume_audio(&app->player);
@ -275,6 +300,8 @@ void app_run(App *app) {
in_blackout ? NULL : app->video_texture, in_blackout ? NULL : app->video_texture,
app->texture_width, app->texture_width,
app->texture_height, app->texture_height,
output_width,
output_height,
&app->fonts, &app->fonts,
&app->ui_cache, &app->ui_cache,
&app->channels, &app->channels,
@ -289,7 +316,9 @@ void app_run(App *app) {
ui_render_fullscreen(app->renderer, ui_render_fullscreen(app->renderer,
in_blackout ? NULL : app->video_texture, in_blackout ? NULL : app->video_texture,
app->texture_width, app->texture_width,
app->texture_height); app->texture_height,
output_width,
output_height);
} }
SDL_RenderPresent(app->renderer); SDL_RenderPresent(app->renderer);

View file

@ -16,6 +16,7 @@ typedef struct App {
int texture_height; int texture_height;
AppMode mode; AppMode mode;
int running; int running;
int is_fullscreen;
int startup_handoff_active; int startup_handoff_active;
int last_blackout_state; int last_blackout_state;
Uint32 startup_handoff_until; Uint32 startup_handoff_until;

BIN
src/app.o

Binary file not shown.

View file

@ -481,16 +481,21 @@ static void draw_scanline_overlay(SDL_Renderer *renderer, int width, int height)
SDL_SetRenderDrawBlendMode(renderer, SDL_BLENDMODE_NONE); SDL_SetRenderDrawBlendMode(renderer, SDL_BLENDMODE_NONE);
} }
void ui_render_no_media(SDL_Renderer *renderer, const UiCache *cache) { void ui_render_no_media(SDL_Renderer *renderer, const UiCache *cache, int window_width, int window_height) {
SDL_Rect full = {0, 0, WINDOW_WIDTH, WINDOW_HEIGHT}; SDL_Rect full = {0, 0, window_width, window_height};
fill_rect(renderer, &full, COLOR_NAVY_DARK); fill_rect(renderer, &full, COLOR_NAVY_DARK);
draw_cached_text(renderer, &cache->no_media_title, 58, 80); draw_cached_text(renderer, &cache->no_media_title, 58, 80);
draw_cached_text(renderer, &cache->no_media_body, 58, 136); draw_cached_text(renderer, &cache->no_media_body, 58, 136);
draw_cached_text(renderer, &cache->no_media_hint, 58, 176); draw_cached_text(renderer, &cache->no_media_hint, 58, 176);
} }
void ui_render_fullscreen(SDL_Renderer *renderer, SDL_Texture *video_texture, int texture_width, int texture_height) { void ui_render_fullscreen(SDL_Renderer *renderer,
SDL_Rect window = {0, 0, WINDOW_WIDTH, WINDOW_HEIGHT}; SDL_Texture *video_texture,
int texture_width,
int texture_height,
int window_width,
int window_height) {
SDL_Rect window = {0, 0, window_width, window_height};
fill_rect(renderer, &window, COLOR_BLACK); fill_rect(renderer, &window, COLOR_BLACK);
draw_video(renderer, video_texture, texture_width, texture_height, window); draw_video(renderer, video_texture, texture_width, texture_height, window);
} }
@ -499,6 +504,8 @@ void ui_render_guide(SDL_Renderer *renderer,
SDL_Texture *video_texture, SDL_Texture *video_texture,
int texture_width, int texture_width,
int texture_height, int texture_height,
int window_width,
int window_height,
const UiFonts *fonts, const UiFonts *fonts,
UiCache *cache, UiCache *cache,
const ChannelList *channels, const ChannelList *channels,
@ -506,14 +513,18 @@ void ui_render_guide(SDL_Renderer *renderer,
Uint64 app_start_ticks, Uint64 app_start_ticks,
Uint64 now_ticks, Uint64 now_ticks,
time_t now_wall) { time_t now_wall) {
SDL_Rect full = {0, 0, WINDOW_WIDTH, WINDOW_HEIGHT}; double scale_x = (double) window_width / WINDOW_WIDTH;
SDL_Rect info_panel = {0, 0, GUIDE_INFO_WIDTH, GUIDE_INFO_HEIGHT}; double scale_y = (double) window_height / WINDOW_HEIGHT;
SDL_Rect preview = {WINDOW_WIDTH - GUIDE_PREVIEW_WIDTH, 0, GUIDE_PREVIEW_WIDTH, GUIDE_PREVIEW_HEIGHT}; int guide_x_start = (int) (GUIDE_X_START * scale_x);
SDL_Rect status_bar = {GUIDE_INFO_WIDTH, 0, WINDOW_WIDTH - GUIDE_INFO_WIDTH, GUIDE_STATUS_HEIGHT}; int sidebar_width = (int) (GUIDE_SIDEBAR_WIDTH * scale_x);
SDL_Rect header_row = {0, GUIDE_GRID_TOP - GUIDE_STATUS_HEIGHT, WINDOW_WIDTH, GUIDE_STATUS_HEIGHT}; SDL_Rect full = {0, 0, window_width, window_height};
SDL_Rect grid = {0, GUIDE_GRID_TOP, WINDOW_WIDTH, WINDOW_HEIGHT - GUIDE_GRID_TOP - GUIDE_FOOTER_HEIGHT}; SDL_Rect info_panel = {0, 0, (int) (GUIDE_INFO_WIDTH * scale_x), (int) (GUIDE_INFO_HEIGHT * scale_y)};
SDL_Rect preview = {window_width - (int) (GUIDE_PREVIEW_WIDTH * scale_x), 0, (int) (GUIDE_PREVIEW_WIDTH * scale_x), (int) (GUIDE_PREVIEW_HEIGHT * scale_y)};
SDL_Rect status_bar = {info_panel.w, 0, window_width - info_panel.w, (int) (GUIDE_STATUS_HEIGHT * scale_y)};
SDL_Rect header_row = {0, (int) ((GUIDE_GRID_TOP - GUIDE_STATUS_HEIGHT) * scale_y), window_width, (int) (GUIDE_STATUS_HEIGHT * scale_y)};
SDL_Rect grid = {0, (int) (GUIDE_GRID_TOP * scale_y), window_width, window_height - (int) (GUIDE_GRID_TOP * scale_y) - (int) (GUIDE_FOOTER_HEIGHT * scale_y)};
int row_height = grid.h / GUIDE_VISIBLE_ROWS; int row_height = grid.h / GUIDE_VISIBLE_ROWS;
int timeline_w = WINDOW_WIDTH - GUIDE_X_START - 20; int timeline_w = window_width - guide_x_start - (int) (20 * scale_x);
int start_index = active_channel - 2; int start_index = active_channel - 2;
const Channel *selected_channel = NULL; const Channel *selected_channel = NULL;
double pixels_per_minute = timeline_w / 90.0; double pixels_per_minute = timeline_w / 90.0;
@ -554,9 +565,9 @@ void ui_render_guide(SDL_Renderer *renderer,
for (int row = 0; row < GUIDE_VISIBLE_ROWS; ++row) { for (int row = 0; row < GUIDE_VISIBLE_ROWS; ++row) {
int channel_index = start_index + row; int channel_index = start_index + row;
SDL_Rect row_rect = {0, grid.y + row * row_height, WINDOW_WIDTH, row_height}; SDL_Rect row_rect = {0, grid.y + row * row_height, window_width, row_height};
SDL_Rect sidebar = {0, row_rect.y, GUIDE_SIDEBAR_WIDTH, row_height}; SDL_Rect sidebar = {0, row_rect.y, sidebar_width, row_height};
SDL_Rect timeline_rect = {GUIDE_X_START, row_rect.y + 6, timeline_w, row_height - 12}; SDL_Rect timeline_rect = {guide_x_start, row_rect.y + (int) (6 * scale_y), timeline_w, row_height - (int) (12 * scale_y)};
SDL_Rect clip = timeline_rect; SDL_Rect clip = timeline_rect;
int is_selected = channel_index == active_channel; int is_selected = channel_index == active_channel;
SDL_Rect inset = {row_rect.x + 4, row_rect.y + 3, row_rect.w - 8, row_rect.h - 6}; SDL_Rect inset = {row_rect.x + 4, row_rect.y + 3, row_rect.w - 8, row_rect.h - 6};
@ -588,9 +599,9 @@ void ui_render_guide(SDL_Renderer *renderer,
const ProgramEntry *program = channel_resolve_program(channel, app_start_ticks, now_ticks, &program_seek, NULL); const ProgramEntry *program = channel_resolve_program(channel, app_start_ticks, now_ticks, &program_seek, NULL);
time_t guide_view_start_time = now_wall - (30 * 60); time_t guide_view_start_time = now_wall - (30 * 60);
time_t program_start_time = now_wall - (time_t) live_position; time_t program_start_time = now_wall - (time_t) live_position;
int block_x = GUIDE_X_START; int block_x = guide_x_start;
int block_w = 48; int block_w = 48;
SDL_Rect block = {GUIDE_X_START, timeline_rect.y + 4, 48, timeline_rect.h - 8}; SDL_Rect block = {guide_x_start, timeline_rect.y + 4, 48, timeline_rect.h - 8};
SDL_Rect title_rect = {block.x + 8, block.y + 8, block.w - 16, block.h - 16}; SDL_Rect title_rect = {block.x + 8, block.y + 8, block.w - 16, block.h - 16};
char title[128]; char title[128];
@ -599,7 +610,7 @@ void ui_render_guide(SDL_Renderer *renderer,
} }
live_position = channel_live_position_precise(channel, app_start_ticks, now_ticks); live_position = channel_live_position_precise(channel, app_start_ticks, now_ticks);
program_start_time = now_wall - (time_t) program_seek; program_start_time = now_wall - (time_t) program_seek;
block_x = GUIDE_X_START + (int) (((double) (program_start_time - guide_view_start_time)) / 60.0 * pixels_per_minute); block_x = guide_x_start + (int) (((double) (program_start_time - guide_view_start_time)) / 60.0 * pixels_per_minute);
block_w = (int) ((program->duration_seconds / 60.0) * pixels_per_minute); block_w = (int) ((program->duration_seconds / 60.0) * pixels_per_minute);
block = (SDL_Rect){block_x, timeline_rect.y + 4, SDL_max(block_w, 48), timeline_rect.h - 8}; block = (SDL_Rect){block_x, timeline_rect.y + 4, SDL_max(block_w, 48), timeline_rect.h - 8};
title_rect = (SDL_Rect){block.x + 8, block.y + 8, block.w - 16, block.h - 16}; title_rect = (SDL_Rect){block.x + 8, block.y + 8, block.w - 16, block.h - 16};
@ -642,13 +653,13 @@ void ui_render_guide(SDL_Renderer *renderer,
} }
if (selected_channel && active_channel >= 0 && start_index <= active_channel && active_channel < start_index + GUIDE_VISIBLE_ROWS) { if (selected_channel && active_channel >= 0 && start_index <= active_channel && active_channel < start_index + GUIDE_VISIBLE_ROWS) {
SDL_Rect highlight = {0, grid.y + (active_channel - start_index) * row_height, WINDOW_WIDTH, row_height}; SDL_Rect highlight = {0, grid.y + (active_channel - start_index) * row_height, window_width, row_height};
draw_selection_glow(renderer, &highlight); draw_selection_glow(renderer, &highlight);
draw_selection_glow(renderer, &(SDL_Rect){GUIDE_X_START, highlight.y + 6, timeline_w, row_height - 12}); draw_selection_glow(renderer, &(SDL_Rect){guide_x_start, highlight.y + (int) (6 * scale_y), timeline_w, row_height - (int) (12 * scale_y)});
} }
draw_footer_legend(renderer, cache, WINDOW_WIDTH, WINDOW_HEIGHT); draw_footer_legend(renderer, cache, window_width, window_height);
draw_scanline_overlay(renderer, WINDOW_WIDTH, WINDOW_HEIGHT); draw_scanline_overlay(renderer, window_width, window_height);
} }
int ui_cache_init(UiCache *cache, SDL_Renderer *renderer, const UiFonts *fonts, const ChannelList *channels) { int ui_cache_init(UiCache *cache, SDL_Renderer *renderer, const UiFonts *fonts, const ChannelList *channels) {

View file

@ -46,11 +46,18 @@ typedef struct UiCache {
int channel_count; int channel_count;
} UiCache; } UiCache;
void ui_render_fullscreen(SDL_Renderer *renderer, SDL_Texture *video_texture, int texture_width, int texture_height); void ui_render_fullscreen(SDL_Renderer *renderer,
SDL_Texture *video_texture,
int texture_width,
int texture_height,
int window_width,
int window_height);
void ui_render_guide(SDL_Renderer *renderer, void ui_render_guide(SDL_Renderer *renderer,
SDL_Texture *video_texture, SDL_Texture *video_texture,
int texture_width, int texture_width,
int texture_height, int texture_height,
int window_width,
int window_height,
const UiFonts *fonts, const UiFonts *fonts,
UiCache *cache, UiCache *cache,
const ChannelList *channels, const ChannelList *channels,
@ -58,7 +65,7 @@ void ui_render_guide(SDL_Renderer *renderer,
Uint64 app_start_ticks, Uint64 app_start_ticks,
Uint64 now_ticks, Uint64 now_ticks,
time_t now_wall); time_t now_wall);
void ui_render_no_media(SDL_Renderer *renderer, const UiCache *cache); void ui_render_no_media(SDL_Renderer *renderer, const UiCache *cache, int window_width, int window_height);
int ui_load_fonts(UiFonts *fonts); int ui_load_fonts(UiFonts *fonts);
void ui_destroy_fonts(UiFonts *fonts); void ui_destroy_fonts(UiFonts *fonts);
int ui_cache_init(UiCache *cache, SDL_Renderer *renderer, const UiFonts *fonts, const ChannelList *channels); int ui_cache_init(UiCache *cache, SDL_Renderer *renderer, const UiFonts *fonts, const ChannelList *channels);

BIN
src/ui.o

Binary file not shown.