I am trying to implement a mini version of bash using Termcap
capabilities, and now I am trying to read user's input and retype it in the terminal Stdout.
Every thing is working fine, but the problem occurs when trying to resize the terminal window, as you can see in the below gif, when I write a SINGLE line and then shrink the window, and expand it again the output wraps good as it should.
But when my text goes past the first terminal line (without pressing Enter), if I do the same steps as before, the output wrapping is different from what I want. What I need is that the last characters of the first line must join with the second line characters when shrinking the window, and vice versa when I expand the window the first characters of the second line join with the first line characters.
Is there a way to make the outputted lines wrap exactly the same as bash ? (joining lines with each other)
Here is my code so far, use -ltermcap
flag when you want to compile it and Thanks in advance.
#include <ctype.h>
#include <termcap.h>
#include <termios.h>
# include <sys/ioctl.h>
# include <signal.h>
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int g_screen_width = 0;
int g_cursor_colm = 0;
int g_printed = 0;
int ft_putchar(int c)
{
int len = write(1, &c, 1);
return (len);
}
int ft_isprint(int c)
{
return (c >= 32 && c < 127);
}
void update_cursor_position()
{
g_cursor_colm = g_printed % g_screen_width;
}
void update_screen_width()
{
struct winsize w;
ioctl(STDIN_FILENO, TIOCGWINSZ, &w);
g_screen_width = w.ws_col;
}
void sigwinch_handler(int signo)
{
if (signo == SIGWINCH)
{
update_screen_width();
update_cursor_position();
}
}
void move_cursor_to_colum(int col)
{
char *ch_cap;
ch_cap = tgetstr("ch", NULL);
tputs(tgoto(ch_cap, 0, col), 1, ft_putchar);
}
void move_cursor_down_vertically()
{
char *do_cap;
do_cap = tgetstr("do", NULL);
tputs(do_cap, 1, ft_putchar);
}
void move_cursor_to_next_line()
{
move_cursor_to_colum(0);
move_cursor_down_vertically();
}
void enable_raw_mode()
{
struct termios raw;
tcgetattr(STDIN_FILENO, &raw);
raw.c_lflag &= ~(ECHO | ICANON);
tcsetattr(STDIN_FILENO, TCSAFLUSH, &raw);
}
void disable_raw_mode(struct termios old_termios_state)
{
tcsetattr(STDIN_FILENO, TCSAFLUSH, &old_termios_state);
}
int main()
{
struct termios original_termios_state;
char *term_type = getenv("TERM");
char c;
char *line;
tgetent(NULL, term_type);
tcgetattr(STDIN_FILENO, &original_termios_state);
enable_raw_mode();
update_screen_width();
signal(SIGWINCH, sigwinch_handler);
while (read(STDIN_FILENO, &c, 1))
{
if (ft_isprint(c))
{
ft_putchar(c);
g_printed++;
g_cursor_colm++;
if (g_cursor_colm == g_screen_width)
{
g_cursor_colm = 0;
move_cursor_to_next_line();
}
}
else if (c == 27) // ESC
{
disable_raw_mode(original_termios_state);
exit(0);
}
}
disable_raw_mode(original_termios_state);
return (0);
}
Your terminal remembers where there are actual line skips (the one that you do explicitly with cursor_down in move_cursor_down_vertically()
), as opposed to line wraps the terminal does on its own due to its limited width.
So:
sigwinch_handler()
when you receive the SIGWINCH signal.