Initial add of numeric channel switching
This commit is contained in:
parent
30a8f71bb7
commit
1034e31a44
6 changed files with 172 additions and 16 deletions
121
src/app.c
121
src/app.c
|
|
@ -14,6 +14,7 @@
|
||||||
#define GUIDE_BROWSE_MAX_OFFSET_MINUTES (GUIDE_BROWSE_MAX_AHEAD_MINUTES - ((int) (TIMELINE_VISIBLE_SECONDS / 60.0) - 30))
|
#define GUIDE_BROWSE_MAX_OFFSET_MINUTES (GUIDE_BROWSE_MAX_AHEAD_MINUTES - ((int) (TIMELINE_VISIBLE_SECONDS / 60.0) - 30))
|
||||||
#define CHANNEL_BANNER_DURATION_MS 5000
|
#define CHANNEL_BANNER_DURATION_MS 5000
|
||||||
#define CHANNEL_SWITCH_LOCK_DURATION_MS 7000
|
#define CHANNEL_SWITCH_LOCK_DURATION_MS 7000
|
||||||
|
#define NUMERIC_INPUT_TIMEOUT_MS 1000
|
||||||
|
|
||||||
static void configure_runtime_environment(void) {
|
static void configure_runtime_environment(void) {
|
||||||
char runtime_dir[64];
|
char runtime_dir[64];
|
||||||
|
|
@ -141,16 +142,106 @@ static void tune_relative(App *app, int delta) {
|
||||||
app->channel_switch_lock_until = now + CHANNEL_SWITCH_LOCK_DURATION_MS;
|
app->channel_switch_lock_until = now + CHANNEL_SWITCH_LOCK_DURATION_MS;
|
||||||
|
|
||||||
next_index = app->player.current_index;
|
next_index = app->player.current_index;
|
||||||
|
fprintf(stderr, "[DEBUG tune_relative] current_index=%d, delta=%d\n", next_index, delta);
|
||||||
if (next_index < 0) {
|
if (next_index < 0) {
|
||||||
next_index = 0;
|
next_index = 0;
|
||||||
}
|
}
|
||||||
next_index = (next_index + delta + app->channels.count) % app->channels.count;
|
next_index = (next_index + delta + app->channels.count) % app->channels.count;
|
||||||
|
fprintf(stderr, "[DEBUG tune_relative] calculated next_index=%d\n", next_index);
|
||||||
destroy_video_texture(app);
|
destroy_video_texture(app);
|
||||||
begin_startup_handoff(app);
|
begin_startup_handoff(app);
|
||||||
player_tune(&app->player, next_index);
|
player_tune(&app->player, next_index);
|
||||||
|
fprintf(stderr, "[DEBUG tune_relative] after player_tune, current_index=%d\n", app->player.current_index);
|
||||||
app->channel_banner_until = SDL_GetTicks() + CHANNEL_BANNER_DURATION_MS;
|
app->channel_banner_until = SDL_GetTicks() + CHANNEL_BANNER_DURATION_MS;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static int find_channel_by_number(const ChannelList *channels, int number) {
|
||||||
|
for (int i = 0; i < channels->count; i++) {
|
||||||
|
if (channels->items[i].number == number) {
|
||||||
|
return i;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void tune_to_channel(App *app, int channel_index) {
|
||||||
|
Uint32 now;
|
||||||
|
|
||||||
|
if (app->channels.count == 0 || channel_index < 0 || channel_index >= app->channels.count) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
fprintf(stderr, "[DEBUG tune_to_channel] requested channel_index=%d, current_index=%d\n", channel_index, app->player.current_index);
|
||||||
|
now = SDL_GetTicks();
|
||||||
|
if (now < app->channel_switch_lock_until) {
|
||||||
|
fprintf(stderr, "[DEBUG tune_to_channel] LOCKED, aborting\n");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
app->channel_switch_lock_until = now + CHANNEL_SWITCH_LOCK_DURATION_MS;
|
||||||
|
|
||||||
|
destroy_video_texture(app);
|
||||||
|
begin_startup_handoff(app);
|
||||||
|
player_tune(&app->player, channel_index);
|
||||||
|
fprintf(stderr, "[DEBUG tune_to_channel] after player_tune, current_index=%d\n", app->player.current_index);
|
||||||
|
app->channel_banner_until = SDL_GetTicks() + CHANNEL_BANNER_DURATION_MS;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void process_numeric_input(App *app) {
|
||||||
|
int target_number;
|
||||||
|
int channel_index;
|
||||||
|
|
||||||
|
if (app->numeric_input_length == 0) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
target_number = atoi(app->numeric_input_buffer);
|
||||||
|
fprintf(stderr, "[DEBUG process_numeric_input] input='%s', target_number=%d, current_index=%d\n",
|
||||||
|
app->numeric_input_buffer, target_number, app->player.current_index);
|
||||||
|
channel_index = find_channel_by_number(&app->channels, target_number);
|
||||||
|
fprintf(stderr, "[DEBUG process_numeric_input] found channel_index=%d for number %d\n", channel_index, target_number);
|
||||||
|
|
||||||
|
if (channel_index >= 0) {
|
||||||
|
tune_to_channel(app, channel_index);
|
||||||
|
app->numeric_input_invalid = 0;
|
||||||
|
} else {
|
||||||
|
app->numeric_input_invalid = 1;
|
||||||
|
app->channel_banner_until = SDL_GetTicks() + CHANNEL_BANNER_DURATION_MS;
|
||||||
|
}
|
||||||
|
|
||||||
|
app->numeric_input_length = 0;
|
||||||
|
app->numeric_input_buffer[0] = '\0';
|
||||||
|
}
|
||||||
|
|
||||||
|
static void handle_numeric_key(App *app, int digit) {
|
||||||
|
Uint32 now;
|
||||||
|
|
||||||
|
if (app->channels.count == 0) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
now = SDL_GetTicks();
|
||||||
|
|
||||||
|
if (now < app->channel_switch_lock_until) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (app->numeric_input_length == 0) {
|
||||||
|
app->numeric_input_timeout = now + NUMERIC_INPUT_TIMEOUT_MS;
|
||||||
|
app->numeric_input_invalid = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (app->numeric_input_length < 3) {
|
||||||
|
app->numeric_input_buffer[app->numeric_input_length] = '0' + digit;
|
||||||
|
app->numeric_input_length++;
|
||||||
|
app->numeric_input_buffer[app->numeric_input_length] = '\0';
|
||||||
|
app->channel_banner_until = SDL_GetTicks() + CHANNEL_BANNER_DURATION_MS;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (app->numeric_input_length >= 3) {
|
||||||
|
process_numeric_input(app);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
static void browse_guide_time(App *app, int delta_minutes) {
|
static void browse_guide_time(App *app, int delta_minutes) {
|
||||||
int next_offset;
|
int next_offset;
|
||||||
|
|
||||||
|
|
@ -256,10 +347,10 @@ static void handle_event(App *app, const SDL_Event *event) {
|
||||||
app->about_modal_open = !app->about_modal_open;
|
app->about_modal_open = !app->about_modal_open;
|
||||||
break;
|
break;
|
||||||
case SDLK_UP:
|
case SDLK_UP:
|
||||||
tune_relative(app, -1);
|
tune_relative(app, 1);
|
||||||
break;
|
break;
|
||||||
case SDLK_DOWN:
|
case SDLK_DOWN:
|
||||||
tune_relative(app, 1);
|
tune_relative(app, -1);
|
||||||
break;
|
break;
|
||||||
case SDLK_LEFT:
|
case SDLK_LEFT:
|
||||||
browse_guide_time(app, -GUIDE_BROWSE_STEP_MINUTES);
|
browse_guide_time(app, -GUIDE_BROWSE_STEP_MINUTES);
|
||||||
|
|
@ -267,6 +358,14 @@ static void handle_event(App *app, const SDL_Event *event) {
|
||||||
case SDLK_RIGHT:
|
case SDLK_RIGHT:
|
||||||
browse_guide_time(app, GUIDE_BROWSE_STEP_MINUTES);
|
browse_guide_time(app, GUIDE_BROWSE_STEP_MINUTES);
|
||||||
break;
|
break;
|
||||||
|
case SDLK_0: case SDLK_1: case SDLK_2: case SDLK_3: case SDLK_4:
|
||||||
|
case SDLK_5: case SDLK_6: case SDLK_7: case SDLK_8: case SDLK_9:
|
||||||
|
handle_numeric_key(app, event->key.keysym.sym - SDLK_0);
|
||||||
|
break;
|
||||||
|
case SDLK_KP_0: case SDLK_KP_1: case SDLK_KP_2: case SDLK_KP_3: case SDLK_KP_4:
|
||||||
|
case SDLK_KP_5: case SDLK_KP_6: case SDLK_KP_7: case SDLK_KP_8: case SDLK_KP_9:
|
||||||
|
handle_numeric_key(app, event->key.keysym.sym - SDLK_KP_0);
|
||||||
|
break;
|
||||||
default:
|
default:
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
@ -357,6 +456,10 @@ void app_run(App *app) {
|
||||||
handle_event(app, &event);
|
handle_event(app, &event);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (app->numeric_input_length > 0 && SDL_GetTicks() >= app->numeric_input_timeout) {
|
||||||
|
process_numeric_input(app);
|
||||||
|
}
|
||||||
|
|
||||||
if (update_video_texture(app) != 0) {
|
if (update_video_texture(app) != 0) {
|
||||||
app->running = 0;
|
app->running = 0;
|
||||||
break;
|
break;
|
||||||
|
|
@ -370,6 +473,15 @@ void app_run(App *app) {
|
||||||
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 = channel_wall_time_from_ticks(app->app_start_time, app->app_start_ticks, now_ticks);
|
now_wall = channel_wall_time_from_ticks(app->app_start_time, app->app_start_ticks, now_ticks);
|
||||||
|
|
||||||
|
static Uint32 last_debug_print = 0;
|
||||||
|
if (now_ticks - last_debug_print > 1000) {
|
||||||
|
fprintf(stderr, "[DEBUG main_loop] current_index=%d, channel_number=%d, lock_remaining=%d\n",
|
||||||
|
app->player.current_index,
|
||||||
|
app->player.current_index >= 0 ? app->channels.items[app->player.current_index].number : -1,
|
||||||
|
(int)(app->channel_switch_lock_until - SDL_GetTicks()));
|
||||||
|
last_debug_print = now_ticks;
|
||||||
|
}
|
||||||
if (app->last_blackout_state && !in_blackout) {
|
if (app->last_blackout_state && !in_blackout) {
|
||||||
app->startup_handoff_active = 1;
|
app->startup_handoff_active = 1;
|
||||||
app->startup_handoff_until = SDL_GetTicks() + 400;
|
app->startup_handoff_until = SDL_GetTicks() + 400;
|
||||||
|
|
@ -428,7 +540,10 @@ void app_run(App *app) {
|
||||||
app->player.current_index,
|
app->player.current_index,
|
||||||
app->app_start_time,
|
app->app_start_time,
|
||||||
now_wall,
|
now_wall,
|
||||||
SDL_GetTicks() < app->channel_banner_until);
|
SDL_GetTicks() < app->channel_banner_until,
|
||||||
|
app->numeric_input_buffer,
|
||||||
|
app->numeric_input_length,
|
||||||
|
app->numeric_input_invalid);
|
||||||
}
|
}
|
||||||
|
|
||||||
SDL_RenderPresent(app->renderer);
|
SDL_RenderPresent(app->renderer);
|
||||||
|
|
|
||||||
|
|
@ -29,6 +29,10 @@ typedef struct App {
|
||||||
time_t app_start_time;
|
time_t app_start_time;
|
||||||
Uint64 app_start_ticks;
|
Uint64 app_start_ticks;
|
||||||
int guide_time_offset_minutes;
|
int guide_time_offset_minutes;
|
||||||
|
char numeric_input_buffer[4];
|
||||||
|
int numeric_input_length;
|
||||||
|
Uint32 numeric_input_timeout;
|
||||||
|
int numeric_input_invalid;
|
||||||
ChannelList channels;
|
ChannelList channels;
|
||||||
Player player;
|
Player player;
|
||||||
UiFonts fonts;
|
UiFonts fonts;
|
||||||
|
|
|
||||||
BIN
src/app.o
BIN
src/app.o
Binary file not shown.
58
src/ui.c
58
src/ui.c
|
|
@ -634,7 +634,10 @@ static void draw_channel_status_banner(SDL_Renderer *renderer,
|
||||||
time_t app_start_time,
|
time_t app_start_time,
|
||||||
time_t now_wall,
|
time_t now_wall,
|
||||||
int window_width,
|
int window_width,
|
||||||
int window_height) {
|
int window_height,
|
||||||
|
const char *numeric_input_buffer,
|
||||||
|
int numeric_input_length,
|
||||||
|
int numeric_input_invalid) {
|
||||||
SDL_Rect banner;
|
SDL_Rect banner;
|
||||||
SDL_Rect channel_pill;
|
SDL_Rect channel_pill;
|
||||||
SDL_Rect info_clip;
|
SDL_Rect info_clip;
|
||||||
|
|
@ -643,6 +646,7 @@ static void draw_channel_status_banner(SDL_Renderer *renderer,
|
||||||
char channel_text[96];
|
char channel_text[96];
|
||||||
char time_range[64];
|
char time_range[64];
|
||||||
char end_text[32];
|
char end_text[32];
|
||||||
|
char numeric_display[8];
|
||||||
time_t start_time;
|
time_t start_time;
|
||||||
time_t end_time;
|
time_t end_time;
|
||||||
double program_seek = 0.0;
|
double program_seek = 0.0;
|
||||||
|
|
@ -689,15 +693,39 @@ static void draw_channel_status_banner(SDL_Renderer *renderer,
|
||||||
color_with_alpha(theme->gloss, 42),
|
color_with_alpha(theme->gloss, 42),
|
||||||
theme->panel_border);
|
theme->panel_border);
|
||||||
stroke_rect(renderer, &banner, theme->panel_border);
|
stroke_rect(renderer, &banner, theme->panel_border);
|
||||||
draw_pill_button(renderer, theme, &channel_pill, theme->panel_fill, theme->panel_border);
|
|
||||||
draw_text_shadowed(renderer,
|
if (numeric_input_length > 0 || numeric_input_invalid) {
|
||||||
fonts->medium,
|
SDL_Color accent_fill = theme->ribbon_mid;
|
||||||
channel_text,
|
SDL_Color accent_border = theme->ribbon_bottom;
|
||||||
&channel_pill,
|
SDL_Color text_color = COLOR_TEXT_LIGHT;
|
||||||
channel_pill.x + 14,
|
|
||||||
channel_pill.y + 8,
|
if (numeric_input_invalid) {
|
||||||
ensure_contrast(theme->panel_text, theme->panel_fill),
|
snprintf(numeric_display, sizeof(numeric_display), "???");
|
||||||
color_with_alpha(COLOR_BLACK, 255));
|
} else {
|
||||||
|
snprintf(numeric_display, sizeof(numeric_display), "%s_", numeric_input_buffer);
|
||||||
|
}
|
||||||
|
|
||||||
|
draw_pill_button(renderer, theme, &channel_pill, accent_fill, accent_border);
|
||||||
|
draw_text_shadowed(renderer,
|
||||||
|
fonts->medium,
|
||||||
|
numeric_display,
|
||||||
|
&channel_pill,
|
||||||
|
channel_pill.x + 14,
|
||||||
|
channel_pill.y + 8,
|
||||||
|
text_color,
|
||||||
|
color_with_alpha(COLOR_BLACK, 255));
|
||||||
|
} else {
|
||||||
|
draw_pill_button(renderer, theme, &channel_pill, theme->panel_fill, theme->panel_border);
|
||||||
|
draw_text_shadowed(renderer,
|
||||||
|
fonts->medium,
|
||||||
|
channel_text,
|
||||||
|
&channel_pill,
|
||||||
|
channel_pill.x + 14,
|
||||||
|
channel_pill.y + 8,
|
||||||
|
ensure_contrast(theme->panel_text, theme->panel_fill),
|
||||||
|
color_with_alpha(COLOR_BLACK, 255));
|
||||||
|
}
|
||||||
|
|
||||||
set_draw_color(renderer, theme->status_divider);
|
set_draw_color(renderer, theme->status_divider);
|
||||||
SDL_RenderDrawLine(renderer,
|
SDL_RenderDrawLine(renderer,
|
||||||
channel_pill.x + channel_pill.w + 6,
|
channel_pill.x + channel_pill.w + 6,
|
||||||
|
|
@ -813,7 +841,10 @@ void ui_render_fullscreen(SDL_Renderer *renderer,
|
||||||
int active_channel,
|
int active_channel,
|
||||||
time_t app_start_time,
|
time_t app_start_time,
|
||||||
time_t now_wall,
|
time_t now_wall,
|
||||||
int show_channel_banner) {
|
int show_channel_banner,
|
||||||
|
const char *numeric_input_buffer,
|
||||||
|
int numeric_input_length,
|
||||||
|
int numeric_input_invalid) {
|
||||||
SDL_Rect window = {0, 0, window_width, 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);
|
||||||
|
|
@ -826,7 +857,10 @@ void ui_render_fullscreen(SDL_Renderer *renderer,
|
||||||
app_start_time,
|
app_start_time,
|
||||||
now_wall,
|
now_wall,
|
||||||
window_width,
|
window_width,
|
||||||
window_height);
|
window_height,
|
||||||
|
numeric_input_buffer,
|
||||||
|
numeric_input_length,
|
||||||
|
numeric_input_invalid);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
5
src/ui.h
5
src/ui.h
|
|
@ -59,7 +59,10 @@ void ui_render_fullscreen(SDL_Renderer *renderer,
|
||||||
int active_channel,
|
int active_channel,
|
||||||
time_t app_start_time,
|
time_t app_start_time,
|
||||||
time_t now_wall,
|
time_t now_wall,
|
||||||
int show_channel_banner);
|
int show_channel_banner,
|
||||||
|
const char *numeric_input_buffer,
|
||||||
|
int numeric_input_length,
|
||||||
|
int numeric_input_invalid);
|
||||||
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,
|
||||||
|
|
|
||||||
BIN
src/ui.o
BIN
src/ui.o
Binary file not shown.
Loading…
Add table
Add a link
Reference in a new issue