Commit 6f50424c authored by mbionchi's avatar mbionchi

[WIP] lotsa changes:

  * add multi-line animation support with scrolling
  * many new data structures to support animations
  * change mode to halfdelay() to support animations
  * disable going up the index as well as g/G for now
parent 4b9da9a6
bin_PROGRAMS = telnetsite
telnetsite_SOURCES = site.c data.c data.h scroll.c scroll.h render.c render.h log.c log.h
telnetsite_SOURCES = main.c anim.c anim.h site.c site.h data.c data.h scroll.c scroll.h render.c render.h log.c log.h
telnetsite_LDADD = ${ncurses_LIBS}
AM_CFLAGS = -Isrc/
/*
* This file is part of telnet-site.
*
* telnet-site 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.
*
* telnet-site 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 telnet-site. If not, see <https://www.gnu.org/licenses/>.
*/
#include "anim.h"
#include "data.h"
#include <stdlib.h>
#include <stddef.h>
void pop_anim_ref_front(struct window *window) {
struct anim_ref *old = window->content.anim_refs;
if (old != NULL) {
window->content.anim_refs = window->content.anim_refs->next;
free(old);
}
}
void pop_anim_ref_back(struct window *window) {
struct anim_ref *iter = window->content.anim_refs,
*prev = NULL;
if (iter != NULL) {
prev = iter;
while (iter->next != NULL) {
prev = iter;
iter = iter->next;
}
prev->next = NULL;
free(iter);
}
}
void push_anim_ref_front(struct window *window, size_t index) {
struct anim_ref *ref = malloc(sizeof(struct anim_ref));
ref->index = index;
ref->counter = 1;
ref->running = 1;
ref->next = window->content.anim_refs;
window->content.anim_refs = ref;
}
void push_anim_ref_back(struct window *window, size_t index) {
struct anim_ref *ref = malloc(sizeof(struct anim_ref));
ref->index = index;
ref->counter = 1;
ref->running = 1;
ref->next = NULL;
if (window->content.anim_refs == NULL) {
window->content.anim_refs = ref;
} else {
struct anim_ref *iter = window->content.anim_refs;
while (iter->next != NULL) {
iter = iter->next;
}
iter->next = ref;
}
}
void anim_tick(struct window *window) {
struct anim_ref *iter = window->content.anim_refs;
while (iter != NULL) {
if (iter->counter >= window->content.formatted[iter->index]->anim->frames[window->content.formatted[iter->index]->anim->current_frame_index].delay) {
if (iter->running) {
size_t i = iter->index;
size_t y = i-window->scroll;
if (window->content.formatted[i]->anim->current_frame_index ==
window->content.formatted[i]->anim->n_frames-1 &&
!window->content.formatted[i]->anim->loop) {
iter->running = 0;
} else {
scrollok(window->window, 0);
window->content.formatted[i]->anim->current_frame_index =
(window->content.formatted[i]->anim->current_frame_index + 1) %
window->content.formatted[i]->anim->n_frames;
mvwprintw(window->window, y, 0, "%s",
window->content.formatted[i]->anim->frames[window->content.formatted[i]->anim->current_frame_index].s->data);
i++;
y++;
while (i < window->content.n_formatted &&
window->content.formatted[i]->type == ANIM &&
!window->content.formatted[i]->anim->is_first_line) {
window->content.formatted[i]->anim->current_frame_index =
(window->content.formatted[i]->anim->current_frame_index + 1) %
window->content.formatted[i]->anim->n_frames;
mvwprintw(window->window, y, 0, "%s",
window->content.formatted[i]->anim->frames[window->content.formatted[i]->anim->current_frame_index].s->data);
i++;
y++;
}
scrollok(window->window, 1);
wrefresh(window->window);
iter->counter = 1;
}
}
} else {
iter->counter++;
}
iter = iter->next;
}
}
/*
* This file is part of telnet-site.
*
* telnet-site 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.
*
* telnet-site 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 telnet-site. If not, see <https://www.gnu.org/licenses/>.
*/
#ifndef _TELNET_SITE_ANIM_H
#define _TELNET_SITE_ANIM_H
#include "data.h"
void anim_tick(struct window *window);
void push_anim_ref_front(struct window *window, size_t index);
void push_anim_ref_back(struct window *window, size_t index);
void pop_anim_ref_front(struct window *window);
void pop_anim_ref_back(struct window *window);
#endif
This diff is collapsed.
......@@ -20,6 +20,7 @@
#include <stdio.h>
#include <dirent.h>
#include <curses.h>
struct line {
char *line;
......@@ -32,10 +33,69 @@ struct section {
struct line *content;
};
struct string {
char *data;
size_t len;
};
struct frame {
unsigned delay;
struct string *s;
};
struct animation {
struct frame *frames;
size_t current_frame_index;
size_t n_frames;
size_t nmemb_frames;
int loop;
int is_first_line;
};
struct nline {
enum { ANIM, TEXT } type;
union {
struct string *text;
struct animation *anim;
};
};
struct content {
struct nline **raw;
size_t n_raw;
struct nline **formatted;
size_t n_formatted;
struct anim_ref *anim_refs;
};
struct window {
WINDOW *window;
size_t cols, rows, scroll;
struct content content;
};
struct anim_ref {
size_t index;
size_t cursor_y;
size_t counter;
int running;
struct anim_ref *next;
};
size_t read_nlines(FILE *fp, struct nline ***nlines);
size_t flow_nlines(struct nline **from, size_t n_from, struct nline ***to, int width);
void print_nlines(struct nline **nlines, size_t nmemb);
void free_nlines(struct nline **nlines, size_t nmemb);
#define ANIM_HINT ";anim"
#define FRAME_HINT ";frame"
#define LOOP_HINT ";loop"
#define NOLOOP_HINT ";noloop"
// ======= old stuff:
void print_lines(struct line *lines);
void dump_sections(struct section **sections, size_t n_sections);
struct line *read_lines(FILE *fp);
struct frame *read_frames(FILE *fp);
struct section **read_sections(DIR *dir, char *dirname, int follow_links, size_t *nmemb);
struct line *flow_content(struct line *lines, int width);
void free_lines(struct line *lines);
......
/*
* This file is part of telnet-site.
*
* telnet-site 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.
*
* telnet-site 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 telnet-site. If not, see <https://www.gnu.org/licenses/>.
*/
#include "log.h"
#include <stdio.h>
#include <stdlib.h>
#include <stdarg.h>
#define LOGPATH "/tmp/telnetsite"
void errlog(char *fmt, ...) {
static FILE *fp = NULL;
if (fp == NULL) {
fp = fopen(LOGPATH, "a");
}
if (fp != NULL) {
va_list args;
va_start(args, fmt);
vfprintf(fp, fmt, args);
fflush(fp);
va_end(args);
}
}
/*
* This file is part of telnet-site.
*
* telnet-site 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.
*
* telnet-site 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 telnet-site. If not, see <https://www.gnu.org/licenses/>.
*/
#ifndef _TELNETSITE_LOG_H
#define _TELNETSITE_LOG_H
#include <stdarg.h>
void errlog(char *fmt, ...);
#endif
/*
* This file is part of telnet-site.
*
* telnet-site 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.
*
* telnet-site 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 telnet-site. If not, see <https://www.gnu.org/licenses/>.
*/
#include "site.h"
#include <unistd.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <stdlib.h>
#include <signal.h>
#include <stdio.h>
int main(int argc, char **argv) {
exit(site(argc, argv));
}
......@@ -15,9 +15,12 @@
* along with telnet-site. If not, see <https://www.gnu.org/licenses/>.
*/
#include "log.h"
#include "render.h"
#include "anim.h"
#include "log.h"
#include "data.h"
#include "site.h"
#include <string.h>
#include <ncurses.h>
......@@ -55,3 +58,29 @@ void render_content(WINDOW *window, struct line *lines) {
}
scrollok(window, 1);
}
// =========================
void render_ncontent(struct window *window) {
size_t cursor_y = 0,
i = 0;
while (i < window->content.n_formatted && cursor_y < window->rows) {
render_nline(window->window, cursor_y, window->content.formatted[i]);
if (window->content.formatted[i]->type == ANIM &&
window->content.formatted[i]->anim->is_first_line) {
push_anim_ref_back(window, i);
}
i++;
cursor_y++;
}
}
void render_nline(WINDOW *window, size_t cursor_y, struct nline *nline) {
scrollok(window, 0);
if (nline->type == TEXT) {
mvwprintw(window, cursor_y, 0, "%s", nline->text->data);
} else if (nline->type == ANIM) {
mvwprintw(window, cursor_y, 0, nline->anim->frames[nline->anim->current_frame_index].s->data);
}
scrollok(window, 1);
}
......@@ -28,4 +28,7 @@ void render_index(WINDOW *window, struct section **sections, size_t n_sections);
void render_separator(WINDOW *window, int selected_index);
void render_content(WINDOW *window, struct line *lines);
void render_ncontent(struct window *window);
void render_nline(WINDOW *window, size_t cursor_y, struct nline *nline);
#endif
......@@ -15,8 +15,11 @@
* along with telnet-site. If not, see <https://www.gnu.org/licenses/>.
*/
#include "log.h"
#include "scroll.h"
#include "anim.h"
#include "log.h"
#include "site.h"
#include "render.h"
#include "data.h"
#include <string.h>
......@@ -123,3 +126,36 @@ void scroll_separator(WINDOW *window, int dy) {
}
wrefresh(window);
}
void scroll_ncontent(struct window *window, int dy) {
if (dy == 1 && window->scroll+window->rows < window->content.n_formatted) {
wscrl(window->window, 1);
window->scroll++;
render_nline(window->window, window->rows-1, window->content.formatted[window->scroll+window->rows-1]);
if (window->content.formatted[window->scroll-1]->type == ANIM &&
(window->content.formatted[window->scroll]->type != ANIM ||
window->content.formatted[window->scroll]->anim->is_first_line)) {
pop_anim_ref_front(window);
}
if (window->content.formatted[window->scroll+window->rows-1]->type == ANIM &&
window->content.formatted[window->scroll+window->rows-1]->anim->is_first_line) {
push_anim_ref_back(window, window->scroll+window->rows-1);
}
} else if (dy == -1 && window->scroll > 0) {
wscrl(window->window, -1);
window->scroll--;
render_nline(window->window, 0, window->content.formatted[window->scroll]);
// was there an animation that went out of focus?
if (window->content.formatted[window->scroll+window->rows-1]->type == ANIM &&
window->content.formatted[window->scroll+window->rows-1]->anim->is_first_line) {
pop_anim_ref_back(window);
}
// is there a new animation that came into focus?
if (window->content.formatted[window->scroll]->type == ANIM &&
(window->content.formatted[window->scroll+1]->type != ANIM ||
window->content.formatted[window->scroll+1]->anim->is_first_line)) {
push_anim_ref_front(window, window->scroll);
}
}
wrefresh(window->window);
}
......@@ -28,4 +28,6 @@ void bot_content(WINDOW *window, struct line **content_top, struct line **conten
void scroll_index(WINDOW *window, struct section **sections, size_t n_sections, int index_scroll, int dy);
void scroll_separator(WINDOW *window, int dy);
void scroll_ncontent(struct window *window, int dy);
#endif
......@@ -15,6 +15,9 @@
* along with telnet-site. If not, see <https://www.gnu.org/licenses/>.
*/
#include "site.h"
#include "anim.h"
#include "render.h"
#include "scroll.h"
#include "data.h"
......@@ -23,9 +26,12 @@
#include <stdio.h>
#include <sys/types.h>
#include <dirent.h>
#include <poll.h>
#include <string.h>
#include <sys/stat.h>
#include <signal.h>
#include <unistd.h>
#include <sys/wait.h>
size_t get_index_width(struct section **sections, size_t n_sections) {
size_t max_len = 0;
......@@ -39,7 +45,15 @@ size_t get_index_width(struct section **sections, size_t n_sections) {
return max_len;
}
struct window index_window;
struct window separator_window;
struct window content_window;
sig_atomic_t had_winch = 0;
void winch_handler(int signo) {
fprintf(stderr, "winch\n");
had_winch = 1;
}
/*
......@@ -54,8 +68,24 @@ void winch_handler(int signo) {
* - figure out why scrolling fails over telnet (SOMETIMES????) ?
* - error handling everywhere
*/
int main(int argc, char **argv) {
int site(int argc, char **argv) {
signal(SIGWINCH, winch_handler);
//=================
/*
FILE *fp1 = fopen(argv[1], "r");
struct content c;
c.n_raw = read_nlines(fp1, &c.raw);
fclose(fp1);
c.n_formatted = flow_nlines(c.raw, c.n_raw, &c.formatted, 60);
print_nlines(c.raw, c.n_raw);
free_nlines(c.raw, c.n_raw);
fprintf(stderr, "===========================================\n");
print_nlines(c.formatted, c.n_formatted);
exit(0);
*/
//=================
int selected_index = 0;
int scroll = 0;
size_t n_sections;
......@@ -67,10 +97,12 @@ int main(int argc, char **argv) {
struct section **sections = read_sections(dir, argv[1], 1, &n_sections);
closedir(dir);
FILE *fp = fopen(sections[selected_index]->filename, "r");
sections[selected_index]->content = read_lines(fp);
content_window.content.n_raw = read_nlines(fp, &content_window.content.raw);
// sections[selected_index]->content = read_lines(fp);
fclose(fp);
initscr();
raw();
cbreak();
halfdelay(1);
noecho();
curs_set(0);
int index_left_margin = 2;
......@@ -83,51 +115,58 @@ int main(int argc, char **argv) {
int separator_left_margin = index_left_margin + index_cols;
int content_left_margin = index_left_margin + index_cols + separator_cols;
int content_right_margin = 8;
int content_rows = LINES;
int content_cols = COLS - content_left_margin - content_right_margin;
WINDOW *index_win = newwin(index_rows, index_cols, 0, index_left_margin);
WINDOW *separator_win = newwin(separator_rows, separator_cols, 0, separator_left_margin);
WINDOW *content_win = newwin(content_rows, content_cols, 0, content_left_margin);
struct line *content_head = flow_content(sections[selected_index]->content, content_cols);
struct line *content_top = content_head;
struct line *content_bot = content_head;
for (int i=0; i<content_rows-1 && content_bot->next != NULL; i++) {
content_bot = content_bot->next;
}
content_window.cols = COLS - content_left_margin - content_right_margin;
content_window.rows = LINES;
content_window.scroll = 0;
content_window.window = newwin(content_window.rows, content_window.cols, 0, content_left_margin);
content_window.content.anim_refs = NULL;
content_window.content.n_formatted = flow_nlines(content_window.content.raw,
content_window.content.n_raw,
&content_window.content.formatted,
content_window.cols);
make_scrollable(index_win);
make_scrollable(separator_win);
make_scrollable(content_win);
make_scrollable(content_window.window);
render_index(index_win, sections, n_sections);
render_separator(separator_win, selected_index+1);
render_content(content_win, content_head);
render_ncontent(&content_window);
refresh();
wrefresh(index_win);
wrefresh(separator_win);
wrefresh(content_win);
wrefresh(content_window.window);
int ch = getch();
while (ch != 'q') {
switch(ch) {
case 'J':
if (selected_index < n_sections-1) {
if (sections[selected_index]->content != NULL) {
free_lines(sections[selected_index]->content);
sections[selected_index]->content = NULL;
if (content_window.content.raw != NULL) {
free_nlines(content_window.content.raw, content_window.content.n_raw);
content_window.content.raw = NULL;
}
if (content_window.content.formatted != NULL) {
free_nlines(content_window.content.formatted, content_window.content.n_formatted);
content_window.content.formatted = NULL;
}
selected_index++;
if (sections[selected_index]->content == NULL) {
if (content_window.content.raw == NULL) {
FILE *fp = fopen(sections[selected_index]->filename, "r");
sections[selected_index]->content = read_lines(fp);
content_window.content.n_raw = read_nlines(fp, &content_window.content.raw);
fclose(fp);
}
wclear(content_win);
free_lines(content_head);
content_head = flow_content(sections[selected_index]->content, content_cols);
content_top = content_head;
content_bot = content_head;
for (int i=0; i<content_rows-1 && content_bot->next != NULL; i++) {
content_bot = content_bot->next;
if (content_window.content.formatted == NULL) {
content_window.content.n_formatted = flow_nlines(content_window.content.raw,
content_window.content.n_raw,
&content_window.content.formatted,
content_window.cols);
}
render_content(content_win, content_head);
wclear(content_window.window);
render_ncontent(&content_window);
if (selected_index + index_scroll_threshold - index_scroll >= index_rows &&
index_scroll + index_rows - 1 <= n_sections ) {
......@@ -138,9 +177,11 @@ int main(int argc, char **argv) {
}
refresh();
wrefresh(content_win);
wrefresh(content_window.window);
}
break;
/*
case 'K':
if (selected_index > 0) {
if (sections[selected_index]->content != NULL) {
......@@ -175,25 +216,40 @@ int main(int argc, char **argv) {
wrefresh(content_win);
}
break;
*/
case 'j':
scroll_content(content_win, &content_top, &content_bot, 1);
scroll_ncontent(&content_window, 1);
break;
case 'k':
scroll_content(content_win, &content_top, &content_bot, -1);
scroll_ncontent(&content_window, -1);
break;
/*
case 'g':
top_content(content_win, &content_top, &content_bot);
break;
case 'G':
bot_content(content_win, &content_top, &content_bot);
break;
*/
case ERR:
anim_tick(&content_window);
/*
* check for:
* - animation updates
* - window redraw
*/
break;
default:
break;
}
ch = getch();
}
delwin(content_win);
delwin(content_window.window);
delwin(index_win);
delwin(separator_win);
endwin();
free_lines(content_head);
return 0;
}
/*
* This file is part of telnet-site.
*
* telnet-site 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.
*
* telnet-site 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 telnet-site. If not, see <https://www.gnu.org/licenses/>.
*/
#ifndef _TELNET_SITE_SITE_H
#define _TELNET_SITE_SITE_H
#include <unistd.h>
#include <stddef.h>
int site(int argc, char **argv);
#endif
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment