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 8e81a9be0097719bfb30d7f5802d5e72fbfc2fa6
   DIR parent 982e80256045fcbe6e41dd057b21539480098a70
   URI Author: c0dev0id <sh+github[at]codevoid[dot]de>
       Date:   Tue,  1 Dec 2020 20:41:55 +0100
       
       rework patches
       
       Diffstat:
         D .gitignore                          |      30 ------------------------------
         A config.h                            |      59 +++++++++++++++++++++++++++++++
         A patches/sent-bilinearscaling-1.0.d… |      88 +++++++++++++++++++++++++++++++
         A patches/sent-options-20190213-72d3… |      72 +++++++++++++++++++++++++++++++
         A patches/sent-progress-bar-1.0.diff  |      31 +++++++++++++++++++++++++++++++
         M sent.1                              |      11 +++++++++++
         M sent.c                              |    1084 +++++++++++++++++--------------
       
       7 files changed, 841 insertions(+), 534 deletions(-)
       ---
   DIR diff --git a/.gitignore b/.gitignore
       t@@ -1,30 +0,0 @@
       -# Object files
       -*.o
       -*.ko
       -*.obj
       -*.elf
       -
       -# Libraries
       -*.lib
       -*.a
       -
       -# Shared objects (inc. Windows DLLs)
       -*.dll
       -*.so
       -*.so.*
       -*.dylib
       -
       -# Executables
       -*.exe
       -*.out
       -*.app
       -*.i*86
       -*.x86_64
       -*.hex
       -/sent
       -
       -# vim
       -*.swp
       -*~
       -
       -config.h
   DIR diff --git a/config.h b/config.h
       t@@ -0,0 +1,59 @@
       +/* See LICENSE file for copyright and license details. */
       +
       +static char *fontfallbacks[] = {
       +        "dejavu sans",
       +        "roboto",
       +        "ubuntu",
       +};
       +#define NUMFONTSCALES 42
       +#define FONTSZ(x) ((int)(10.0 * powf(1.1288, (x)))) /* x in [0, NUMFONTSCALES-1] */
       +
       +static const char *colors[] = {
       +        "#226622", /* foreground color */
       +        "#080808", /* background color */
       +};
       +
       +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;
       +
       +/* height of the presentation progress bar */
       +static const int progressheight = 12;
       +
       +static Mousekey mshortcuts[] = {
       +        /* button         function        argument */
       +        { Button1,        advance,        {.i = +1} },
       +        { Button3,        advance,        {.i = -1} },
       +        { Button4,        advance,        {.i = -1} },
       +        { Button5,        advance,        {.i = +1} },
       +};
       +
       +static Shortcut shortcuts[] = {
       +        /* keysym         function        argument */
       +        { XK_Escape,      quit,           {0} },
       +        { XK_q,           quit,           {0} },
       +        { XK_Right,       advance,        {.i = +1} },
       +        { XK_Left,        advance,        {.i = -1} },
       +        { XK_Return,      advance,        {.i = +1} },
       +        { XK_space,       advance,        {.i = +1} },
       +        { XK_BackSpace,   advance,        {.i = -1} },
       +        { XK_l,           advance,        {.i = +1} },
       +        { XK_h,           advance,        {.i = -1} },
       +        { XK_j,           advance,        {.i = +1} },
       +        { XK_k,           advance,        {.i = -1} },
       +        { XK_Down,        advance,        {.i = +1} },
       +        { XK_Up,          advance,        {.i = -1} },
       +        { XK_Next,        advance,        {.i = +1} },
       +        { XK_Prior,       advance,        {.i = -1} },
       +        { XK_n,           advance,        {.i = +1} },
       +        { XK_p,           advance,        {.i = -1} },
       +        { XK_r,           reload,         {0} },
       +};
       +
       +static Filter filters[] = {
       +        { "\\.ff$", "cat" },
       +        { "\\.ff.bz2$", "bunzip2" },
       +        { "\\.[a-z0-9]+$", "2ff" },
       +};
   DIR diff --git a/patches/sent-bilinearscaling-1.0.diff b/patches/sent-bilinearscaling-1.0.diff
       t@@ -0,0 +1,88 @@
       +diff --git a/sent.c b/sent.c
       +index 0da2bff..d92cf3b 100644
       +--- a/sent.c
       ++++ b/sent.c
       +@@ -282,27 +282,66 @@ ffprepare(Image *img)
       +         img->state |= SCALED;
       + }
       + 
       ++static unsigned char double_to_uchar_clamp255(double dbl)
       ++{
       ++        dbl = round(dbl);
       ++
       ++        return
       ++                (dbl < 0.0)   ? 0 :
       ++                (dbl > 255.0) ? 255 : (unsigned char)dbl;
       ++}
       ++
       ++static int int_clamp(int integer, int lower, int upper)
       ++{
       ++        if (integer < lower)
       ++                return lower;
       ++        else if (integer >= upper)
       ++                return upper - 1;
       ++        else
       ++                return integer;
       ++}
       ++
       + 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]);
       ++        const unsigned width = img->ximg->width;
       ++        const unsigned height = img->ximg->height;
       ++        unsigned char* newBuf = (unsigned char*)img->ximg->data;
       ++        const unsigned jdy = img->ximg->bytes_per_line / 4 - width;
       ++
       ++        const double x_scale = ((double)img->bufwidth/(double)width);
       ++        const double y_scale = ((double)img->bufheight/(double)height);
       ++
       ++        for (unsigned y = 0; y < height; ++y) {
       ++                const double old_y = (double)y * y_scale;
       ++                const double y_factor = ceil(old_y) - old_y;
       ++                const int old_y_int_0 = int_clamp((int)floor(old_y), 0, img->bufheight);
       ++                const int old_y_int_1 = int_clamp((int)ceil(old_y), 0, img->bufheight);
       ++
       ++                for (unsigned x = 0; x < width; ++x) {
       ++                        const double old_x = (double)x * x_scale;
       ++                        const double x_factor = ceil(old_x) - old_x;
       ++                        const int old_x_int_0 = int_clamp((int)floor(old_x), 0, img->bufwidth);
       ++                        const int old_x_int_1 = int_clamp((int)ceil(old_x), 0, img->bufwidth);
       ++
       ++                        const unsigned c00_pos = 3*((old_x_int_0) + ((old_y_int_0)*img->bufwidth));
       ++                        const unsigned c01_pos = 3*((old_x_int_0) + ((old_y_int_1)*img->bufwidth));
       ++                        const unsigned c10_pos = 3*((old_x_int_1) + ((old_y_int_0)*img->bufwidth));
       ++                        const unsigned c11_pos = 3*((old_x_int_1) + ((old_y_int_1)*img->bufwidth));
       ++
       ++                        for (int i = 2; i >= 0 ; --i) {
       ++                                const unsigned char c00 = img->buf[c00_pos + i];
       ++                                const unsigned char c01 = img->buf[c01_pos + i];
       ++                                const unsigned char c10 = img->buf[c10_pos + i];
       ++                                const unsigned char c11 = img->buf[c11_pos + i];
       ++
       ++                                const double x_result_0 = (double)c00*x_factor + (double)c10*(1.0 - x_factor);
       ++                                const double x_result_1 = (double)c01*x_factor + (double)c11*(1.0 - x_factor);
       ++                                const double result = x_result_0*y_factor + x_result_1*(1.0 - y_factor);
       ++
       ++                                *newBuf++ = double_to_uchar_clamp255(result);
       ++                        }
       +                         newBuf++;
       +-                        bufx += dx;
       +                 }
       +                 newBuf += jdy;
       +         }
   DIR diff --git a/patches/sent-options-20190213-72d33d4.diff b/patches/sent-options-20190213-72d33d4.diff
       t@@ -0,0 +1,72 @@
       +From 3a348cc15a97df8e8784b129800293dcfba28f3f Mon Sep 17 00:00:00 2001
       +From: Sunur Efe Vural <efe@efe.kim>
       +Date: Wed, 13 Feb 2019 14:28:17 -0500
       +Subject: [PATCH] Commandline Options
       +
       +A simple patch that adds extra commandline options to sent.
       +---
       + sent.1 | 11 +++++++++++
       + sent.c | 11 ++++++++++-
       + 2 files changed, 21 insertions(+), 1 deletion(-)
       +
       +diff --git a/sent.1 b/sent.1
       +index fabc614..5d55bf4 100644
       +--- a/sent.1
       ++++ b/sent.1
       +@@ -5,6 +5,9 @@
       + .Nd simple plaintext presentation tool
       + .Sh SYNOPSIS
       + .Nm
       ++.Op Fl f Ar font
       ++.Op Fl c Ar fgcolor
       ++.Op Fl b Ar bgcolor
       + .Op Fl v
       + .Op Ar file
       + .Sh DESCRIPTION
       +@@ -21,6 +24,14 @@ few minutes.
       + .Bl -tag -width Ds
       + .It Fl v
       + Print version information to stdout and exit.
       ++.It Fl f Ar font
       ++Defines the
       ++.Ar font
       ++when sent is run.
       ++.It Fl c Ar fgcolor
       ++Defines the foreground color when sent is run.
       ++.It Fl b Ar bgcolor
       ++Defines the background color when sent is run.
       + .El
       + .Sh USAGE
       + .Bl -tag -width Ds
       +diff --git a/sent.c b/sent.c
       +index c50a572..0b36e32 100644
       +--- a/sent.c
       ++++ b/sent.c
       +@@ -675,7 +675,7 @@ configure(XEvent *e)
       + void
       + usage()
       + {
       +-        die("usage: %s [file]", argv0);
       ++        die("usage: %s [-c fgcolor] [-b bgcolor] [-f font] [file]", argv0);
       + }
       + 
       + int
       +@@ -687,6 +687,15 @@ main(int argc, char *argv[])
       +         case 'v':
       +                 fprintf(stderr, "sent-"VERSION"\n");
       +                 return 0;
       ++        case 'f':
       ++                fontfallbacks[0] = EARGF(usage());
       ++                break;
       ++        case 'c':
       ++                colors[0] = EARGF(usage());
       ++                break;
       ++        case 'b':
       ++                colors[1] = EARGF(usage());
       ++                break;
       +         default:
       +                 usage();
       +         } ARGEND
       +-- 
       +2.20.1
       +
   DIR diff --git a/patches/sent-progress-bar-1.0.diff b/patches/sent-progress-bar-1.0.diff
       t@@ -0,0 +1,31 @@
       +diff --git a/config.def.h b/config.def.h
       +index 60eb376..25d89e0 100644
       +--- a/config.def.h
       ++++ b/config.def.h
       +@@ -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} },
       +diff --git a/sent.c b/sent.c
       +index c50a572..046466e 100644
       +--- a/sent.c
       ++++ b/sent.c
       +@@ -533,6 +533,12 @@ xdraw()
       +                                  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))
   DIR diff --git a/sent.1 b/sent.1
       t@@ -5,6 +5,9 @@
        .Nd simple plaintext presentation tool
        .Sh SYNOPSIS
        .Nm
       +.Op Fl f Ar font
       +.Op Fl c Ar fgcolor
       +.Op Fl b Ar bgcolor
        .Op Fl v
        .Op Ar file
        .Sh DESCRIPTION
       t@@ -21,6 +24,14 @@ few minutes.
        .Bl -tag -width Ds
        .It Fl v
        Print version information to stdout and exit.
       +.It Fl f Ar font
       +Defines the
       +.Ar font
       +when sent is run.
       +.It Fl c Ar fgcolor
       +Defines the foreground color when sent is run.
       +.It Fl b Ar bgcolor
       +Defines the background color when sent is run.
        .El
        .Sh USAGE
        .Bl -tag -width Ds
   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,562 +127,638 @@ 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)
       +static unsigned char double_to_uchar_clamp255(double dbl)
        {
       -    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;
       -    }
       +        dbl = round(dbl);
       +
       +        return
       +                (dbl < 0.0)   ? 0 :
       +                (dbl > 255.0) ? 255 : (unsigned char)dbl;
       +}
       +
       +static int int_clamp(int integer, int lower, int upper)
       +{
       +        if (integer < lower)
       +                return lower;
       +        else if (integer >= upper)
       +                return upper - 1;
       +        else
       +                return integer;
       +}
       +
       +void
       +ffscale(Image *img)
       +{
       +        const unsigned width = img->ximg->width;
       +        const unsigned height = img->ximg->height;
       +        unsigned char* newBuf = (unsigned char*)img->ximg->data;
       +        const unsigned jdy = img->ximg->bytes_per_line / 4 - width;
       +
       +        const double x_scale = ((double)img->bufwidth/(double)width);
       +        const double y_scale = ((double)img->bufheight/(double)height);
       +
       +        for (unsigned y = 0; y < height; ++y) {
       +                const double old_y = (double)y * y_scale;
       +                const double y_factor = ceil(old_y) - old_y;
       +                const int old_y_int_0 = int_clamp((int)floor(old_y), 0, img->bufheight);
       +                const int old_y_int_1 = int_clamp((int)ceil(old_y), 0, img->bufheight);
       +
       +                for (unsigned x = 0; x < width; ++x) {
       +                        const double old_x = (double)x * x_scale;
       +                        const double x_factor = ceil(old_x) - old_x;
       +                        const int old_x_int_0 = int_clamp((int)floor(old_x), 0, img->bufwidth);
       +                        const int old_x_int_1 = int_clamp((int)ceil(old_x), 0, img->bufwidth);
       +
       +                        const unsigned c00_pos = 3*((old_x_int_0) + ((old_y_int_0)*img->bufwidth));
       +                        const unsigned c01_pos = 3*((old_x_int_0) + ((old_y_int_1)*img->bufwidth));
       +                        const unsigned c10_pos = 3*((old_x_int_1) + ((old_y_int_0)*img->bufwidth));
       +                        const unsigned c11_pos = 3*((old_x_int_1) + ((old_y_int_1)*img->bufwidth));
       +
       +                        for (int i = 2; i >= 0 ; --i) {
       +                                const unsigned char c00 = img->buf[c00_pos + i];
       +                                const unsigned char c01 = img->buf[c01_pos + i];
       +                                const unsigned char c10 = img->buf[c10_pos + i];
       +                                const unsigned char c11 = img->buf[c11_pos + i];
       +
       +                                const double x_result_0 = (double)c00*x_factor + (double)c10*(1.0 - x_factor);
       +                                const double x_result_1 = (double)c01*x_factor + (double)c11*(1.0 - x_factor);
       +                                const double result = x_result_0*y_factor + x_result_1*(1.0 - y_factor);
       +
       +                                *newBuf++ = double_to_uchar_clamp255(result);
       +                        }
       +                        newBuf++;
       +                }
       +                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 (