add multiline support - sent - simple plaintext presentation tool | |
git clone git://git.suckless.org/sent | |
Log | |
Files | |
Refs | |
README | |
LICENSE | |
--- | |
commit 22a0a7f25596c28588bac5bf9e12cd8c534684d4 | |
parent d7eac23083be2278327e94255a182a221a0df273 | |
Author: Markus Teich <[email protected]> | |
Date: Sat, 7 Nov 2015 23:54:23 +0100 | |
add multiline support | |
Diffstat: | |
M config.def.h | 2 ++ | |
M example | 62 ++++++++++++++++++++++++++---… | |
M sent.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 * … | |
+ die("cannot realloc %u bytes:", maxlin… | |
+ } | |
+ | |
+ 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 star… | |
+ 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->l… | |
+ s->linecount++; | |
+ } while ((p = fgets(buf, sizeof(buf), fp)) && strcmp(buf, "\n"… | |
+ 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(sl… | |
- die("could not read image %s", slides[idx + 1].text + … | |
+ die("could not read image %s", slides[idx + 1].lines[0… | |
if (0 < idx && slides[idx - 1].img && !pngread(slides[idx - 1]… | |
- die("could not read image %s", slides[idx - 1].text + … | |
+ 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, he… | |
+ for (i = 0; i < slides[idx].linecount; i++) | |
+ drw_text(d, | |
+ (xw.w - width) / 2, | |
+ (xw.h - height) / 2 + i * linespacing * d->fo… | |
+ 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].… | |
+ eprintf("could not prepare image %s for drawing", slides[idx].… | |
} 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(); |