t* My version of sent
       
   URI git clone git://git.codevoid.de/sent-sdk.git
   DIR Log
   DIR Files
   DIR Refs
   DIR README
   DIR LICENSE
       ---
   DIR commit 982e80256045fcbe6e41dd057b21539480098a70
   DIR parent 72d33d463fed7ba271961a6f91cae1fed8faa454
   URI Author: Stefan Hagen <sh+git[at]codevoid[dot]de>
       Date:   Sun,  3 Nov 2019 12:17:28 +0100
       
       Progress bar patch + %retab
       
       Diffstat:
         M config.def.h                        |       3 +++
         M config.mk                           |       2 +-
         M sent.c                              |    1026 +++++++++++++++----------------
       
       3 files changed, 508 insertions(+), 523 deletions(-)
       ---
   DIR diff --git a/config.def.h b/config.def.h
       t@@ -19,6 +19,9 @@ static const float linespacing = 1.4;
        static const float usablewidth = 0.75;
        static const float usableheight = 0.75;
        
       +/* height of the presentation progress bar */
       +static const int progressheight = 5;
       +
        static Mousekey mshortcuts[] = {
                /* button         function        argument */
                { Button1,        advance,        {.i = +1} },
   DIR diff --git a/config.mk b/config.mk
       t@@ -14,7 +14,7 @@ X11LIB = /usr/X11R6/lib
        INCS = -I. -I/usr/include -I/usr/include/freetype2 -I${X11INC}
        LIBS = -L/usr/lib -lc -lm -L${X11LIB} -lXft -lfontconfig -lX11
        # OpenBSD (uncomment)
       -#INCS = -I. -I${X11INC} -I${X11INC}/freetype2
       +INCS = -I. -I${X11INC} -I${X11INC}/freetype2
        # FreeBSD (uncomment)
        #INCS = -I. -I/usr/local/include -I/usr/local/include/freetype2 -I${X11INC}
        #LIBS = -L/usr/local/lib -lc -lm -L${X11LIB} -lXft -lfontconfig -lX11
   DIR diff --git a/sent.c b/sent.c
       t@@ -31,59 +31,59 @@ char *argv0;
        #define MAXFONTSTRLEN  128
        
        typedef enum {
       -        NONE = 0,
       -        SCALED = 1,
       +    NONE = 0,
       +    SCALED = 1,
        } imgstate;
        
        typedef struct {
       -        unsigned char *buf;
       -        unsigned int bufwidth, bufheight;
       -        imgstate state;
       -        XImage *ximg;
       -        int numpasses;
       +    unsigned char *buf;
       +    unsigned int bufwidth, bufheight;
       +    imgstate state;
       +    XImage *ximg;
       +    int numpasses;
        } Image;
        
        typedef struct {
       -        char *regex;
       -        char *bin;
       +    char *regex;
       +    char *bin;
        } Filter;
        
        typedef struct {
       -        unsigned int linecount;
       -        char **lines;
       -        Image *img;
       -        char *embed;
       +    unsigned int linecount;
       +    char **lines;
       +    Image *img;
       +    char *embed;
        } Slide;
        
        /* Purely graphic info */
        typedef struct {
       -        Display *dpy;
       -        Window win;
       -        Atom wmdeletewin, netwmname;
       -        Visual *vis;
       -        XSetWindowAttributes attrs;
       -        int scr;
       -        int w, h;
       -        int uw, uh; /* usable dimensions for drawing text and images */
       +    Display *dpy;
       +    Window win;
       +    Atom wmdeletewin, netwmname;
       +    Visual *vis;
       +    XSetWindowAttributes attrs;
       +    int scr;
       +    int w, h;
       +    int uw, uh; /* usable dimensions for drawing text and images */
        } XWindow;
        
        typedef union {
       -        int i;
       -        unsigned int ui;
       -        float f;
       -        const void *v;
       +    int i;
       +    unsigned int ui;
       +    float f;
       +    const void *v;
        } Arg;
        
        typedef struct {
       -        unsigned int b;
       -        void (*func)(const Arg *);
       -        const Arg arg;
       +    unsigned int b;
       +    void (*func)(const Arg *);
       +    const Arg arg;
        } Mousekey;
        
        typedef struct {
       -        KeySym keysym;
       -        void (*func)(const Arg *);
       -        const Arg arg;
       +    KeySym keysym;
       +    void (*func)(const Arg *);
       +    const Arg arg;
        } Shortcut;
        
        static void fffree(Image *img);
       t@@ -127,580 +127,562 @@ static Fnt *fonts[NUMFONTSCALES];
        static int running = 1;
        
        static void (*handler[LASTEvent])(XEvent *) = {
       -        [ButtonPress] = bpress,
       -        [ClientMessage] = cmessage,
       -        [ConfigureNotify] = configure,
       -        [Expose] = expose,
       -        [KeyPress] = kpress,
       +    [ButtonPress] = bpress,
       +    [ClientMessage] = cmessage,
       +    [ConfigureNotify] = configure,
       +    [Expose] = expose,
       +    [KeyPress] = kpress,
        };
        
       -int
       -filter(int fd, const char *cmd)
       +int filter(int fd, const char *cmd)
        {
       -        int fds[2];
       -
       -        if (pipe(fds) < 0)
       -                die("sent: Unable to create pipe:");
       -
       -        switch (fork()) {
       -        case -1:
       -                die("sent: Unable to fork:");
       -        case 0:
       -                dup2(fd, 0);
       -                dup2(fds[1], 1);
       -                close(fds[0]);
       -                close(fds[1]);
       -                execlp("sh", "sh", "-c", cmd, (char *)0);
       -                fprintf(stderr, "sent: execlp sh -c '%s': %s\n", cmd, strerror(errno));
       -                _exit(1);
       -        }
       -        close(fds[1]);
       -        return fds[0];
       +    int fds[2];
       +
       +    if (pipe(fds) < 0)
       +        die("sent: Unable to create pipe:");
       +
       +    switch (fork()) {
       +        case -1:
       +            die("sent: Unable to fork:");
       +        case 0:
       +            dup2(fd, 0);
       +            dup2(fds[1], 1);
       +            close(fds[0]);
       +            close(fds[1]);
       +            execlp("sh", "sh", "-c", cmd, (char *)0);
       +            fprintf(stderr, "sent: execlp sh -c '%s': %s\n", cmd, strerror(errno));
       +            _exit(1);
       +    }
       +    close(fds[1]);
       +    return fds[0];
        }
        
       -void
       -fffree(Image *img)
       +void fffree(Image *img)
        {
       -        free(img->buf);
       -        if (img->ximg)
       -                XDestroyImage(img->ximg);
       -        free(img);
       +    free(img->buf);
       +    if (img->ximg)
       +        XDestroyImage(img->ximg);
       +    free(img);
        }
        
       -void
       -ffload(Slide *s)
       +void ffload(Slide *s)
        {
       -        uint32_t y, x;
       -        uint16_t *row;
       -        uint8_t opac, fg_r, fg_g, fg_b, bg_r, bg_g, bg_b;
       -        size_t rowlen, off, nbytes, i;
       -        ssize_t count;
       -        unsigned char hdr[16];
       -        char *bin = NULL;
       -        char *filename;
       -        regex_t regex;
       -        int fdin, fdout;
       -
       -        if (s->img || !(filename = s->embed) || !s->embed[0])
       -                return; /* already done */
       -
       -        for (i = 0; i < LEN(filters); i++) {
       -                if (regcomp(&regex, filters[i].regex,
       -                            REG_NOSUB | REG_EXTENDED | REG_ICASE)) {
       -                        fprintf(stderr, "sent: Invalid regex '%s'\n", filters[i].regex);
       -                        continue;
       -                }
       -                if (!regexec(&regex, filename, 0, NULL, 0)) {
       -                        bin = filters[i].bin;
       -                        regfree(&regex);
       -                        break;
       -                }
       -                regfree(&regex);
       -        }
       -        if (!bin)
       -                die("sent: Unable to find matching filter for '%s'", filename);
       -
       -        if ((fdin = open(filename, O_RDONLY)) < 0)
       -                die("sent: Unable to open '%s':", filename);
       -
       -        if ((fdout = filter(fdin, bin)) < 0)
       -                die("sent: Unable to filter '%s':", filename);
       -        close(fdin);
       -
       -        if (read(fdout, hdr, 16) != 16)
       -                die("sent: Unable to read filtered file '%s':", filename);
       -        if (memcmp("farbfeld", hdr, 8))
       -                die("sent: Filtered file '%s' has no valid farbfeld header", filename);
       -
       -        s->img = ecalloc(1, sizeof(Image));
       -        s->img->bufwidth = ntohl(*(uint32_t *)&hdr[8]);
       -        s->img->bufheight = ntohl(*(uint32_t *)&hdr[12]);
       -
       -        if (s->img->buf)
       -                free(s->img->buf);
       -        /* internally the image is stored in 888 format */
       -        s->img->buf = ecalloc(s->img->bufwidth * s->img->bufheight, strlen("888"));
       -
       -        /* scratch buffer to read row by row */
       -        rowlen = s->img->bufwidth * 2 * strlen("RGBA");
       -        row = ecalloc(1, rowlen);
       -
       -        /* extract window background color channels for transparency */
       -        bg_r = (sc[ColBg].pixel >> 16) % 256;
       -        bg_g = (sc[ColBg].pixel >>  8) % 256;
       -        bg_b = (sc[ColBg].pixel >>  0) % 256;
       -
       -        for (off = 0, y = 0; y < s->img->bufheight; y++) {
       -                nbytes = 0;
       -                while (nbytes < rowlen) {
       -                        count = read(fdout, (char *)row + nbytes, rowlen - nbytes);
       -                        if (count < 0)
       -                                die("sent: Unable to read from pipe:");
       -                        nbytes += count;
       -                }
       -                for (x = 0; x < rowlen / 2; x += 4) {
       -                        fg_r = ntohs(row[x + 0]) / 257;
       -                        fg_g = ntohs(row[x + 1]) / 257;
       -                        fg_b = ntohs(row[x + 2]) / 257;
       -                        opac = ntohs(row[x + 3]) / 257;
       -                        /* blend opaque part of image data with window background color to
       -                         * emulate transparency */
       -                        s->img->buf[off++] = (fg_r * opac + bg_r * (255 - opac)) / 255;
       -                        s->img->buf[off++] = (fg_g * opac + bg_g * (255 - opac)) / 255;
       -                        s->img->buf[off++] = (fg_b * opac + bg_b * (255 - opac)) / 255;
       -                }
       -        }
       -
       -        free(row);
       -        close(fdout);
       +    uint32_t y, x;
       +    uint16_t *row;
       +    uint8_t opac, fg_r, fg_g, fg_b, bg_r, bg_g, bg_b;
       +    size_t rowlen, off, nbytes, i;
       +    ssize_t count;
       +    unsigned char hdr[16];
       +    char *bin = NULL;
       +    char *filename;
       +    regex_t regex;
       +    int fdin, fdout;
       +
       +    if (s->img || !(filename = s->embed) || !s->embed[0])
       +        return; /* already done */
       +
       +    for (i = 0; i < LEN(filters); i++) {
       +        if (regcomp(&regex, filters[i].regex,
       +                    REG_NOSUB | REG_EXTENDED | REG_ICASE)) {
       +            fprintf(stderr, "sent: Invalid regex '%s'\n", filters[i].regex);
       +            continue;
       +        }
       +        if (!regexec(&regex, filename, 0, NULL, 0)) {
       +            bin = filters[i].bin;
       +            regfree(&regex);
       +            break;
       +        }
       +        regfree(&regex);
       +    }
       +    if (!bin)
       +        die("sent: Unable to find matching filter for '%s'", filename);
       +
       +    if ((fdin = open(filename, O_RDONLY)) < 0)
       +        die("sent: Unable to open '%s':", filename);
       +
       +    if ((fdout = filter(fdin, bin)) < 0)
       +        die("sent: Unable to filter '%s':", filename);
       +    close(fdin);
       +
       +    if (read(fdout, hdr, 16) != 16)
       +        die("sent: Unable to read filtered file '%s':", filename);
       +    if (memcmp("farbfeld", hdr, 8))
       +        die("sent: Filtered file '%s' has no valid farbfeld header", filename);
       +
       +    s->img = ecalloc(1, sizeof(Image));
       +    s->img->bufwidth = ntohl(*(uint32_t *)&hdr[8]);
       +    s->img->bufheight = ntohl(*(uint32_t *)&hdr[12]);
       +
       +    if (s->img->buf)
       +        free(s->img->buf);
       +    /* internally the image is stored in 888 format */
       +    s->img->buf = ecalloc(s->img->bufwidth * s->img->bufheight, strlen("888"));
       +
       +    /* scratch buffer to read row by row */
       +    rowlen = s->img->bufwidth * 2 * strlen("RGBA");
       +    row = ecalloc(1, rowlen);
       +
       +    /* extract window background color channels for transparency */
       +    bg_r = (sc[ColBg].pixel >> 16) % 256;
       +    bg_g = (sc[ColBg].pixel >>  8) % 256;
       +    bg_b = (sc[ColBg].pixel >>  0) % 256;
       +
       +    for (off = 0, y = 0; y < s->img->bufheight; y++) {
       +        nbytes = 0;
       +        while (nbytes < rowlen) {
       +            count = read(fdout, (char *)row + nbytes, rowlen - nbytes);
       +            if (count < 0)
       +                die("sent: Unable to read from pipe:");
       +            nbytes += count;
       +        }
       +        for (x = 0; x < rowlen / 2; x += 4) {
       +            fg_r = ntohs(row[x + 0]) / 257;
       +            fg_g = ntohs(row[x + 1]) / 257;
       +            fg_b = ntohs(row[x + 2]) / 257;
       +            opac = ntohs(row[x + 3]) / 257;
       +            /* blend opaque part of image data with window background color to
       +             * emulate transparency */
       +            s->img->buf[off++] = (fg_r * opac + bg_r * (255 - opac)) / 255;
       +            s->img->buf[off++] = (fg_g * opac + bg_g * (255 - opac)) / 255;
       +            s->img->buf[off++] = (fg_b * opac + bg_b * (255 - opac)) / 255;
       +        }
       +    }
       +
       +    free(row);
       +    close(fdout);
        }
        
       -void
       -ffprepare(Image *img)
       +void ffprepare(Image *img)
        {
       -        int depth = DefaultDepth(xw.dpy, xw.scr);
       -        int width = xw.uw;
       -        int height = xw.uh;
       +    int depth = DefaultDepth(xw.dpy, xw.scr);
       +    int width = xw.uw;
       +    int height = xw.uh;
        
       -        if (xw.uw * img->bufheight > xw.uh * img->bufwidth)
       -                width = img->bufwidth * xw.uh / img->bufheight;
       -        else
       -                height = img->bufheight * xw.uw / img->bufwidth;
       +    if (xw.uw * img->bufheight > xw.uh * img->bufwidth)
       +        width = img->bufwidth * xw.uh / img->bufheight;
       +    else
       +        height = img->bufheight * xw.uw / img->bufwidth;
        
       -        if (depth < 24)
       -                die("sent: Display color depths < 24 not supported");
       +    if (depth < 24)
       +        die("sent: Display color depths < 24 not supported");
        
       -        if (!(img->ximg = XCreateImage(xw.dpy, CopyFromParent, depth, ZPixmap, 0,
       -                                       NULL, width, height, 32, 0)))
       -                die("sent: Unable to create XImage");
       +    if (!(img->ximg = XCreateImage(xw.dpy, CopyFromParent, depth, ZPixmap, 0,
       +                    NULL, width, height, 32, 0)))
       +        die("sent: Unable to create XImage");
        
       -        img->ximg->data = ecalloc(height, img->ximg->bytes_per_line);
       -        if (!XInitImage(img->ximg))
       -                die("sent: Unable to initiate XImage");
       +    img->ximg->data = ecalloc(height, img->ximg->bytes_per_line);
       +    if (!XInitImage(img->ximg))
       +        die("sent: Unable to initiate XImage");
        
       -        ffscale(img);
       -        img->state |= SCALED;
       +    ffscale(img);
       +    img->state |= SCALED;
        }
        
       -void
       -ffscale(Image *img)
       +void ffscale(Image *img)
        {
       -        unsigned int x, y;
       -        unsigned int width = img->ximg->width;
       -        unsigned int height = img->ximg->height;
       -        char* newBuf = img->ximg->data;
       -        unsigned char* ibuf;
       -        unsigned int jdy = img->ximg->bytes_per_line / 4 - width;
       -        unsigned int dx = (img->bufwidth << 10) / width;
       -
       -        for (y = 0; y < height; y++) {
       -                unsigned int bufx = img->bufwidth / width;
       -                ibuf = &img->buf[y * img->bufheight / height * img->bufwidth * 3];
       -
       -                for (x = 0; x < width; x++) {
       -                        *newBuf++ = (ibuf[(bufx >> 10)*3+2]);
       -                        *newBuf++ = (ibuf[(bufx >> 10)*3+1]);
       -                        *newBuf++ = (ibuf[(bufx >> 10)*3+0]);
       -                        newBuf++;
       -                        bufx += dx;
       -                }
       -                newBuf += jdy;
       -        }
       +    unsigned int x, y;
       +    unsigned int width = img->ximg->width;
       +    unsigned int height = img->ximg->height;
       +    char* newBuf = img->ximg->data;
       +    unsigned char* ibuf;
       +    unsigned int jdy = img->ximg->bytes_per_line / 4 - width;
       +    unsigned int dx = (img->bufwidth << 10) / width;
       +
       +    for (y = 0; y < height; y++) {
       +        unsigned int bufx = img->bufwidth / width;
       +        ibuf = &img->buf[y * img->bufheight / height * img->bufwidth * 3];
       +
       +        for (x = 0; x < width; x++) {
       +            *newBuf++ = (ibuf[(bufx >> 10)*3+2]);
       +            *newBuf++ = (ibuf[(bufx >> 10)*3+1]);
       +            *newBuf++ = (ibuf[(bufx >> 10)*3+0]);
       +            newBuf++;
       +            bufx += dx;
       +        }
       +        newBuf += jdy;
       +    }
        }
        
       -void
       -ffdraw(Image *img)
       +void ffdraw(Image *img)
        {
       -        int xoffset = (xw.w - img->ximg->width) / 2;
       -        int yoffset = (xw.h - img->ximg->height) / 2;
       -        XPutImage(xw.dpy, xw.win, d->gc, img->ximg, 0, 0,
       -                  xoffset, yoffset, img->ximg->width, img->ximg->height);
       -        XFlush(xw.dpy);
       +    int xoffset = (xw.w - img->ximg->width) / 2;
       +    int yoffset = (xw.h - img->ximg->height) / 2;
       +    XPutImage(xw.dpy, xw.win, d->gc, img->ximg, 0, 0,
       +            xoffset, yoffset, img->ximg->width, img->ximg->height);
       +    XFlush(xw.dpy);
        }
        
       -void
       -getfontsize(Slide *s, unsigned int *width, unsigned int *height)
       +void getfontsize(Slide *s, unsigned int *width, unsigned int *height)
        {
       -        int i, j;
       -        unsigned int curw, newmax;
       -        float lfac = linespacing * (s->linecount - 1) + 1;
       -
       -        /* fit height */
       -        for (j = NUMFONTSCALES - 1; j >= 0; j--)
       -                if (fonts[j]->h * lfac <= xw.uh)
       -                        break;
       -        LIMIT(j, 0, NUMFONTSCALES - 1);
       -        drw_setfontset(d, fonts[j]);
       -
       -        /* fit width */
       -        *width = 0;
       -        for (i = 0; i < s->linecount; i++) {
       -                curw = drw_fontset_getwidth(d, s->lines[i]);
       -                newmax = (curw >= *width);
       -                while (j > 0 && curw > xw.uw) {
       -                        drw_setfontset(d, fonts[--j]);
       -                        curw = drw_fontset_getwidth(d, s->lines[i]);
       -                }
       -                if (newmax)
       -                        *width = curw;
       -        }
       -        *height = fonts[j]->h * lfac;
       +    int i, j;
       +    unsigned int curw, newmax;
       +    float lfac = linespacing * (s->linecount - 1) + 1;
       +
       +    /* fit height */
       +    for (j = NUMFONTSCALES - 1; j >= 0; j--)
       +        if (fonts[j]->h * lfac <= xw.uh)
       +            break;
       +    LIMIT(j, 0, NUMFONTSCALES - 1);
       +    drw_setfontset(d, fonts[j]);
       +
       +    /* fit width */
       +    *width = 0;
       +    for (i = 0; i < s->linecount; i++) {
       +        curw = drw_fontset_getwidth(d, s->lines[i]);
       +        newmax = (curw >= *width);
       +        while (j > 0 && curw > xw.uw) {
       +            drw_setfontset(d, fonts[--j]);
       +            curw = drw_fontset_getwidth(d, s->lines[i]);
       +        }
       +        if (newmax)
       +            *width = curw;
       +    }
       +    *height = fonts[j]->h * lfac;
        }
        
       -void
       -cleanup(int slidesonly)
       +void cleanup(int slidesonly)
        {
       -        unsigned int i, j;
       -
       -        if (!slidesonly) {
       -                for (i = 0; i < NUMFONTSCALES; i++)
       -                        drw_fontset_free(fonts[i]);
       -                free(sc);
       -                drw_free(d);
       -
       -                XDestroyWindow(xw.dpy, xw.win);
       -                XSync(xw.dpy, False);
       -                XCloseDisplay(xw.dpy);
       -        }
       -
       -        if (slides) {
       -                for (i = 0; i < slidecount; i++) {
       -                        for (j = 0; j < slides[i].linecount; j++)
       -                                free(slides[i].lines[j]);
       -                        free(slides[i].lines);
       -                        if (slides[i].img)
       -                                fffree(slides[i].img);
       -                }
       -                if (!slidesonly) {
       -                        free(slides);
       -                        slides = NULL;
       -                }
       -        }
       +    unsigned int i, j;
       +
       +    if (!slidesonly) {
       +        for (i = 0; i < NUMFONTSCALES; i++)
       +            drw_fontset_free(fonts[i]);
       +        free(sc);
       +        drw_free(d);
       +
       +        XDestroyWindow(xw.dpy, xw.win);
       +        XSync(xw.dpy, False);
       +        XCloseDisplay(xw.dpy);
       +    }
       +
       +    if (slides) {
       +        for (i = 0; i < slidecount; i++) {
       +            for (j = 0; j < slides[i].linecount; j++)
       +                free(slides[i].lines[j]);
       +            free(slides[i].lines);
       +            if (slides[i].img)
       +                fffree(slides[i].img);
       +        }
       +        if (!slidesonly) {
       +            free(slides);
       +            slides = NULL;
       +        }
       +    }
        }
        
       -void
       -reload(const Arg *arg)
       +void reload(const Arg *arg)
        {
       -        FILE *fp = NULL;
       -        unsigned int i;
       -
       -        if (!fname) {
       -                fprintf(stderr, "sent: Cannot reload from stdin. Use a file!\n");
       -                return;
       -        }
       -
       -        cleanup(1);
       -        slidecount = 0;
       -
       -        if (!(fp = fopen(fname, "r")))
       -                die("sent: Unable to open '%s' for reading:", fname);
       -        load(fp);
       -        fclose(fp);
       -
       -        LIMIT(idx, 0, slidecount-1);
       -        for (i = 0; i < slidecount; i++)
       -                ffload(&slides[i]);
       -        xdraw();
       +    FILE *fp = NULL;
       +    unsigned int i;
       +
       +    if (!fname) {
       +        fprintf(stderr, "sent: Cannot reload from stdin. Use a file!\n");
       +        return;
       +    }
       +
       +    cleanup(1);
       +    slidecount = 0;
       +
       +    if (!(fp = fopen(fname, "r")))
       +        die("sent: Unable to open '%s' for reading:", fname);
       +    load(fp);
       +    fclose(fp);
       +
       +    LIMIT(idx, 0, slidecount-1);
       +    for (i = 0; i < slidecount; i++)
       +        ffload(&slides[i]);
       +    xdraw();
        }
        
       -void
       -load(FILE *fp)
       +void load(FILE *fp)
        {
       -        static size_t size = 0;
       -        size_t blen, maxlines;
       -        char buf[BUFSIZ], *p;
       -        Slide *s;
       -
       -        /* read each line from fp and add it to the item list */
       -        while (1) {
       -                /* eat consecutive empty lines */
       -                while ((p = fgets(buf, sizeof(buf), fp)))
       -                        if (strcmp(buf, "\n") != 0 && buf[0] != '#')
       -                                break;
       -                if (!p)
       -                        break;
       -
       -                if ((slidecount+1) * sizeof(*slides) >= size)
       -                        if (!(slides = realloc(slides, (size += BUFSIZ))))
       -                                die("sent: Unable to reallocate %u bytes:", size);
       -
       -                /* 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("sent: Unable to reallocate %u bytes:", maxlines * sizeof(s->lines[0]));
       -                        }
       -
       -                        blen = strlen(buf);
       -                        if (!(s->lines[s->linecount] = strdup(buf)))
       -                                die("sent: Unable to strdup:");
       -                        if (s->lines[s->linecount][blen-1] == '\n')
       -                                s->lines[s->linecount][blen-1] = '\0';
       -
       -                        /* mark as image slide if first line of a slide starts with @ */
       -                        if (s->linecount == 0 && s->lines[0][0] == '@')
       -                                s->embed = &s->lines[0][1];
       -
       -                        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;
       -        }
       -
       -        if (!slidecount)
       -                die("sent: No slides in file");
       +    static size_t size = 0;
       +    size_t blen, maxlines;
       +    char buf[BUFSIZ], *p;
       +    Slide *s;
       +
       +    /* read each line from fp and add it to the item list */
       +    while (1) {
       +        /* eat consecutive empty lines */
       +        while ((p = fgets(buf, sizeof(buf), fp)))
       +            if (strcmp(buf, "\n") != 0 && buf[0] != '#')
       +                break;
       +        if (!p)
       +            break;
       +
       +        if ((slidecount+1) * sizeof(*slides) >= size)
       +            if (!(slides = realloc(slides, (size += BUFSIZ))))
       +                die("sent: Unable to reallocate %u bytes:", size);
       +
       +        /* 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("sent: Unable to reallocate %u bytes:", maxlines * sizeof(s->lines[0]));
       +            }
       +
       +            blen = strlen(buf);
       +            if (!(s->lines[s->linecount] = strdup(buf)))
       +                die("sent: Unable to strdup:");
       +            if (s->lines[s->linecount][blen-1] == '\n')
       +                s->lines[s->linecount][blen-1] = '\0';
       +
       +            /* mark as image slide if first line of a slide starts with @ */
       +            if (s->linecount == 0 && s->lines[0][0] == '@')
       +                s->embed = &s->lines[0][1];
       +
       +            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;
       +    }
       +
       +    if (!slidecount)
       +        die("sent: No slides in file");
        }
        
       -void
       -advance(const Arg *arg)
       +void advance(const Arg *arg)
        {
       -        int new_idx = idx + arg->i;
       -        LIMIT(new_idx, 0, slidecount-1);
       -        if (new_idx != idx) {
       -                if (slides[idx].img)
       -                        slides[idx].img->state &= ~SCALED;
       -                idx = new_idx;
       -                xdraw();
       -        }
       +    int new_idx = idx + arg->i;
       +    LIMIT(new_idx, 0, slidecount-1);
       +    if (new_idx != idx) {
       +        if (slides[idx].img)
       +            slides[idx].img->state &= ~SCALED;
       +        idx = new_idx;
       +        xdraw();
       +    }
        }
        
       -void
       -quit(const Arg *arg)
       +void quit(const Arg *arg)
        {
       -        running = 0;
       +    running = 0;
        }
        
       -void
       -resize(int width, int height)
       +void resize(int width, int height)
        {
       -        xw.w = width;
       -        xw.h = height;
       -        xw.uw = usablewidth * width;
       -        xw.uh = usableheight * height;
       -        drw_resize(d, width, height);
       +    xw.w = width;
       +    xw.h = height;
       +    xw.uw = usablewidth * width;
       +    xw.uh = usableheight * height;
       +    drw_resize(d, width, height);
        }
        
       -void
       -run()
       +void run()
        {
       -        XEvent ev;
       -
       -        /* Waiting for window mapping */
       -        while (1) {
       -                XNextEvent(xw.dpy, &ev);
       -                if (ev.type == ConfigureNotify) {
       -                        resize(ev.xconfigure.width, ev.xconfigure.height);
       -                } else if (ev.type == MapNotify) {
       -                        break;
       -                }
       -        }
       -
       -        while (running) {
       -                XNextEvent(xw.dpy, &ev);
       -                if (handler[ev.type])
       -                        (handler[ev.type])(&ev);
       -        }
       +    XEvent ev;
       +
       +    /* Waiting for window mapping */
       +    while (1) {
       +        XNextEvent(xw.dpy, &ev);
       +        if (ev.type == ConfigureNotify) {
       +            resize(ev.xconfigure.width, ev.xconfigure.height);
       +        } else if (ev.type == MapNotify) {
       +            break;
       +        }
       +    }
       +
       +    while (running) {
       +        XNextEvent(xw.dpy, &ev);
       +        if (handler[ev.type])
       +            (handler[ev.type])(&ev);
       +    }
        }
        
       -void
       -xdraw()
       +void xdraw()
        {
       -        unsigned int height, width, i;
       -        Image *im = slides[idx].img;
       -
       -        getfontsize(&slides[idx], &width, &height);
       -        XClearWindow(xw.dpy, xw.win);
       -
       -        if (!im) {
       -                drw_rect(d, 0, 0, xw.w, xw.h, 1, 1);
       -                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,
       -                                 0,
       -                                 slides[idx].lines[i],
       -                                 0);
       -                drw_map(d, xw.win, 0, 0, xw.w, xw.h);
       -        } else {
       -                if (!(im->state & SCALED))
       -                        ffprepare(im);
       -                ffdraw(im);
       -        }
       +    unsigned int height, width, i;
       +    Image *im = slides[idx].img;
       +
       +    getfontsize(&slides[idx], &width, &height);
       +    XClearWindow(xw.dpy, xw.win);
       +
       +    if (!im) {
       +        drw_rect(d, 0, 0, xw.w, xw.h, 1, 1);
       +        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,
       +                    0,
       +                    slides[idx].lines[i],
       +                    0);
       +        if (idx != 0 && progressheight != 0) {
       +            drw_rect(d,
       +                    0, xw.h - progressheight,
       +                    (xw.w * idx)/(slidecount - 1), progressheight,
       +                    1, 0);
       +        }
       +        drw_map(d, xw.win, 0, 0, xw.w, xw.h);
       +    } else {
       +        if (!(im->state & SCALED))
       +            ffprepare(im);
       +        ffdraw(im);
       +    }
        }
        
       -void
       -xhints()
       +void xhints()
        {
       -        XClassHint class = {.res_name = "sent", .res_class = "presenter"};
       -        XWMHints wm = {.flags = InputHint, .input = True};
       -        XSizeHints *sizeh = NULL;
       +    XClassHint class = {.res_name = "sent", .res_class = "presenter"};
       +    XWMHints wm = {.flags = InputHint, .input = True};
       +    XSizeHints *sizeh = NULL;
        
       -        if (!(sizeh = XAllocSizeHints()))
       -                die("sent: Unable to allocate size hints");
       +    if (!(sizeh = XAllocSizeHints()))
       +        die("sent: Unable to allocate size hints");
        
       -        sizeh->flags = PSize;
       -        sizeh->height = xw.h;
       -        sizeh->width = xw.w;
       +    sizeh->flags = PSize;
       +    sizeh->height = xw.h;
       +    sizeh->width = xw.w;
        
       -        XSetWMProperties(xw.dpy, xw.win, NULL, NULL, NULL, 0, sizeh, &wm, &class);
       -        XFree(sizeh);
       +    XSetWMProperties(xw.dpy, xw.win, NULL, NULL, NULL, 0, sizeh, &wm, &class);
       +    XFree(sizeh);
        }
        
       -void
       -xinit()
       +void xinit()
        {
       -        XTextProperty prop;
       -        unsigned int i;
       -
       -        if (!(xw.dpy = XOpenDisplay(NULL)))
       -                die("sent: Unable to open display");
       -        xw.scr = XDefaultScreen(xw.dpy);
       -        xw.vis = XDefaultVisual(xw.dpy, xw.scr);
       -        resize(DisplayWidth(xw.dpy, xw.scr), DisplayHeight(xw.dpy, xw.scr));
       -
       -        xw.attrs.bit_gravity = CenterGravity;
       -        xw.attrs.event_mask = KeyPressMask | ExposureMask | StructureNotifyMask |
       -                              ButtonMotionMask | ButtonPressMask;
       -
       -        xw.win = XCreateWindow(xw.dpy, XRootWindow(xw.dpy, xw.scr), 0, 0,
       -                               xw.w, xw.h, 0, XDefaultDepth(xw.dpy, xw.scr),
       -                               InputOutput, xw.vis, CWBitGravity | CWEventMask,
       -                               &xw.attrs);
       -
       -        xw.wmdeletewin = XInternAtom(xw.dpy, "WM_DELETE_WINDOW", False);
       -        xw.netwmname = XInternAtom(xw.dpy, "_NET_WM_NAME", False);
       -        XSetWMProtocols(xw.dpy, xw.win, &xw.wmdeletewin, 1);
       -
       -        if (!(d = drw_create(xw.dpy, xw.scr, xw.win, xw.w, xw.h)))
       -                die("sent: Unable to create drawing context");
       -        sc = drw_scm_create(d, colors, 2);
       -        drw_setscheme(d, sc);
       -        XSetWindowBackground(xw.dpy, xw.win, sc[ColBg].pixel);
       -
       -        xloadfonts();
       -        for (i = 0; i < slidecount; i++)
       -                ffload(&slides[i]);
       -
       -        XStringListToTextProperty(&argv0, 1, &prop);
       -        XSetWMName(xw.dpy, xw.win, &prop);
       -        XSetTextProperty(xw.dpy, xw.win, &prop, xw.netwmname);
       -        XFree(prop.value);
       -        XMapWindow(xw.dpy, xw.win);
       -        xhints();
       -        XSync(xw.dpy, False);
       +    XTextProperty prop;
       +    unsigned int i;
       +
       +    if (!(xw.dpy = XOpenDisplay(NULL)))
       +        die("sent: Unable to open display");
       +    xw.scr = XDefaultScreen(xw.dpy);
       +    xw.vis = XDefaultVisual(xw.dpy, xw.scr);
       +    resize(DisplayWidth(xw.dpy, xw.scr), DisplayHeight(xw.dpy, xw.scr));
       +
       +    xw.attrs.bit_gravity = CenterGravity;
       +    xw.attrs.event_mask = KeyPressMask | ExposureMask | StructureNotifyMask |
       +        ButtonMotionMask | ButtonPressMask;
       +
       +    xw.win = XCreateWindow(xw.dpy, XRootWindow(xw.dpy, xw.scr), 0, 0,
       +            xw.w, xw.h, 0, XDefaultDepth(xw.dpy, xw.scr),
       +            InputOutput, xw.vis, CWBitGravity | CWEventMask,
       +            &xw.attrs);
       +
       +    xw.wmdeletewin = XInternAtom(xw.dpy, "WM_DELETE_WINDOW", False);
       +    xw.netwmname = XInternAtom(xw.dpy, "_NET_WM_NAME", False);
       +
       +    XSetWMProtocols(xw.dpy, xw.win, &xw.wmdeletewin, 1);
       +
       +    if (!(d = drw_create(xw.dpy, xw.scr, xw.win, xw.w, xw.h)))
       +        die("sent: Unable to create drawing context");
       +    sc = drw_scm_create(d, colors, 2);
       +    drw_setscheme(d,