diff --git a/Ncurses/Makefile b/Ncurses/Makefile new file mode 100644 index 0000000..14e0e38 --- /dev/null +++ b/Ncurses/Makefile @@ -0,0 +1,13 @@ +CC=gcc +CFLAGS= -Wall -Werror -Wextra -O3 `pkg-config --libs --cflags ncurses` + +all: menu mouse_menu acs rolling readf ncurses_and_readline + +ncurses_and_readline: ncurses_and_readline.c + @echo -e "\t\tCC $<" + $(CC) $(CFLAGS) -lreadline -o $@ $< + +%: %.c + @echo -e "\t\tCC $<" + $(CC) $(CFLAGS) -o $@ $< + diff --git a/Ncurses/acs.c b/Ncurses/acs.c new file mode 100644 index 0000000..43fe00d --- /dev/null +++ b/Ncurses/acs.c @@ -0,0 +1,45 @@ +#include + +int main() +{ + initscr(); + + printw("Upper right corner "); addch(ACS_URCORNER); printw("\n"); + printw("Upper left corner "); addch(ACS_ULCORNER); printw("\n"); + printw("Lower left corner "); addch(ACS_LLCORNER); printw("\n"); + printw("Lower right corner "); addch(ACS_LRCORNER); printw("\n"); + printw("Tee pointing right "); addch(ACS_LTEE); printw("\n"); + printw("Tee pointing left "); addch(ACS_RTEE); printw("\n"); + printw("Tee pointing up "); addch(ACS_BTEE); printw("\n"); + printw("Tee pointing down "); addch(ACS_TTEE); printw("\n"); + printw("Horizontal line "); addch(ACS_HLINE); printw("\n"); + printw("Vertical line "); addch(ACS_VLINE); printw("\n"); + printw("Large Plus or cross over "); addch(ACS_PLUS); printw("\n"); + printw("Scan Line 1 "); addch(ACS_S1); printw("\n"); + printw("Scan Line 3 "); addch(ACS_S3); printw("\n"); + printw("Scan Line 7 "); addch(ACS_S7); printw("\n"); + printw("Scan Line 9 "); addch(ACS_S9); printw("\n"); + printw("Diamond "); addch(ACS_DIAMOND); printw("\n"); + printw("Checker board (stipple) "); addch(ACS_CKBOARD); printw("\n"); + printw("Degree Symbol "); addch(ACS_DEGREE); printw("\n"); + printw("Plus/Minus Symbol "); addch(ACS_PLMINUS); printw("\n"); + printw("Bullet "); addch(ACS_BULLET); printw("\n"); + printw("Arrow Pointing Left "); addch(ACS_LARROW); printw("\n"); + printw("Arrow Pointing Right "); addch(ACS_RARROW); printw("\n"); + printw("Arrow Pointing Down "); addch(ACS_DARROW); printw("\n"); + printw("Arrow Pointing Up "); addch(ACS_UARROW); printw("\n"); + printw("Board of squares "); addch(ACS_BOARD); printw("\n"); + printw("Lantern Symbol "); addch(ACS_LANTERN); printw("\n"); + printw("Solid Square Block "); addch(ACS_BLOCK); printw("\n"); + printw("Less/Equal sign "); addch(ACS_LEQUAL); printw("\n"); + printw("Greater/Equal sign "); addch(ACS_GEQUAL); printw("\n"); + printw("Pi "); addch(ACS_PI); printw("\n"); + printw("Not equal "); addch(ACS_NEQUAL); printw("\n"); + printw("UK pound sign "); addch(ACS_STERLING); printw("\n"); + + refresh(); + getch(); + endwin(); + + return 0; +} diff --git a/Ncurses/menu.c b/Ncurses/menu.c new file mode 100644 index 0000000..56f723d --- /dev/null +++ b/Ncurses/menu.c @@ -0,0 +1,94 @@ +#include +#include + +#define WIDTH 30 +#define HEIGHT 10 + +int startx = 0; +int starty = 0; + +char *choices[] = { + "Choice 1", + "Choice 2", + "Choice 3", + "Choice 4", + "Exit", + }; +int n_choices = sizeof(choices) / sizeof(char *); +void print_menu(WINDOW *menu_win, int highlight); + +int main() +{ WINDOW *menu_win; + int highlight = 1; + int choice = 0; + int c; + + initscr(); + clear(); + noecho(); + cbreak(); /* Line buffering disabled. pass on everything */ + startx = (80 - WIDTH) / 2; + starty = (24 - HEIGHT) / 2; + + menu_win = newwin(HEIGHT, WIDTH, starty, startx); + keypad(menu_win, TRUE); + mvprintw(0, 0, "Use arrow keys to go up and down, Press enter to select a choice"); + refresh(); + print_menu(menu_win, highlight); + while(1) + { c = wgetch(menu_win); + switch(c) + { case KEY_UP: + if(highlight == 1) + highlight = n_choices; + else + --highlight; + break; + case KEY_DOWN: + if(highlight == n_choices) + highlight = 1; + else + ++highlight; + break; + case 10: + choice = highlight; + break; + default: + mvprintw(24, 0, "Charcter pressed is = %3d", c); + if(c > 32 && c < 256) printw(" (%c)", c); + clrtoeol(); + refresh(); + break; + } + print_menu(menu_win, highlight); + if(choice != 0) /* User did a choice come out of the infinite loop */ + break; + } + mvprintw(23, 0, "You chose choice %d with choice string %s\n", choice, choices[choice - 1]); + clrtoeol(); + refresh(); + sleep(1); + endwin(); + return 0; +} + + +void print_menu(WINDOW *menu_win, int highlight) +{ + int x, y, i; + + x = 2; + y = 2; + box(menu_win, 0, 0); + for(i = 0; i < n_choices; ++i) + { if(highlight == i + 1) /* High light the present choice */ + { wattron(menu_win, A_REVERSE); + mvwprintw(menu_win, y, x, "%s", choices[i]); + wattroff(menu_win, A_REVERSE); + } + else + mvwprintw(menu_win, y, x, "%s", choices[i]); + ++y; + } + wrefresh(menu_win); +} diff --git a/Ncurses/mouse_menu.c b/Ncurses/mouse_menu.c new file mode 100644 index 0000000..ce5b629 --- /dev/null +++ b/Ncurses/mouse_menu.c @@ -0,0 +1,132 @@ +#include +#include +#include + +#define WIDTH 30 +#define HEIGHT 10 + +int startx = 0; +int starty = 0; + +char *choices[] = { "Choice 1", + "Choice 2", + "Choice 3", + "Choice 4", + "Exit", + }; + +int n_choices = sizeof(choices) / sizeof(char *); + +void print_menu(WINDOW *menu_win, int choice); +void report_choice(int mouse_x, int mouse_y, int *p_choice); + +int main() +{ int c, choice = 0, highlight = 1; + WINDOW *menu_win; + MEVENT event; + + /* Initialize curses */ + initscr(); + clear(); + noecho(); + cbreak(); //Line buffering disabled. pass on everything + + /* Try to put the window in the middle of screen */ + startx = (80 - WIDTH) / 2; + starty = (24 - HEIGHT) / 2; + + attron(A_REVERSE); + mvprintw(23, 1, "startx=%d, starty=%d", startx, starty); + refresh(); + attroff(A_REVERSE); + + /* Print the menu for the first time */ + menu_win = newwin(HEIGHT, WIDTH, starty, startx); + keypad(menu_win, TRUE); + print_menu(menu_win, 1); + /* Get all the mouse events */ + mousemask(ALL_MOUSE_EVENTS, NULL); + while(1) + { c = wgetch(menu_win); + mvprintw(4, 1, "Character pressed is = %3d", c); + refresh(); + switch(c) + { case KEY_MOUSE: + if(getmouse(&event) == OK){ /* When the user clicks left mouse button */ + mvprintw(5, 1, "x=%d, y=%d, bstate=0x%08x", event.x, event.y, event.bstate); + clrtoeol(); + refresh(); + if(event.bstate & (BUTTON1_PRESSED|BUTTON1_CLICKED)) + { + report_choice(event.x + 1, event.y + 1, &choice); + if(choice == -1) //Exit chosen + goto end; + mvprintw(22, 1, "Choice made is : %d String Chosen is \"%10s\"", choice, choices[choice - 1]); + clrtoeol(); + } + } + break; + case KEY_UP: + refresh(); + if(highlight == 1) + highlight = n_choices; + else + --highlight; + break; + case KEY_DOWN: + if(highlight == n_choices) + highlight = 1; + else + ++highlight; + break; + case 10: + choice = highlight; + mvprintw(2, 1, "Choice: %d", choice); + break; + } + print_menu(menu_win, highlight); + refresh(); + if(choice == n_choices) break; + } +end: + endwin(); + return 0; +} + + +void print_menu(WINDOW *menu_win, int choice) +{ + int x, y, i; + + x = 2; + y = 2; + box(menu_win, 0, 0); + for(i = 0; i < n_choices; ++i) + { if(choice == i + 1) + { wattron(menu_win, A_REVERSE); + mvwprintw(menu_win, y, x, "%s", choices[i]); + wattroff(menu_win, A_REVERSE); + } + else + mvwprintw(menu_win, y, x, "%s", choices[i]); + ++y; + } + wrefresh(menu_win); +} + +/* Report the choice according to mouse position */ +void report_choice(int mouse_x, int mouse_y, int *p_choice) +{ int i,j, choice; + + i = startx + 2; + j = starty + 3; + + for(choice = 0; choice < n_choices; ++choice) + if(mouse_y == j + choice && mouse_x >= i && mouse_x <= i + (int)strlen(choices[choice])) + { if(choice == n_choices - 1) + *p_choice = -1; + else + *p_choice = choice + 1; + break; + } +} diff --git a/Ncurses/ncurses.cflags b/Ncurses/ncurses.cflags new file mode 100644 index 0000000..68d5165 --- /dev/null +++ b/Ncurses/ncurses.cflags @@ -0,0 +1 @@ +-std=c17 \ No newline at end of file diff --git a/Ncurses/ncurses.config b/Ncurses/ncurses.config new file mode 100644 index 0000000..e0284f4 --- /dev/null +++ b/Ncurses/ncurses.config @@ -0,0 +1,2 @@ +// Add predefined macros for your project here. For example: +// #define THE_ANSWER 42 diff --git a/Ncurses/ncurses.creator b/Ncurses/ncurses.creator new file mode 100644 index 0000000..e94cbbd --- /dev/null +++ b/Ncurses/ncurses.creator @@ -0,0 +1 @@ +[General] diff --git a/Ncurses/ncurses.cxxflags b/Ncurses/ncurses.cxxflags new file mode 100644 index 0000000..6435dfc --- /dev/null +++ b/Ncurses/ncurses.cxxflags @@ -0,0 +1 @@ +-std=c++17 \ No newline at end of file diff --git a/Ncurses/ncurses.files b/Ncurses/ncurses.files new file mode 100644 index 0000000..f098653 --- /dev/null +++ b/Ncurses/ncurses.files @@ -0,0 +1,7 @@ +acs.c +menu.c +mouse_menu.c +ncurses_and_readline.c +rolling.c +scroll.c +scrollfile.c diff --git a/Ncurses/ncurses.includes b/Ncurses/ncurses.includes new file mode 100644 index 0000000..9c558e3 --- /dev/null +++ b/Ncurses/ncurses.includes @@ -0,0 +1 @@ +. diff --git a/Ncurses/ncurses_and_readline.c b/Ncurses/ncurses_and_readline.c new file mode 100644 index 0000000..7e913b8 --- /dev/null +++ b/Ncurses/ncurses_and_readline.c @@ -0,0 +1,336 @@ +/* + * This file is part of the ncurses project. + * Copyright 2020 Edward V. Emelianov . + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +// based on https://stackoverflow.com/a/28709979/1965803 -> +// https://github.com/ulfalizer/readline-and-ncurses +// Copyright (c) 2015-2019, Ulf Magnusson +// SPDX-License-Identifier: ISC + +#include +#include +#include +#include +#include +#include +#include +#include + +// Keeps track of the terminal mode so we can reset the terminal if needed on errors +static bool visual_mode = false; +// insert commands when true; roll upper screen when false +static bool insert_mode = true; +static bool should_exit = false; + +static void fail_exit(const char *msg){ + // Make sure endwin() is only called in visual mode. As a note, calling it + // twice does not seem to be supported and messed with the cursor position. + if(visual_mode) endwin(); + fprintf(stderr, "%s\n", msg); + exit(EXIT_FAILURE); +} + +static WINDOW *msg_win; // Message window +static WINDOW *sep_win; // Separator line above the command (readline) window +static WINDOW *cmd_win; // Command (readline) window + +// string list +typedef struct _Line{ + int Nline; + char *contents; + struct _Line *prev, *next; +} Line; +// head of list, current item and first line on screen +Line *head = NULL, *curr = NULL, *firstline = NULL; +int nr_lines = 0; // total anount of data portions @ input + +static unsigned char input; // Input character for readline + +// Used to signal "no more input" after feeding a character to readline +static bool input_avail = false; + +// Not bothering with 'input_avail' and just returning 0 here seems to do the +// right thing too, but this might be safer across readline versions +static int readline_input_avail(){ + return input_avail; +} + +static int readline_getc(__attribute__((__unused__)) FILE *dummy){ + input_avail = false; + return input; +} + +static void forward_to_readline(char c){ + input = c; + input_avail = true; + rl_callback_read_char(); +} + +static void msg_win_redisplay(bool for_resize){ + werase(msg_win); + Line *l = firstline; + int nlines = 0; // total amount of lines @ output + for(; l && (nlines < LINES - 2); l = l->next){ + size_t contlen = strlen(l->contents) + 128; + char *buf = malloc(contlen); + // don't add trailing '\n' (or last line will be empty with cursor) + contlen = snprintf(buf, contlen, "%s", l->contents); + int nlnext = (contlen - 1) / COLS + 1; + wmove(msg_win, nlines, 0); + if(nlines + nlnext < LINES-2){ // can put out the full line + waddstr(msg_win, buf); + //wprintw(msg_win, "%d (%d): %s -> %d", l->Nline, firstline->Nline, l->contents, nlnext); + nlines += nlnext; + }else{ // put only first part + int rest = LINES-2 - nlines; + waddnstr(msg_win, buf, rest *COLS); + break; + } + free(buf); + } + curs_set(0); + if(for_resize) wnoutrefresh(msg_win); + else wrefresh(msg_win); +} + +static void got_command(char *line){ + if(!line) // Ctrl-D pressed on empty line + should_exit = true; + else{ + if(!*line) return; // zero length + add_history(line); + Line *lp = malloc(sizeof(Line)); + lp->contents = line; + lp->prev = curr; + lp->next = NULL; + lp->Nline = nr_lines++; + if(!curr || !head){ + head = curr = firstline = lp; + }else + curr->next = lp; + curr = lp; + // roll back to show last input + if(curr->prev){ + firstline = curr; + int totalln = (strlen(firstline->contents) - 1)/COLS + 1; + while(firstline->prev){ + totalln += (strlen(firstline->prev->contents) - 1)/COLS + 1; + if(totalln > LINES-2) break; + firstline = firstline->prev; + } + } + msg_win_redisplay(false); + } +} + +static void cmd_win_redisplay(bool for_resize){ + int cursor_col = 2 + rl_point; // "> " width is 2 + werase(cmd_win); + int x = 0, maxw = COLS-2; + if(cursor_col > maxw){ + x = cursor_col - maxw; + cursor_col = maxw; + } + char abuf[4096]; + snprintf(abuf, 4096, "> %s", rl_line_buffer); + waddstr(cmd_win, abuf+x); + wmove(cmd_win, 0, cursor_col); + curs_set(2); + if(for_resize) wnoutrefresh(cmd_win); + else wrefresh(cmd_win); +} + +static void readline_redisplay(void){ + cmd_win_redisplay(false); +} + +static void show_mode(bool for_resize){ + wclear(sep_win); + if(insert_mode) waddstr(sep_win, "INSERT (TAB to switch, ctrl+D to quit)"); + else waddstr(sep_win, "SCROLL (TAB to switch, q to quit)"); + if(for_resize) wnoutrefresh(sep_win); + else wrefresh(sep_win); + cmd_win_redisplay(for_resize); +} + +static void resize(void){ + if(LINES > 2){ + wresize(msg_win, LINES - 2, COLS); + wresize(sep_win, 1, COLS); + wresize(cmd_win, 1, COLS); + mvwin(sep_win, LINES - 2, 0); + mvwin(cmd_win, LINES - 1, 0); + } + msg_win_redisplay(true); + show_mode(true); + doupdate(); +} + +static void init_ncurses(void){ + if (!initscr()) + fail_exit("Failed to initialize ncurses"); + visual_mode = true; + if(has_colors()){ + start_color(); + use_default_colors(); + } + cbreak(); + noecho(); + nonl(); + intrflush(NULL, FALSE); + // Do not enable keypad() since we want to pass unadulterated input to readline + keypad(cmd_win, 0); + // Explicitly specify a "very visible" cursor to make sure it's at least + // consistent when we turn the cursor on and off (maybe it would make sense + // to query it and use the value we get back too). "normal" vs. "very + // visible" makes no difference in gnome-terminal or xterm. Let this fail + // for terminals that do not support cursor visibility adjustments. + curs_set(2); + if(LINES > 2){ + msg_win = newwin(LINES - 2, COLS, 0, 0); + sep_win = newwin(1, COLS, LINES - 2, 0); + cmd_win = newwin(1, COLS, LINES - 1, 0); + } + else{ + // Degenerate case. Give the windows the minimum workable size to + // prevent errors from e.g. wmove(). + msg_win = newwin(1, COLS, 0, 0); + sep_win = newwin(1, COLS, 0, 0); + cmd_win = newwin(1, COLS, 0, 0); + } + if(!msg_win || !sep_win || !cmd_win) + fail_exit("Failed to allocate windows"); + // Allow strings longer than the command window and show only the last part if the string doesn't fit + // scrollok(cmd_win, TRUE); + if(has_colors()){ + // Use white-on-blue cells for the separator window... + init_pair(1, COLOR_WHITE, COLOR_BLUE); + wbkgd(sep_win, COLOR_PAIR(1)); + } + else + // ...or the "best highlighting mode of the terminal" if it doesn't + // support colors + wbkgd(sep_win, A_STANDOUT); + show_mode(false); + mousemask(BUTTON4_PRESSED|BUTTON5_PRESSED, NULL); +} + +static void deinit_ncurses(void){ + delwin(msg_win); + delwin(sep_win); + delwin(cmd_win); + endwin(); + visual_mode = false; +} + +static void init_readline(void){ + // Let ncurses do all terminal and signal handling + rl_catch_signals = 0; + rl_catch_sigwinch = 0; + rl_deprep_term_function = NULL; + rl_prep_term_function = NULL; + // Prevent readline from setting the LINES and COLUMNS environment + // variables, which override dynamic size adjustments in ncurses. When + // using the alternate readline interface (as we do here), LINES and + // COLUMNS are not updated if the terminal is resized between two calls to + // rl_callback_read_char() (which is almost always the case). + rl_change_environment = 0; + // Handle input by manually feeding characters to readline + rl_getc_function = readline_getc; + rl_input_available_hook = readline_input_avail; + rl_redisplay_function = readline_redisplay; + rl_callback_handler_install("> ", got_command); +} + +static void deinit_readline(void){ + rl_callback_handler_remove(); +} + +static void rolldown(){ + if(firstline && firstline->prev){ + firstline = firstline->prev; + msg_win_redisplay(false); + } +} + +static void rollup(){ + if(firstline && firstline->next){ + firstline = firstline->next; + msg_win_redisplay(false); + } +} + +int main(void){ + init_ncurses(); + init_readline(); + MEVENT event; + do{ + int c = wgetch(cmd_win); + bool processed = true; + switch(c){ + case KEY_MOUSE: + if(getmouse(&event) == OK){ + if(event.bstate & (BUTTON4_PRESSED)) rolldown(); // wheel up + else if(event.bstate & (BUTTON5_PRESSED)) rollup(); // wheel down + } + break; + case '\t': // tab switch between scroll and edit mode + keypad(cmd_win, insert_mode); // enable/disable reaction @ special characters + insert_mode = !insert_mode; + show_mode(false); + break; + case KEY_RESIZE: + resize(); + break; + default: + processed = false; + } + if(processed) continue; + if(insert_mode){ + forward_to_readline(c); + }else{ + switch(c){ + case KEY_UP: // roll down for one item + rolldown(); + break; + case KEY_DOWN: // roll up for one item + rollup(); + break; + case KEY_PPAGE: // PageUp: roll down for 10 items + for(int i = 0; i < 10; ++i){ + if(firstline && firstline->prev) firstline = firstline->prev; + else break; + } + msg_win_redisplay(false); + break; + case KEY_NPAGE: // PageUp: roll up for 10 items + for(int i = 0; i < 10; ++i){ + if(firstline && firstline->next) firstline = firstline->next; + else break; + } + msg_win_redisplay(false); + break; + default: + if(c == 'q' || c == 'Q') should_exit = true; // quit + } + } + }while(!should_exit); + deinit_ncurses(); + deinit_readline(); + puts("Shut down cleanly"); + return 0; +} diff --git a/Ncurses/rolling.c b/Ncurses/rolling.c new file mode 100644 index 0000000..642b717 --- /dev/null +++ b/Ncurses/rolling.c @@ -0,0 +1,28 @@ +#include +#include + +static int maxx, maxy; + + +void initscreen(){ + initscr(); + getmaxyx(stdscr, maxy, maxx); // getyx - get current coords + cbreak(); + keypad(stdscr, TRUE); // We get F1, F2 etc.. + noecho(); + nodelay(stdscr, TRUE); // getch() returns ERR if no keys pressed + start_color(); + init_pair(1, COLOR_RED, COLOR_BLACK); + init_pair(2, COLOR_GREEN, COLOR_BLACK); + init_pair(3, COLOR_YELLOW, COLOR_BLACK); + init_pair(4, COLOR_BLUE, COLOR_BLACK); + init_pair(5, COLOR_MAGENTA, COLOR_BLACK); + init_pair(6, COLOR_CYAN, COLOR_BLACK); + init_pair(7, COLOR_WHITE, COLOR_BLACK); + clear(); +} + +int main(){ + initscreen(); + return 0; +} diff --git a/Ncurses/scroll.c b/Ncurses/scroll.c new file mode 100644 index 0000000..377f3a0 --- /dev/null +++ b/Ncurses/scroll.c @@ -0,0 +1,110 @@ +#include +#include +#include +#include + +#define MAXLEN 127 + +typedef struct Line { + char *contents; + struct Line *prev, *next; +} Line; + +char inputstr[MAXLEN+1] = {0}; +int currinp = 0; + +Line *head = NULL, *curr = NULL, *firstline = NULL; + +int refreshmain = 1, refreshstr = 1; + +int nr_lines; +int curr_line; +WINDOW *mainwin, *onestring; + +int term_rows, term_cols; + +void draw(Line *l); + +int main(int argc, char **argv) +{ + Line *lp; + // printf("\033c"); - cls + initscr(); + noecho(); + getmaxyx(stdscr, term_rows, term_cols); + term_rows -= 2; + mainwin = newwin(term_rows, term_cols, 0, 0); + onestring = newwin(1, term_cols, term_rows+1, 0); + keypad(onestring, TRUE); // for KEY_UP, KEY_DOWN + + curr_line = 0; + + draw(curr); + + int ch; + while ((ch = wgetch(onestring)) != EOF) + { + if (ch == KEY_UP && firstline->prev != NULL) + { + curr_line--; + firstline = firstline->prev; + refreshmain = 1; + } + else if (ch == KEY_DOWN && firstline->next != NULL && curr_line + term_rows < nr_lines) + { + curr_line++; + firstline = firstline->next; + refreshmain = 1; + } + else if(ch == 10 && currinp){ + lp = malloc(sizeof(Line)); + lp->contents = strdup(inputstr); + lp->prev = curr; + lp->next = NULL; + if(!curr || !head){ + head = curr = firstline = lp; + }else + curr->next = lp; + curr = lp; + ++nr_lines; + while(nr_lines - curr_line > term_rows){ + curr_line++; + firstline = firstline->next; + } + currinp = 0; + inputstr[0] = 0; + refreshstr = 1; + refreshmain = 1; + }else if(ch >= ' ' && ch < 256 && ch != 127){ + if(currinp > MAXLEN){ + wclear(onestring); + waddstr(onestring, "Limit reached!"); + wrefresh(onestring); + }else{ + inputstr[currinp++] = ch; + inputstr[currinp] = 0; + } + refreshstr = 1; + } + draw(firstline); + } + + endwin(); + return 0; +} + +void draw(Line *l){ + if(refreshmain){ + refreshmain = 0; + wclear(mainwin); + for(int i = 0; i < term_rows && l != NULL; i++, l = l->next) + wprintw(mainwin, "%s\n", l->contents); + wrefresh(mainwin); + } + if(refreshstr){ + refreshstr = 0; + wclear(onestring); + wprintw(onestring, " > %s", inputstr); + wrefresh(onestring); + } +} diff --git a/Ncurses/scrollfile.c b/Ncurses/scrollfile.c new file mode 100644 index 0000000..08b88b1 --- /dev/null +++ b/Ncurses/scrollfile.c @@ -0,0 +1,128 @@ +#include +#include +#include + +#define MAXLEN 128 + +typedef struct Line { + char contents[MAXLEN]; + struct Line *prev, *next; +} Line; + +#define MAXSTRL 120 +char inputstr[MAXSTRL+1] = {0}; +int currinp = 0; + +Line *head, *curr; + +int refreshmain = 1, refreshstr = 1; + +int nr_lines; +int curr_line; +WINDOW *mainwin, *onestring; + +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(); + getmaxyx(stdscr, term_rows, term_cols); + term_rows -= 2; + mainwin = newwin(term_rows, term_cols, 0, 0); + onestring = newwin(1, term_cols, term_rows+1, 0); + keypad(onestring, TRUE); // for KEY_UP, KEY_DOWN + + waddstr(mainwin, "Reading text...\n"); + wrefresh(mainwin); + sleep(1); + load(argv[1]); + + curr = head; + curr_line = 0; + + draw(curr); + + int ch; + while ((ch = wgetch(onestring)) != EOF) + { + if (ch == KEY_UP && curr->prev != NULL) + { + curr_line--; + curr = curr->prev; + refreshmain = 1; + } + else if (ch == KEY_DOWN && curr->next != NULL + && curr_line + term_rows < nr_lines) + { + curr_line++; + curr = curr->next; + refreshmain = 1; + } + else if(ch == 10){ + currinp = 0; + inputstr[0] = 0; + refreshstr = 1; + }else if(ch >= ' ' && ch < 256 && ch != 127){ + if(currinp > MAXSTRL){ + wclear(onestring); + waddstr(onestring, "Limit reached!"); + wrefresh(onestring); + }else{ + inputstr[currinp++] = ch; + inputstr[currinp] = 0; + } + refreshstr = 1; + } + 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) +{ + if(refreshmain){ + refreshmain = 0; + wclear(mainwin); + for(int i = 0; i < term_rows && l != NULL; i++, l = l->next) + waddstr(mainwin, l->contents); + wrefresh(mainwin); + } + if(refreshstr){ + refreshstr = 0; + wclear(onestring); + wprintw(onestring, " > %s", inputstr); + wrefresh(onestring); + } +}