What is the recommended way to do text scrolling in ncurses?

I am trying to implement a ncurses application with text scrolling as less. What is the recommended way to do this?

Here is what I know:

  • You can use scroll to move the text buffer up or down 1 line. However, you will get one empty line at the top if you scroll down or below, if you scroll up and you have to repaint yourself.
  • Ncurses makes a phrase for you that will ruin my math when I have to determine which line I should repaint in step 1.

I assume that I could reimplement wordwrap myself and save an array of all the lines after the wrapper, but this seems like a common problem, so there may be a better way.

+6
source share
3 answers

I am not very good at ncurses yet, so I don’t know all the library functions, but I keep a doubly linked list of text strings. Here's a primitive clone less:

 #include <ncurses.h> #include <stdlib.h> #define MAXLEN 128 typedef struct Line { char contents[MAXLEN]; struct Line *prev, *next; } Line; Line *head, *curr; int nr_lines; int curr_line; int term_rows, term_cols; void load(const char *filename); void draw(Line *l); int main(int argc, char **argv) { if (argc == 1) { fputs("less: No file to open\n", stderr); return 1; } initscr(); noecho(); keypad(stdscr, TRUE); // for KEY_UP, KEY_DOWN getmaxyx(stdscr, term_rows, term_cols); addstr("Reading text...\n"); load(argv[1]); curr = head; curr_line = 0; draw(curr); int ch; while ((ch = getch()) != EOF && ch != 'q') { if (ch == KEY_UP && curr->prev != NULL) { curr_line--; curr = curr->prev; } else if (ch == KEY_DOWN && curr->next != NULL && curr_line + term_rows < nr_lines) { curr_line++; curr = curr->next; } draw(curr); } endwin(); return 0; } void load(const char *filename) { FILE *fp = fopen(filename, "r"); Line *lp; head = malloc(sizeof(Line)); head->prev = head->next = NULL; curr = head; while (fgets(curr->contents, MAXLEN, fp)) { lp = malloc(sizeof(Line)); lp->prev = curr; curr->next = lp; curr = lp; nr_lines++; } curr->next = NULL; } void draw(Line *l) { int i; clear(); for (i = 0; i < term_rows && l != NULL; i++, l = l->next) addstr(l->contents); } 

I'm not sure how this will handle strings longer than 128 characters, but this is another problem with a different solution.

As for word wrapping, if you don't need to keep the original text format, you can split the long lines into two, as they are read from the file.

+2
source

ncurses only knows about the current screen. When the screen scrolls, ncurses does not track the text that leaves the screen. Your terminal may have a reverse scroll buffer, but it is not dependent on ncurses, and curses are not visible in the terminal's scroll buffer.

I expect that you will need to keep track of your current location in the file if you need to scroll.

I did not think that ncurses did word wrap, how do you do word wrap?

+1
source

The answer depends on how much text you have and on what other uses you make data on the screen. But usually the pad function is the preferred way to display scrollable text. This is not only a ncurses function, but is also supported by most curse implementations (i.e., anything after the end of the 1980s).

A pad is like a window, but its size is not limited to the current screen size. Instead, data is displayed through a view whose position within the pad can be easily changed.

There are two sample programs in ncurses-examples : view.c displays a file by writing its contents to a window, while padview.c uses a panel that draws the entire file and uses pad functions to move around as needed, up / down and left / right.

In these programs, the show_all function performs the actual drawing and has approximately a third length for padview.c (35 lines) compared to the equivalent for view.c (94 lines).

Further reading: An example discussion of view for comparing ncurses and slang libraries.

+1
source

Source: https://habr.com/ru/post/894106/


All Articles