commit - 9a25f829e319067e29c27e63e54be2ed86d8491d
commit + 65d9b3ca4af593a5d167621ecf713fcf1974b197
blob - 9b6cc8523d58871663b3a93f05e6daf88a10f206
blob + 731893b48ab311b07be28cc9781bb004cec09e22
--- Makefile.am
+++ Makefile.am
ui.c \
url.c \
url.h \
- util.c
+ util.c \
+ wrap.c
LDADD = $(LIBOBJS)
blob - 2aa38854e1fc49c79cfdb069ab33fc5086a0c448
blob + 7edd041d9229b57421b328977b7c462705f23732
--- gemini.c
+++ gemini.c
#include <assert.h>
#include <ctype.h>
#include <errno.h>
-#include <event.h>
#include <netdb.h>
#include <stdarg.h>
#include <stdio.h>
blob - 1cfaeef114ef2fdd2b03e11a08ad16dbf39b8bd5
blob + c988b657fc27872b89876faefae0da1b1c6ffcd1
--- telescope.c
+++ telescope.c
#include <err.h>
#include <errno.h>
-#include <event.h>
#include <signal.h>
#include <stdio.h>
#include <stdlib.h>
blob - 1e2b21f9f2433b16dae018ed58cdcfe771ecfd74
blob + 8feb4c5a1a23363dfdec4de402b93406a725ffef
--- telescope.h
+++ telescope.h
#include "compat.h"
+#include <event.h>
+
#include "url.h"
#define MIN(a, b) ((a) < (b) ? (a) : (b))
TAILQ_ENTRY(hist) entries;
};
-struct ui_state;
+struct ui_state {
+ int curs_x;
+ int curs_y;
+ size_t line_off;
+ size_t line_max;
+ short loading_anim;
+ short loading_anim_step;
+ struct event loadingev;
+
+ TAILQ_HEAD(, vline) head;
+};
+
extern TAILQ_HEAD(tabshead, tab) tabshead;
struct tab {
struct parser page;
char meta[GEMINI_URL_LEN];
int redirect_count;
- struct ui_state *s;
+ struct ui_state s;
};
struct proto {
char *telescope_strnchr(char*, char, size_t);
int has_prefix(const char*, const char*);
+/* wrap.c */
+void wrap_text(struct tab*, const char*, struct line*, size_t);
+int hardwrap_text(struct tab*, struct line*, size_t);
+
#endif /* TELESCOPE_H */
blob - 61bb4b2e8b25cd4f5da39cbaa94eb2e26a22c764
blob + 99bc5fce8931cffd9c0e9136f8b5990114aea823
--- ui.c
+++ ui.c
/*
* Ncurses UI for telescope.
*
- *
- * Text wrapping
- * =============
- *
- * There's a simple text wrapping algorithm.
- *
- * 1. if it's a line in a pre-formatted block:
- * a. hard wrap.
- * b. repeat
- * 2. there is enough room for the next word?
- * a. yes: render it
- * b. no: break the current line.
- * i. while there isn't enough space to draw the current
- * word, hard-wrap it
- * ii. draw the remainder of the current word (can be the
- * the entirely)
- * 3. render the spaces after the word
- * a. but if there is not enough room break the line and
- * forget them
- * 4. repeat
- *
- *
* Text scrolling
* ==============
*
static struct event stdioev, winchev;
static void load_default_keys(void);
-static int push_line(struct tab*, const struct line*, const char*, size_t, int);
static void empty_vlist(struct tab*);
static void restore_cursor(struct tab *);
static void dispatch_stdio(int, short, void*);
static void handle_clear_minibuf(int, short, void*);
static void handle_resize(int, short, void*);
-static int word_bourdaries(const char*, const char*, const char**, const char**);
-static void wrap_text(struct tab*, const char*, struct line*);
-static int hardwrap_text(struct tab*, struct line*);
static int wrap_page(struct tab*);
static void print_vline(struct vline*);
static void redraw_tabline(void);
static uint32_t tab_counter;
-struct ui_state {
- int curs_x;
- int curs_y;
- size_t line_off;
- size_t line_max;
-
- short loading_anim;
- short loading_anim_step;
- struct event loadingev;
-
- TAILQ_HEAD(, vline) head;
-};
-
static char keybuf[64];
struct kmap global_map,
[LINE_PRE_END] = { 0 },
};
+static void
+empty_vlist(struct tab *tab)
+{
+ struct vline *vl, *t;
+
+ tab->s.line_max = 0;
+
+ TAILQ_FOREACH_SAFE(vl, &tab->s.head, vlines, t) {
+ TAILQ_REMOVE(&tab->s.head, vl, vlines);
+ free(vl->line);
+ free(vl);
+ }
+}
+
static inline void
global_set_key(const char *key, void (*fn)(struct tab*))
{
minibuffer_set_key("M-n", cmd_mini_next_history_element);
minibuffer_set_key("<up>", cmd_mini_previous_history_element);
minibuffer_set_key("<down>", cmd_mini_next_history_element);
-}
-
-static int
-push_line(struct tab *tab, const struct line *l, const char *buf, size_t len, int cont)
-{
- struct vline *vl;
-
- tab->s->line_max++;
-
- if ((vl = calloc(1, sizeof(*vl))) == NULL)
- return 0;
-
- if (len != 0 && (vl->line = calloc(1, len+1)) == NULL) {
- free(vl);
- return 0;
- }
-
- vl->parent = l;
- if (len != 0)
- memcpy(vl->line, buf, len);
- vl->flags = cont;
-
- if (TAILQ_EMPTY(&tab->s->head))
- TAILQ_INSERT_HEAD(&tab->s->head, vl, vlines);
- else
- TAILQ_INSERT_TAIL(&tab->s->head, vl, vlines);
- return 1;
-}
-
-static void
-empty_vlist(struct tab *tab)
-{
- struct vline *vl, *t;
-
- tab->s->line_max = 0;
-
- TAILQ_FOREACH_SAFE(vl, &tab->s->head, vlines, t) {
- TAILQ_REMOVE(&tab->s->head, vl, vlines);
- free(vl->line);
- free(vl);
- }
}
static void
restore_cursor(struct tab *tab)
{
- wmove(body, tab->s->curs_y, tab->s->curs_x);
+ wmove(body, tab->s.curs_y, tab->s.curs_x);
}
static void
cmd_previous_line(struct tab *tab)
{
- if (--tab->s->curs_y < 0) {
- tab->s->curs_y = 0;
+ if (--tab->s.curs_y < 0) {
+ tab->s.curs_y = 0;
cmd_scroll_line_up(tab);
}
static void
cmd_next_line(struct tab *tab)
{
- if (tab->s->line_off + tab->s->curs_y >= tab->s->line_max)
+ if (tab->s.line_off + tab->s.curs_y >= tab->s.line_max)
return;
- if (++tab->s->curs_y > body_lines-1) {
- tab->s->curs_y = body_lines-1;
+ if (++tab->s.curs_y > body_lines-1) {
+ tab->s.curs_y = body_lines-1;
cmd_scroll_line_down(tab);
}
static void
cmd_forward_char(struct tab *tab)
{
- tab->s->curs_x = MIN(body_cols-1, tab->s->curs_x+1);
+ tab->s.curs_x = MIN(body_cols-1, tab->s.curs_x+1);
restore_cursor(tab);
}
static void
cmd_backward_char(struct tab *tab)
{
- tab->s->curs_x = MAX(0, tab->s->curs_x-1);
+ tab->s.curs_x = MAX(0, tab->s.curs_x-1);
restore_cursor(tab);
}
static void
cmd_move_beginning_of_line(struct tab *tab)
{
- tab->s->curs_x = 0;
+ tab->s.curs_x = 0;
restore_cursor(tab);
}
size_t off;
const char *prfx;
- off = tab->s->line_off + tab->s->curs_y;
- if (off >= tab->s->line_max) {
- tab->s->curs_x = 0;
+ off = tab->s.line_off + tab->s.curs_y;
+ if (off >= tab->s.line_max) {
+ tab->s.curs_x = 0;
goto end;
}
vl = nth_line(tab, off);
if (vl->line != NULL)
- tab->s->curs_x = strlen(vl->line);
+ tab->s.curs_x = strlen(vl->line);
else
- tab->s->curs_x = 0;
+ tab->s.curs_x = 0;
prfx = line_prefixes[vl->parent->type].prfx1;
- tab->s->curs_x += strlen(prfx);
+ tab->s.curs_x += strlen(prfx);
end:
restore_cursor(tab);
{
struct vline *vl;
- if (tab->s->line_off == 0)
+ if (tab->s.line_off == 0)
return;
- vl = nth_line(tab, --tab->s->line_off);
+ vl = nth_line(tab, --tab->s.line_off);
wscrl(body, -1);
wmove(body, 0, 0);
print_vline(vl);
struct vline *vl;
size_t n;
- if (tab->s->line_max == 0 || tab->s->line_off == tab->s->line_max-1)
+ if (tab->s.line_max == 0 || tab->s.line_off == tab->s.line_max-1)
return;
- tab->s->line_off++;
+ tab->s.line_off++;
wscrl(body, 1);
- if (tab->s->line_max - tab->s->line_off < body_lines)
+ if (tab->s.line_max - tab->s.line_off < body_lines)
return;
- vl = nth_line(tab, tab->s->line_off + body_lines-1);
+ vl = nth_line(tab, tab->s.line_off + body_lines-1);
wmove(body, body_lines-1, 0);
print_vline(vl);
}
static void
cmd_beginning_of_buffer(struct tab *tab)
{
- tab->s->line_off = 0;
- tab->s->curs_y = 0;
+ tab->s.line_off = 0;
+ tab->s.curs_y = 0;
redraw_body(tab);
}
{
ssize_t off;
- off = tab->s->line_max - body_lines;
+ off = tab->s.line_max - body_lines;
off = MAX(0, off);
- tab->s->line_off = off;
- tab->s->curs_y = MIN(body_lines, tab->s->line_max);
+ tab->s.line_off = off;
+ tab->s.curs_y = MIN(body_lines, tab->s.line_max);
redraw_body(tab);
}
struct vline *vl;
size_t nth;
- nth = tab->s->line_off + tab->s->curs_y;
- if (nth >= tab->s->line_max)
+ nth = tab->s.line_off + tab->s.curs_y;
+ if (nth >= tab->s.line_max)
return;
vl = nth_line(tab, nth);
if (vl->parent->type != LINE_LINK)
struct vline *vl;
size_t nth;
- nth = tab->s->line_off + tab->s->curs_y;
- if (nth > tab->s->line_max)
+ nth = tab->s.line_off + tab->s.curs_y;
+ if (nth > tab->s.line_max)
return;
vl = nth_line(tab, nth);
if (vl->parent->type != LINE_LINK)
TAILQ_REMOVE(&tabshead, tab, tabs);
- free(tab->s);
free(tab);
}
size_t i;
i = 0;
- TAILQ_FOREACH(vl, &tab->s->head, vlines) {
+ TAILQ_FOREACH(vl, &tab->s.head, vlines) {
if (i == n)
return vl;
i++;
wrap_page(tab);
redraw_tab(tab);
-}
-
-/*
- * Helper function for wrap_text. Find the end of the current word
- * and the end of the separator after the word.
- */
-static int
-word_boundaries(const char *s, const char *sep, const char **endword, const char **endspc)
-{
- *endword = s;
- *endword = s;
-
- if (*s == '\0')
- return 0;
-
- /* find the end of the current world */
- for (; *s != '\0'; ++s) {
- if (strchr(sep, *s) != NULL)
- break;
- }
-
- *endword = s;
-
- /* find the end of the separator */
- for (; *s != '\0'; ++s) {
- if (strchr(sep, *s) == NULL)
- break;
- }
-
- *endspc = s;
-
- return 1;
}
-static inline int
-emitline(struct tab *tab, size_t zero, size_t *off, const struct line *l,
- const char **line, int *cont)
-{
- if (!push_line(tab, l, *line, *off - zero, *cont))
- return 0;
- if (!*cont)
- *cont = 1;
- *line += *off - zero;
- *off = zero;
- return 1;
-}
-
-static inline void
-emitstr(const char **s, size_t len, size_t *off)
-{
- size_t i;
-
- /* printw("%*s", ...) doesn't seem to respect the precision, so... */
- for (i = 0; i < len; ++i)
- addch((*s)[i]);
- *off += len;
- *s += len;
-}
-
-/*
- * Build a list of visual line by wrapping the given line, assuming
- * that when printed will have a leading prefix prfx.
- *
- * TODO: it considers each byte one cell on the screen!
- */
-static void
-wrap_text(struct tab *tab, const char *prfx, struct line *l)
-{
- size_t zero, off, len, split;
- int cont = 0;
- const char *endword, *endspc, *line, *linestart;
-
- zero = strlen(prfx);
- off = zero;
- line = l->line;
- linestart = l->line;
-
- while (word_boundaries(line, " \t-", &endword, &endspc)) {
- len = endword - line;
- if (off + len >= body_cols) {
- emitline(tab, zero, &off, l, &linestart, &cont);
- while (len >= body_cols) {
- /* hard wrap */
- emitline(tab, zero, &off, l, &linestart, &cont);
- len -= body_cols-1;
- line += body_cols-1;
- }
-
- if (len != 0)
- off += len;
- } else
- off += len;
-
- /* print the spaces iff not at bol */
- len = endspc - endword;
- /* line = endspc; */
- if (off != zero) {
- if (off + len >= body_cols) {
- emitline(tab, zero, &off, l, &linestart, &cont);
- linestart = endspc;
- } else
- off += len;
- }
-
- line = endspc;
- }
-
- emitline(tab, zero, &off, l, &linestart, &cont);
-}
-
static int
-hardwrap_text(struct tab *tab, struct line *l)
-{
- size_t off, len;
- int cont;
- const char *linestart;
-
- if (l->line == NULL)
- return emitline(tab, 0, &off, l, &linestart, &cont);
-
- len = strlen(l->line);
- off = 0;
- linestart = l->line;
-
- while (len >= COLS) {
- len -= COLS-1;
- off = COLS-1;
- if (!emitline(tab, 0, &off, l, &linestart, &cont))
- return 0;
- }
-
- if (len != 0)
- return emitline(tab, 0, &len, l, &linestart, &cont);
-
- return 1;
-}
-
-static int
wrap_page(struct tab *tab)
{
struct line *l;
case LINE_TITLE_3:
case LINE_ITEM:
case LINE_QUOTE:
- wrap_text(tab, prfx, l);
- break;
case LINE_PRE_START:
case LINE_PRE_END:
- push_line(tab, l, NULL, 0, 0);
+ wrap_text(tab, prfx, l, body_cols);
+ /* push_line(tab, l, NULL, 0, 0); */
break;
case LINE_PRE_CONTENT:
- hardwrap_text(tab, l);
+ hardwrap_text(tab, l, body_cols);
break;
}
}
wmove(modeline, 0, 0);
wprintw(modeline, "-%c %s-mode ",
- spin[tab->s->loading_anim_step], mode);
+ spin[tab->s.loading_anim_step], mode);
- pct = (tab->s->line_off + tab->s->curs_y) * 100.0 / tab->s->line_max;
+ pct = (tab->s.line_off + tab->s.curs_y) * 100.0 / tab->s.line_max;
- if (tab->s->line_max <= body_lines)
+ if (tab->s.line_max <= body_lines)
wprintw(modeline, "All ");
- else if (tab->s->line_off == 0)
+ else if (tab->s.line_off == 0)
wprintw(modeline, "Top ");
- else if (tab->s->line_off + body_lines >= tab->s->line_max)
+ else if (tab->s.line_off + body_lines >= tab->s.line_max)
wprintw(modeline, "Bottom ");
else
wprintw(modeline, "%.0f%% ", pct);
wprintw(modeline, "%d/%d %s ",
- tab->s->line_off + tab->s->curs_y,
- tab->s->line_max,
+ tab->s.line_off + tab->s.curs_y,
+ tab->s.line_max,
tab->hist_cur->h);
getyx(modeline, y, x);
werase(body);
- tab->s->line_off = MIN(tab->s->line_max, tab->s->line_off);
- if (TAILQ_EMPTY(&tab->s->head))
+ tab->s.line_off = MIN(tab->s.line_max-1, tab->s.line_off);
+ if (TAILQ_EMPTY(&tab->s.head))
return;
line = 0;
- vl = nth_line(tab, tab->s->line_off);
+ vl = nth_line(tab, tab->s.line_off);
for (; vl != NULL; vl = TAILQ_NEXT(vl, vlines)) {
wmove(body, line, 0);
print_vline(vl);
static void
start_loading_anim(struct tab *tab)
{
- if (tab->s->loading_anim)
+ if (tab->s.loading_anim)
return;
- tab->s->loading_anim = 1;
- evtimer_set(&tab->s->loadingev, update_loading_anim, tab);
- evtimer_add(&tab->s->loadingev, &loadingev_timer);
+ tab->s.loading_anim = 1;
+ evtimer_set(&tab->s.loadingev, update_loading_anim, tab);
+ evtimer_add(&tab->s.loadingev, &loadingev_timer);
}
static void
{
struct tab *tab = d;
- tab->s->loading_anim_step = (tab->s->loading_anim_step+1)%4;
+ tab->s.loading_anim_step = (tab->s.loading_anim_step+1)%4;
redraw_modeline(tab);
wrefresh(modeline);
if (in_minibuffer)
wrefresh(minibuf);
- evtimer_add(&tab->s->loadingev, &loadingev_timer);
+ evtimer_add(&tab->s.loadingev, &loadingev_timer);
}
static void
stop_loading_anim(struct tab *tab)
{
- if (!tab->s->loading_anim)
+ if (!tab->s.loading_anim)
return;
- evtimer_del(&tab->s->loadingev);
- tab->s->loading_anim = 0;
- tab->s->loading_anim_step = 0;
+ evtimer_del(&tab->s.loadingev);
+ tab->s.loading_anim = 0;
+ tab->s.loading_anim_step = 0;
redraw_modeline(tab);
start_loading_anim(tab);
load_url(tab, url);
- tab->s->curs_x = 0;
- tab->s->curs_y = 0;
+ tab->s.curs_x = 0;
+ tab->s.curs_y = 0;
redraw_tab(tab);
}
TAILQ_INIT(&tab->hist.head);
- if ((tab->s = calloc(1, sizeof(*t->s))) == NULL)
- goto err;
+ TAILQ_INIT(&tab->s.head);
- TAILQ_INIT(&tab->s->head);
-
tab->id = tab_counter++;
switch_to_tab(tab);
blob - /dev/null
blob + 6062df3edff54d64e0b131bf27d583583bccfc85 (mode 644)
--- /dev/null
+++ wrap.c
+/*
+ * Copyright (c) 2021 Omar Polo <op@omarpolo.com>
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#include "telescope.h"
+
+#include <stdlib.h>
+#include <string.h>
+
+/*
+ * Text wrapping
+ * =============
+ *
+ * There's a simple text wrapping algorithm.
+ *
+ * 1. if it's a line in a pre-formatted block:
+ * a. hard wrap.
+ * b. repeat
+ * 2. there is enough room for the next word?
+ * a. yes: render it
+ * b. no: break the current line.
+ * i. while there isn't enough space to draw the current
+ * word, hard-wrap it
+ * ii. draw the remainder of the current word (can be the
+ * the entirely)
+ * 3. render the spaces after the word
+ * a. but if there is not enough room break the line and
+ * forget them
+ * 4. repeat
+ *
+ */
+
+static int
+push_line(struct tab *tab, const struct line *l, const char *buf, size_t len, int cont)
+{
+ struct vline *vl;
+
+ tab->s.line_max++;
+
+ if ((vl = calloc(1, sizeof(*vl))) == NULL)
+ return 0;
+
+ if (len != 0 && (vl->line = calloc(1, len+1)) == NULL) {
+ free(vl);
+ return 0;
+ }
+
+ vl->parent = l;
+ if (len != 0)
+ memcpy(vl->line, buf, len);
+ vl->flags = cont;
+
+ if (TAILQ_EMPTY(&tab->s.head))
+ TAILQ_INSERT_HEAD(&tab->s.head, vl, vlines);
+ else
+ TAILQ_INSERT_TAIL(&tab->s.head, vl, vlines);
+ return 1;
+}
+
+/*
+ * Helper function for wrap_text. Find the end of the current word
+ * and the end of the separator after the word.
+ */
+static int
+word_boundaries(const char *s, const char *sep, const char **endword, const char **endspc)
+{
+ *endword = s;
+ *endword = s;
+
+ if (*s == '\0')
+ return 0;
+
+ /* find the end of the current world */
+ for (; *s != '\0'; ++s) {
+ if (strchr(sep, *s) != NULL)
+ break;
+ }
+
+ *endword = s;
+
+ /* find the end of the separator */
+ for (; *s != '\0'; ++s) {
+ if (strchr(sep, *s) == NULL)
+ break;
+ }
+
+ *endspc = s;
+
+ return 1;
+}
+
+static inline int
+emitline(struct tab *tab, size_t zero, size_t *off, const struct line *l,
+ const char **line, int *cont)
+{
+ if (!push_line(tab, l, *line, *off - zero, *cont))
+ return 0;
+ if (!*cont)
+ *cont = 1;
+ *line += *off - zero;
+ *off = zero;
+ return 1;
+}
+
+/*
+ * Build a list of visual line by wrapping the given line, assuming
+ * that when printed will have a leading prefix prfx.
+ *
+ * TODO: it considers each byte one cell on the screen!
+ */
+void
+wrap_text(struct tab *tab, const char *prfx, struct line *l, size_t width)
+{
+ size_t zero, off, len, split;
+ int cont = 0;
+ const char *endword, *endspc, *line, *linestart;
+
+ zero = strlen(prfx);
+ off = zero;
+ line = l->line;
+ linestart = l->line;
+
+ if (line == NULL) {
+ push_line(tab, l, NULL, 0, 0);
+ return;
+ }
+
+ while (word_boundaries(line, " \t-", &endword, &endspc)) {
+ len = endword - line;
+ if (off + len >= width) {
+ emitline(tab, zero, &off, l, &linestart, &cont);
+ while (len >= width) {
+ /* hard wrap */
+ emitline(tab, zero, &off, l, &linestart, &cont);
+ len -= width-1;
+ line += width-1;
+ }
+
+ if (len != 0)
+ off += len;
+ } else
+ off += len;
+
+ /* print the spaces iff not at bol */
+ len = endspc - endword;
+ /* line = endspc; */
+ if (off != zero) {
+ if (off + len >= width) {
+ emitline(tab, zero, &off, l, &linestart, &cont);
+ linestart = endspc;
+ } else
+ off += len;
+ }
+
+ line = endspc;
+ }
+
+ emitline(tab, zero, &off, l, &linestart, &cont);
+}
+
+int
+hardwrap_text(struct tab *tab, struct line *l, size_t width)
+{
+ size_t off, len;
+ int cont;
+ const char *linestart;
+
+ if (l->line == NULL)
+ return emitline(tab, 0, &off, l, &linestart, &cont);
+
+ len = strlen(l->line);
+ off = 0;
+ linestart = l->line;
+
+ while (len >= width) {
+ len -= width-1;
+ off = width-1;
+ if (!emitline(tab, 0, &off, l, &linestart, &cont))
+ return 0;
+ }
+
+ if (len != 0)
+ return emitline(tab, 0, &len, l, &linestart, &cont);
+
+ return 1;
+}