Skip to content

Commit ccbca35

Browse files
committed
add scrollback feature
1 parent 55acf7b commit ccbca35

7 files changed

Lines changed: 182 additions & 4 deletions

File tree

Makefile

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
VERSION ?= 2.1.0
1+
VERSION ?= 2.0.0
22
# compiler and linker
33
CROSS_COMPILE ?=
44
CC = ${CROSS_COMPILE}gcc

src/config.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,9 @@
33
static int borderpx = 2;
44
char default_shell[] = "/bin/bash";
55

6+
/* Scrollback configuration */
7+
int scrollback_lines = 256; /* Number of lines to keep in scrollback buffer */
8+
69
static int initial_width = 320;
710
static int initial_height = 240;
811
static float opt_scale = 2.0;

src/keyboard.c

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -395,20 +395,27 @@ int handle_keyboard_event(SDL_Event *event) {
395395
if (event->key.type == SDL_KEYDOWN && event->key.state == SDL_PRESSED) {
396396
if (event->key.keysym.sym == JOYBUTTON_UP) {
397397
simulate_key(SDLK_UP, STATE_TYPED);
398+
return 1;
398399
} else if (event->key.keysym.sym == JOYBUTTON_DOWN) {
399400
simulate_key(SDLK_DOWN, STATE_TYPED);
401+
return 1;
400402
} else if (event->key.keysym.sym == JOYBUTTON_LEFT) {
401403
simulate_key(SDLK_LEFT, STATE_TYPED);
404+
return 1;
402405
} else if (event->key.keysym.sym == JOYBUTTON_RIGHT) {
403406
simulate_key(SDLK_RIGHT, STATE_TYPED);
407+
return 1;
404408
} else if (event->key.keysym.sym == JOYBUTTON_START || event->key.keysym.sym == JOYBUTTON_A) {
405409
simulate_key(SDLK_RETURN, STATE_TYPED);
410+
return 1;
406411
} else if (event->key.keysym.sym == JOYBUTTON_SELECT) {
407412
simulate_key(SDLK_TAB, STATE_TYPED);
413+
return 1;
408414
} else if (event->key.keysym.sym == JOYBUTTON_B) {
409415
tty_write("\003", 1); // Ctrl+C
416+
return 1;
410417
}
411-
return 1;
418+
return 0;
412419
}
413420
#endif
414421
return 0;

src/keyboard.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,8 @@
3434
#define KEY_OSKACTIVATE JOYBUTTON_X
3535
#define KEY_OSKLOCATION JOYBUTTON_Y
3636
#define KEY_OSKTOGGLE JOYBUTTON_R1
37+
#define KEY_SCROLLUP JOYBUTTON_L2
38+
#define KEY_SCROLLDOWN JOYBUTTON_R2
3739
#define KEY_QUIT JOYBUTTON_MENU
3840
#define KEY_TAB JOYBUTTON_SELECT
3941
#define KEY_RETURN JOYBUTTON_START
@@ -54,6 +56,8 @@
5456
#define KEY_OSKACTIVATE SDLK_F12
5557
#define KEY_OSKLOCATION SDLK_F11
5658
#define KEY_OSKTOGGLE SDLK_F9
59+
#define KEY_SCROLLUP SDLK_F8
60+
#define KEY_SCROLLDOWN SDLK_F7
5761
#define KEY_QUIT SDLK_UNKNOWN // not used
5862
#define KEY_TAB SDLK_TAB
5963
#define KEY_RETURN SDLK_RETURN

src/main.c

Lines changed: 77 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -114,6 +114,7 @@ SDL_Surface *rotated_screen; // final frame matching window size
114114

115115
static void draw(void);
116116
static void draw_region(int, int, int, int);
117+
static void draw_scrollbar(void);
117118
static void main_loop(void);
118119
int tty_thread(void *unused);
119120

@@ -567,6 +568,9 @@ void x_draw_cursor(void) {
567568
static int oldx = 0, oldy = 0;
568569
int sl;
569570
Glyph g = {{' '}, ATTR_NULL, defaultbg, defaultcs, 0};
571+
572+
/* Don't draw cursor when scrolled */
573+
if (t_get_scroll_offset() > 0) return;
570574

571575
LIMIT(oldx, 0, term.col - 1);
572576
LIMIT(oldy, 0, term.row - 1);
@@ -603,25 +607,80 @@ void redraw(void) {
603607

604608
void draw(void) {
605609
draw_region(0, 0, term.col, term.row);
610+
draw_scrollbar();
606611
update_render();
607612
}
608613

614+
void draw_scrollbar(void) {
615+
int scroll_offset = t_get_scroll_offset();
616+
if (scroll_offset == 0 || main_window.surface == NULL) return;
617+
618+
/* Draw scroll indicator in top-right corner */
619+
char scroll_text[64];
620+
snprintf(scroll_text, sizeof(scroll_text), "[%d]^", scroll_offset);
621+
622+
int text_x = main_window.surface->w - (strlen(scroll_text) * main_window.char_width) - borderpx - 2;
623+
int text_y = borderpx;
624+
625+
SDL_Color indicator_bg = drawing_ctx.colors[defaultcs];
626+
SDL_Color indicator_fg = drawing_ctx.colors[defaultbg];
627+
628+
/* Draw background box */
629+
SDL_Rect bg_rect = {
630+
text_x - 2,
631+
text_y - 1,
632+
strlen(scroll_text) * main_window.char_width + 4,
633+
main_window.char_height + 2
634+
};
635+
SDL_FillRect(main_window.surface, &bg_rect, SDL_MapRGB(main_window.surface->format, indicator_bg.r, indicator_bg.g, indicator_bg.b));
636+
637+
/* Draw text */
638+
if (is_ttf_loaded()) {
639+
draw_string_ttf(main_window.surface, scroll_text, text_x, text_y, indicator_fg, indicator_bg);
640+
} else {
641+
draw_string(main_window.surface, scroll_text, text_x, text_y, SDL_MapRGB(main_window.surface->format, indicator_fg.r, indicator_fg.g, indicator_fg.b), embedded_font_name);
642+
}
643+
}
644+
609645
void draw_region(int x1, int y1, int x2, int y2) {
610646
int ic, ib, x, y, ox, sl;
611647
Glyph base, new;
612648
char buf[DRAW_BUF_SIZ];
649+
int scroll_offset = t_get_scroll_offset();
650+
Line line_to_draw;
613651

614652
if (!(main_window.state & WIN_VISIBLE)) return;
615653

616654
for (y = y1; y < y2; y++) {
617655
if (!term.dirty[y]) continue;
618656

657+
/* Determine which line to draw (from scrollback or current screen) */
658+
if (scroll_offset > 0 && y < scroll_offset) {
659+
/* Draw from scrollback buffer (circular buffer) */
660+
int sb_idx = (term.scrollback_pos - scroll_offset + y + term.scrollback_size) % term.scrollback_size;
661+
if (sb_idx >= 0 && sb_idx < term.scrollback_count) {
662+
line_to_draw = term.scrollback[sb_idx];
663+
} else {
664+
line_to_draw = term.line[y];
665+
}
666+
} else {
667+
/* Draw from current screen, offset by scroll amount */
668+
int screen_y = y - scroll_offset;
669+
if (screen_y >= 0 && screen_y < term.row) {
670+
line_to_draw = term.line[screen_y];
671+
} else {
672+
sdl_term_clear(0, y, term.col, y);
673+
term.dirty[y] = 0;
674+
continue;
675+
}
676+
}
677+
619678
sdl_term_clear(0, y, term.col, y);
620679
term.dirty[y] = 0;
621-
base = term.line[y][0];
680+
base = line_to_draw[0];
622681
ic = ib = ox = 0;
623682
for (x = x1; x < x2; x++) {
624-
new = term.line[y][x];
683+
new = line_to_draw[x];
625684
if (ib > 0 && (!(new.state & GLYPH_SET) || ATTRCMP(base, new) || ib >= DRAW_BUF_SIZ - UTF_SIZ)) {
626685
x_draws(buf, base, ox, y, ic, ib);
627686
ic = ib = 0;
@@ -690,6 +749,22 @@ void k_press(SDL_Event *ev) {
690749

691750
// printf("kpress: keysym=%d scancode=%d mod=%d\n", ksym, e->keysym.scancode, e->keysym.mod);
692751

752+
/* Handle scroll up/down for scrollback */
753+
if (ksym == KEY_SCROLLUP) {
754+
t_scroll_view_up(3);
755+
draw(); // Force immediate redraw
756+
return;
757+
} else if (ksym == KEY_SCROLLDOWN) {
758+
t_scroll_view_down(3);
759+
draw(); // Force immediate redraw
760+
return;
761+
}
762+
763+
/* Reset scroll on any other key press */
764+
if (t_get_scroll_offset() > 0) {
765+
t_scroll_view_reset();
766+
}
767+
693768
if ((non_printing_key = k_map(ksym, e->keysym.mod))) { /* 1. non printing keys from vt100.h */
694769
// print_non_printing_key_for_debug(non_printing_key, e);
695770
tty_write(non_printing_key, strlen(non_printing_key));

src/vt100.c

Lines changed: 75 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ extern unsigned int defaultbg;
2121
extern unsigned int tabspaces;
2222
extern char default_shell[];
2323
extern char termname[];
24+
extern int scrollback_lines;
2425

2526
/* External variables from main.c */
2627
extern char *opt_io;
@@ -334,6 +335,8 @@ void t_new(int col, int row) {
334335
term.dirty[row] = 0;
335336
}
336337
memset(term.tabs, 0, term.col * sizeof(*term.tabs));
338+
/* initialize scrollback buffer */
339+
t_scrollback_init(scrollback_lines);
337340
/* setup screen */
338341
t_reset();
339342
}
@@ -376,6 +379,13 @@ void t_scroll_up(int orig, int n) {
376379
Line temp;
377380
LIMIT(n, 0, term.bot - orig + 1);
378381

382+
/* Save scrolled lines to scrollback buffer (only from top of scroll region) */
383+
if (orig == term.top) {
384+
for (i = 0; i < n && orig + i <= term.bot; i++) {
385+
t_scrollback_add_line(term.line[orig + i]);
386+
}
387+
}
388+
379389
t_clear_region(0, orig, term.col - 1, orig + n - 1);
380390

381391
for (i = orig; i <= term.bot - n; i++) {
@@ -386,6 +396,9 @@ void t_scroll_up(int orig, int n) {
386396
term.dirty[i] = 1;
387397
term.dirty[i + n] = 1;
388398
}
399+
400+
/* Reset scroll view when content scrolls */
401+
t_scroll_view_reset();
389402
}
390403

391404
void t_newline(int first_col) {
@@ -399,6 +412,68 @@ void t_newline(int first_col) {
399412
t_move_to(first_col ? 0 : term.c.x, y);
400413
}
401414

415+
/* Scrollback buffer functions */
416+
void t_scrollback_init(int max_lines) {
417+
int i;
418+
term.scrollback_size = max_lines;
419+
term.scrollback_count = 0;
420+
term.scrollback_pos = 0;
421+
term.scroll_offset = 0;
422+
423+
if (max_lines > 0) {
424+
term.scrollback = x_malloc(max_lines * sizeof(Line));
425+
for (i = 0; i < max_lines; i++) {
426+
term.scrollback[i] = x_malloc(term.col * sizeof(Glyph));
427+
}
428+
} else {
429+
term.scrollback = NULL;
430+
}
431+
}
432+
433+
void t_scrollback_add_line(Line line) {
434+
if (term.scrollback_size <= 0 || !term.scrollback) return;
435+
436+
/* Copy line data to scrollback buffer (circular buffer) */
437+
memcpy(term.scrollback[term.scrollback_pos], line, term.col * sizeof(Glyph));
438+
439+
/* Update circular buffer position */
440+
term.scrollback_pos = (term.scrollback_pos + 1) % term.scrollback_size;
441+
442+
/* Update count (max out at scrollback_size) */
443+
if (term.scrollback_count < term.scrollback_size) {
444+
term.scrollback_count++;
445+
}
446+
}
447+
448+
void t_scroll_view_up(int n) {
449+
if (term.scrollback_count == 0) return;
450+
451+
term.scroll_offset += n;
452+
LIMIT(term.scroll_offset, 0, term.scrollback_count);
453+
454+
t_full_dirt();
455+
}
456+
457+
void t_scroll_view_down(int n) {
458+
if (term.scroll_offset == 0) return;
459+
460+
term.scroll_offset -= n;
461+
LIMIT(term.scroll_offset, 0, term.scrollback_count);
462+
463+
t_full_dirt();
464+
}
465+
466+
void t_scroll_view_reset(void) {
467+
if (term.scroll_offset == 0) return;
468+
469+
term.scroll_offset = 0;
470+
t_full_dirt();
471+
}
472+
473+
int t_get_scroll_offset(void) {
474+
return term.scroll_offset;
475+
}
476+
402477
void csi_parse(void) {
403478
/* int noarg = 1; */
404479
char *p = csiescseq.buf;

src/vt100.h

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -118,6 +118,12 @@ typedef struct {
118118
int mode; /* terminal mode flags */
119119
int esc; /* escape state flags */
120120
bool *tabs;
121+
/* Scrollback buffer */
122+
Line *scrollback; /* scrollback buffer */
123+
int scrollback_size; /* max scrollback lines */
124+
int scrollback_count; /* current number of lines in scrollback */
125+
int scrollback_pos; /* current position in circular buffer */
126+
int scroll_offset; /* current scroll offset (0 = bottom) */
121127
} Term;
122128

123129
/* Global terminal state - extern declarations */
@@ -156,6 +162,14 @@ void t_set_dirt(int top, int bot);
156162
void t_set_mode(bool priv, bool set, int *args, int narg);
157163
void t_full_dirt(void);
158164

165+
/* Scrollback functions */
166+
void t_scrollback_init(int max_lines);
167+
void t_scrollback_add_line(Line line);
168+
void t_scroll_view_up(int n);
169+
void t_scroll_view_down(int n);
170+
void t_scroll_view_reset(void);
171+
int t_get_scroll_offset(void);
172+
159173
/* CSI/Escape sequence functions */
160174
void csi_dump(void);
161175
void csi_handle(void);

0 commit comments

Comments
 (0)