sent

simple plaintext presentation tool
git clone git://git.suckless.org/sent
Log | Files | Refs | README | LICENSE

commit 22a0a7f25596c28588bac5bf9e12cd8c534684d4
parent d7eac23083be2278327e94255a182a221a0df273
Author: Markus Teich <markus.teich@stusta.mhn.de>
Date:   Sat,  7 Nov 2015 23:54:23 +0100

add multiline support

Diffstat:
Mconfig.def.h | 2++
Mexample | 62+++++++++++++++++++++++++++++++++++++++++++++++++++++---------
Msent.c | 133++++++++++++++++++++++++++++++++++++++++++++++++++++++-------------------------
3 files changed, 146 insertions(+), 51 deletions(-)

diff --git a/config.def.h b/config.def.h @@ -11,6 +11,8 @@ static char *fontfallbacks[] = { static const char *fgcol = "#000000"; static const char *bgcol = "#FFFFFF"; +static const float linespacing = 1.4; + /* how much screen estate is to be used at max for the content */ static const float usablewidth = 0.75; static const float usableheight = 0.75; diff --git a/example b/example @@ -1,14 +1,58 @@ sent -takahashi -why? + +Origin: + Takahashi + +Why? +• PPTX sucks +• LATEX sucks +• PDF sucks + +also: +terminal presentations +don't support images… + @nyan.png +this text will not be displayed, since the @ at the start of the first line +makes this paragraph an image slide. + easy to use -depends on Xlib, libpng + +depends on +♽ Xlib +☢ libpng + ~1000 lines of code -how? -sent FILENAME -one slide per line + +usage: +$ sent FILE1 [FILE2 …] + +▸ one slide per paragraph +▸ lines starting with # are ignored +▸ paragraphs starting with a @ line are png images +▸ for an empty slide just use a \ as a paragraph + # This is a comment and will not be part of the presentation -# The next line starts with a whitespace, it will not produce an image slide - @FILE.png -thanks / questions? + +# multiple empty lines between paragraphs are also ignored + + +# The following lines should produce +# one empty slide + + + +\ +\ + +\@this_line_actually_started_with_a_\.png +\#This line as well +⇒ Prepend a backslash to kill behaviour of special characters + +😀😁😂😃😄😅😆😇😈😉😊😋😌😍😎😏 +😐😑😒😓😔😕😖😗😘😙😚😛😜😝😞😟 +😠😡😢😣😥😦😧😨😩😪😫😭😮😯😰😱 +😲😳😴😵😶😷😸😹😺😻😼😽😾😿🙀☠ + +thanks +questions? diff --git a/sent.c b/sent.c @@ -43,7 +43,8 @@ typedef struct { } Image; typedef struct { - char *text; + unsigned int linecount; + char **lines; Image *img; } Slide; @@ -85,7 +86,7 @@ static int pngprepare(Image *img); static void pngscale(Image *img); static void pngdraw(Image *img); -static void getfontsize(char *str, unsigned int *width, unsigned int *height); +static void getfontsize(Slide *s, unsigned int *width, unsigned int *height); static void cleanup(); static void eprintf(const char *, ...); static void die(const char *, ...); @@ -149,7 +150,7 @@ Image *pngopen(char *filename) return NULL; } if (!(img->info_ptr = png_create_info_struct(img->png_ptr)) - || setjmp(png_jmpbuf(img->png_ptr))) { + || setjmp(png_jmpbuf(img->png_ptr))) { pngfree(img); return NULL; } @@ -315,28 +316,38 @@ void pngdraw(Image *img) img->state |= DRAWN; } -void getfontsize(char *str, unsigned int *width, unsigned int *height) +void getfontsize(Slide *s, unsigned int *width, unsigned int *height) { - size_t i; + size_t i, j; + unsigned int curw, imax; + float lfac = linespacing * (s->linecount - 1) + 1; - for (i = 0; i < NUMFONTSCALES; i++) { - drw_setfontset(d, fonts[i]); - *height = fonts[i]->h; - *width = drw_fontset_getwidth(d, str); - if (*width > xw.uw || *height > xw.uh) + /* fit height */ + for (j = NUMFONTSCALES - 1; j >= 0; j--) + if (fonts[j]->h * lfac <= xw.uh) break; + drw_setfontset(d, fonts[j]); + + /* fit width */ + *width = 0; + for (i = 0; i < s->linecount; i++) { + curw = drw_fontset_getwidth(d, s->lines[i]); + if (curw >= *width) + imax = i; + while (j >= 0 && curw > xw.uw) { + drw_setfontset(d, fonts[--j]); + curw = drw_fontset_getwidth(d, s->lines[i]); + } + if (imax == i) + *width = curw; } - if (i > 0) { - drw_setfontset(d, fonts[i-1]); - *height = fonts[i-1]->h; - *width = drw_fontset_getwidth(d, str); - } - *width += d->fonts->h; + *height = fonts[j]->h * lfac; + *width += fonts[j]->h; } void cleanup() { - unsigned int i; + unsigned int i, j; for (i = 0; i < NUMFONTSCALES; i++) drw_fontset_free(fonts[i]); @@ -348,8 +359,9 @@ void cleanup() XCloseDisplay(xw.dpy); if (slides) { for (i = 0; i < slidecount; i++) { - if (slides[i].text) - free(slides[i].text); + for (j = 0; j < slides[i].linecount; j++) + free(slides[i].lines[j]); + free(slides[i].lines); if (slides[i].img) pngfree(slides[i].img); } @@ -394,27 +406,57 @@ void eprintf(const char *fmt, ...) void load(FILE *fp) { static size_t size = 0; + size_t blen, maxlines; char buf[BUFSIZ], *p; - size_t i = slidecount; + Slide *s; /* read each line from fp and add it to the item list */ - while (fgets(buf, sizeof(buf), fp)) { - if ((i+1) * sizeof(*slides) >= size) + while (1) { + if ((slidecount+1) * sizeof(*slides) >= size) if (!(slides = realloc(slides, (size += BUFSIZ)))) die("cannot realloc %u bytes:", size); - if (*buf == '#') - continue; - if ((p = strchr(buf, '\n'))) - *p = '\0'; - if (!(slides[i].text = strdup(buf))) - die("cannot strdup %u bytes:", strlen(buf)+1); - if (slides[i].text[0] == '@') - slides[i].img = pngopen(slides[i].text + 1); - else - slides[i].img = 0; - i++; + + /* eat consecutive empty lines */ + while ((p = fgets(buf, sizeof(buf), fp))) + if (strcmp(buf, "\n") != 0 && buf[0] != '#') + break; + if (!p) + break; + + /* read one slide */ + maxlines = 0; + memset((s = &slides[slidecount]), 0, sizeof(Slide)); + do { + if (buf[0] == '#') + continue; + + /* grow lines array */ + if (s->linecount >= maxlines) { + maxlines = 2 * s->linecount + 1; + if (!(s->lines = realloc(s->lines, maxlines * sizeof(s->lines[0])))) + die("cannot realloc %u bytes:", maxlines * sizeof(s->lines[0])); + } + + blen = strlen(buf); + if (!(s->lines[s->linecount] = strdup(buf))) + die("cannot strdup:"); + if (s->lines[s->linecount][blen-1] == '\n') + s->lines[s->linecount][blen-1] = '\0'; + + /* only make image slide if first line of a slide starts with @ */ + if (s->linecount == 0 && s->lines[0][0] == '@') { + memmove(s->lines[0], &s->lines[0][1], blen); + s->img = pngopen(s->lines[0]); + } + + if (s->lines[s->linecount][0] == '\\') + memmove(s->lines[s->linecount], &s->lines[s->linecount][1], blen); + s->linecount++; + } while ((p = fgets(buf, sizeof(buf), fp)) && strcmp(buf, "\n") != 0); + slidecount++; + if (!p) + break; } - slidecount = i; } void advance(const Arg *arg) @@ -427,9 +469,9 @@ void advance(const Arg *arg) idx = new_idx; xdraw(); if (slidecount > idx + 1 && slides[idx + 1].img && !pngread(slides[idx + 1].img)) - die("could not read image %s", slides[idx + 1].text + 1); + die("could not read image %s", slides[idx + 1].lines[0]); if (0 < idx && slides[idx - 1].img && !pngread(slides[idx - 1].img)) - die("could not read image %s", slides[idx - 1].text + 1); + die("could not read image %s", slides[idx - 1].lines[0]); } } @@ -476,20 +518,27 @@ void usage() void xdraw() { - unsigned int height, width; + unsigned int height, width, i; Image *im = slides[idx].img; - getfontsize(slides[idx].text, &width, &height); + getfontsize(&slides[idx], &width, &height); XClearWindow(xw.dpy, xw.win); if (!im) { drw_rect(d, 0, 0, xw.w, xw.h, 1, 1); - drw_text(d, (xw.w - width) / 2, (xw.h - height) / 2, width, height, slides[idx].text, 0); + for (i = 0; i < slides[idx].linecount; i++) + drw_text(d, + (xw.w - width) / 2, + (xw.h - height) / 2 + i * linespacing * d->fonts->h, + width, + d->fonts->h, + slides[idx].lines[i], + 0); drw_map(d, xw.win, 0, 0, xw.w, xw.h); } else if (!(im->state & LOADED) && !pngread(im)) { - eprintf("could not read image %s", slides[idx].text + 1); + eprintf("could not read image %s", slides[idx].lines[0]); } else if (!(im->state & SCALED) && !pngprepare(im)) { - eprintf("could not prepare image %s for drawing", slides[idx].text + 1); + eprintf("could not prepare image %s for drawing", slides[idx].lines[0]); } else if (!(im->state & DRAWN)) { pngdraw(im); } @@ -634,7 +683,7 @@ int main(int argc, char *argv[]) } } - if (!slides || !slides[0].text) + if (!slides || !slides[0].lines) usage(); xinit();