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 CHANNEL_BANNER_DURATION_MS 5000
|
||||
#define CHANNEL_SWITCH_LOCK_DURATION_MS 7000
|
||||
#define NUMERIC_INPUT_TIMEOUT_MS 1000
|
||||
|
||||
static void configure_runtime_environment(void) {
|
||||
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;
|
||||
|
||||
next_index = app->player.current_index;
|
||||
fprintf(stderr, "[DEBUG tune_relative] current_index=%d, delta=%d\n", next_index, delta);
|
||||
if (next_index < 0) {
|
||||
next_index = 0;
|
||||
}
|
||||
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);
|
||||
begin_startup_handoff(app);
|
||||
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;
|
||||
}
|
||||
|
||||
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) {
|
||||
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;
|
||||
break;
|
||||
case SDLK_UP:
|
||||
tune_relative(app, -1);
|
||||
tune_relative(app, 1);
|
||||
break;
|
||||
case SDLK_DOWN:
|
||||
tune_relative(app, 1);
|
||||
tune_relative(app, -1);
|
||||
break;
|
||||
case SDLK_LEFT:
|
||||
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:
|
||||
browse_guide_time(app, GUIDE_BROWSE_STEP_MINUTES);
|
||||
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:
|
||||
break;
|
||||
}
|
||||
|
|
@ -357,6 +456,10 @@ void app_run(App *app) {
|
|||
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) {
|
||||
app->running = 0;
|
||||
break;
|
||||
|
|
@ -370,6 +473,15 @@ void app_run(App *app) {
|
|||
in_blackout = player_is_in_blackout(&app->player);
|
||||
now_ticks = SDL_GetTicks64();
|
||||
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) {
|
||||
app->startup_handoff_active = 1;
|
||||
app->startup_handoff_until = SDL_GetTicks() + 400;
|
||||
|
|
@ -428,7 +540,10 @@ void app_run(App *app) {
|
|||
app->player.current_index,
|
||||
app->app_start_time,
|
||||
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);
|
||||
|
|
|
|||
|
|
@ -29,6 +29,10 @@ typedef struct App {
|
|||
time_t app_start_time;
|
||||
Uint64 app_start_ticks;
|
||||
int guide_time_offset_minutes;
|
||||
char numeric_input_buffer[4];
|
||||
int numeric_input_length;
|
||||
Uint32 numeric_input_timeout;
|
||||
int numeric_input_invalid;
|
||||
ChannelList channels;
|
||||
Player player;
|
||||
UiFonts fonts;
|
||||
|
|
|
|||
BIN
src/app.o
BIN
src/app.o
Binary file not shown.
40
src/ui.c
40
src/ui.c
|
|
@ -634,7 +634,10 @@ static void draw_channel_status_banner(SDL_Renderer *renderer,
|
|||
time_t app_start_time,
|
||||
time_t now_wall,
|
||||
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 channel_pill;
|
||||
SDL_Rect info_clip;
|
||||
|
|
@ -643,6 +646,7 @@ static void draw_channel_status_banner(SDL_Renderer *renderer,
|
|||
char channel_text[96];
|
||||
char time_range[64];
|
||||
char end_text[32];
|
||||
char numeric_display[8];
|
||||
time_t start_time;
|
||||
time_t end_time;
|
||||
double program_seek = 0.0;
|
||||
|
|
@ -689,6 +693,28 @@ static void draw_channel_status_banner(SDL_Renderer *renderer,
|
|||
color_with_alpha(theme->gloss, 42),
|
||||
theme->panel_border);
|
||||
stroke_rect(renderer, &banner, theme->panel_border);
|
||||
|
||||
if (numeric_input_length > 0 || numeric_input_invalid) {
|
||||
SDL_Color accent_fill = theme->ribbon_mid;
|
||||
SDL_Color accent_border = theme->ribbon_bottom;
|
||||
SDL_Color text_color = COLOR_TEXT_LIGHT;
|
||||
|
||||
if (numeric_input_invalid) {
|
||||
snprintf(numeric_display, sizeof(numeric_display), "???");
|
||||
} 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,
|
||||
|
|
@ -698,6 +724,8 @@ static void draw_channel_status_banner(SDL_Renderer *renderer,
|
|||
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);
|
||||
SDL_RenderDrawLine(renderer,
|
||||
channel_pill.x + channel_pill.w + 6,
|
||||
|
|
@ -813,7 +841,10 @@ void ui_render_fullscreen(SDL_Renderer *renderer,
|
|||
int active_channel,
|
||||
time_t app_start_time,
|
||||
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};
|
||||
fill_rect(renderer, &window, COLOR_BLACK);
|
||||
draw_video(renderer, video_texture, texture_width, texture_height, window);
|
||||
|
|
@ -826,7 +857,10 @@ void ui_render_fullscreen(SDL_Renderer *renderer,
|
|||
app_start_time,
|
||||
now_wall,
|
||||
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,
|
||||
time_t app_start_time,
|
||||
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,
|
||||
SDL_Texture *video_texture,
|
||||
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