st-undercurl-0.8.4-20210822.diff - sites - public wiki contents of suckless.org
 (HTM) git clone git://git.suckless.org/sites
 (DIR) Log
 (DIR) Files
 (DIR) Refs
       ---
       st-undercurl-0.8.4-20210822.diff (16126B)
       ---
            1 diff --git a/config.def.h b/config.def.h
            2 index 6f05dce..7ae1b92 100644
            3 --- a/config.def.h
            4 +++ b/config.def.h
            5 @@ -470,3 +470,27 @@ static char ascii_printable[] =
            6          " !\"#$%&'()*+,-./0123456789:;<=>?"
            7          "@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_"
            8          "`abcdefghijklmnopqrstuvwxyz{|}~";
            9 +
           10 +/**
           11 + * Undercurl style. Set UNDERCURL_STYLE to one of the available styles.
           12 + *
           13 + * Curly: Dunno how to draw it *shrug*
           14 + *  _   _   _   _
           15 + * ( ) ( ) ( ) ( )
           16 + *         (_) (_) (_) (_)
           17 + *
           18 + * Spiky:
           19 + * /\  /\   /\        /\
           20 + *   \/  \/          \/
           21 + *
           22 + * Capped:
           23 + *        _     _     _
           24 + * / \   / \   / \
           25 + *    \_/   \_/
           26 + */
           27 +// Available styles
           28 +#define UNDERCURL_CURLY 0
           29 +#define UNDERCURL_SPIKY 1
           30 +#define UNDERCURL_CAPPED 2
           31 +// Active style
           32 +#define UNDERCURL_STYLE UNDERCURL_SPIKY
           33 diff --git a/st.c b/st.c
           34 index 76b7e0d..542ab3a 100644
           35 --- a/st.c
           36 +++ b/st.c
           37 @@ -33,6 +33,7 @@
           38  #define UTF_SIZ       4
           39  #define ESC_BUF_SIZ   (128*UTF_SIZ)
           40  #define ESC_ARG_SIZ   16
           41 +#define CAR_PER_ARG   4
           42  #define STR_BUF_SIZ   ESC_BUF_SIZ
           43  #define STR_ARG_SIZ   ESC_ARG_SIZ
           44  
           45 @@ -139,6 +140,7 @@ typedef struct {
           46          int arg[ESC_ARG_SIZ];
           47          int narg;              /* nb of args */
           48          char mode[2];
           49 +        int carg[ESC_ARG_SIZ][CAR_PER_ARG]; /* colon args */
           50  } CSIEscape;
           51  
           52  /* STR Escape sequence structs */
           53 @@ -159,6 +161,7 @@ static void ttywriteraw(const char *, size_t);
           54  
           55  static void csidump(void);
           56  static void csihandle(void);
           57 +static void readcolonargs(char **, int, int[][CAR_PER_ARG]);
           58  static void csiparse(void);
           59  static void csireset(void);
           60  static int eschandle(uchar);
           61 @@ -1131,6 +1134,28 @@ tnewline(int first_col)
           62          tmoveto(first_col ? 0 : term.c.x, y);
           63  }
           64  
           65 +void
           66 +readcolonargs(char **p, int cursor, int params[][CAR_PER_ARG])
           67 +{
           68 +        int i = 0;
           69 +        for (; i < CAR_PER_ARG; i++)
           70 +                params[cursor][i] = -1;
           71 +
           72 +        if (**p != ':')
           73 +                return;
           74 +
           75 +        char *np = NULL;
           76 +        i = 0;
           77 +
           78 +        while (**p == ':' && i < CAR_PER_ARG) {
           79 +                while (**p == ':')
           80 +                        (*p)++;
           81 +                params[cursor][i] = strtol(*p, &np, 10);
           82 +                *p = np;
           83 +                i++;
           84 +        }
           85 +}
           86 +
           87  void
           88  csiparse(void)
           89  {
           90 @@ -1153,6 +1178,7 @@ csiparse(void)
           91                          v = -1;
           92                  csiescseq.arg[csiescseq.narg++] = v;
           93                  p = np;
           94 +                readcolonargs(&p, csiescseq.narg-1, csiescseq.carg);
           95                  if (*p != ';' || csiescseq.narg == ESC_ARG_SIZ)
           96                          break;
           97                  p++;
           98 @@ -1369,6 +1395,10 @@ tsetattr(int *attr, int l)
           99                                  ATTR_STRUCK     );
          100                          term.c.attr.fg = defaultfg;
          101                          term.c.attr.bg = defaultbg;
          102 +                        term.c.attr.ustyle = -1;
          103 +                        term.c.attr.ucolor[0] = -1;
          104 +                        term.c.attr.ucolor[1] = -1;
          105 +                        term.c.attr.ucolor[2] = -1;
          106                          break;
          107                  case 1:
          108                          term.c.attr.mode |= ATTR_BOLD;
          109 @@ -1380,7 +1410,14 @@ tsetattr(int *attr, int l)
          110                          term.c.attr.mode |= ATTR_ITALIC;
          111                          break;
          112                  case 4:
          113 -                        term.c.attr.mode |= ATTR_UNDERLINE;
          114 +                        term.c.attr.ustyle = csiescseq.carg[i][0];
          115 +
          116 +                        if (term.c.attr.ustyle != 0)
          117 +                                term.c.attr.mode |= ATTR_UNDERLINE;
          118 +                        else
          119 +                                term.c.attr.mode &= ~ATTR_UNDERLINE;
          120 +
          121 +                        term.c.attr.mode ^= ATTR_DIRTYUNDERLINE;
          122                          break;
          123                  case 5: /* slow blink */
          124                          /* FALLTHROUGH */
          125 @@ -1431,6 +1468,18 @@ tsetattr(int *attr, int l)
          126                  case 49:
          127                          term.c.attr.bg = defaultbg;
          128                          break;
          129 +                case 58:
          130 +                        term.c.attr.ucolor[0] = csiescseq.carg[i][1];
          131 +                        term.c.attr.ucolor[1] = csiescseq.carg[i][2];
          132 +                        term.c.attr.ucolor[2] = csiescseq.carg[i][3];
          133 +                        term.c.attr.mode ^= ATTR_DIRTYUNDERLINE;
          134 +                        break;
          135 +                case 59:
          136 +                        term.c.attr.ucolor[0] = -1;
          137 +                        term.c.attr.ucolor[1] = -1;
          138 +                        term.c.attr.ucolor[2] = -1;
          139 +                        term.c.attr.mode ^= ATTR_DIRTYUNDERLINE;
          140 +                        break;
          141                  default:
          142                          if (BETWEEN(attr[i], 30, 37)) {
          143                                  term.c.attr.fg = attr[i] - 30;
          144 diff --git a/st.h b/st.h
          145 index 3d351b6..95bdcbd 100644
          146 --- a/st.h
          147 +++ b/st.h
          148 @@ -34,6 +34,7 @@ enum glyph_attribute {
          149          ATTR_WIDE       = 1 << 9,
          150          ATTR_WDUMMY     = 1 << 10,
          151          ATTR_BOLD_FAINT = ATTR_BOLD | ATTR_FAINT,
          152 +        ATTR_DIRTYUNDERLINE = 1 << 15,
          153  };
          154  
          155  enum selection_mode {
          156 @@ -65,6 +66,8 @@ typedef struct {
          157          ushort mode;      /* attribute flags */
          158          uint32_t fg;      /* foreground  */
          159          uint32_t bg;      /* background  */
          160 +        int ustyle;          /* underline style */
          161 +        int ucolor[3];    /* underline color */
          162  } Glyph;
          163  
          164  typedef Glyph *Line;
          165 diff --git a/st.info b/st.info
          166 index 8201ad6..659878c 100644
          167 --- a/st.info
          168 +++ b/st.info
          169 @@ -1,4 +1,5 @@
          170  st-mono| simpleterm monocolor,
          171 +        Su,
          172          acsc=+C\,D-A.B0E``aaffgghFiGjjkkllmmnnooppqqrrssttuuvvwwxxyyzz{{||}}~~,
          173          am,
          174          bce,
          175 diff --git a/x.c b/x.c
          176 index 210f184..3a0e79e 100644
          177 --- a/x.c
          178 +++ b/x.c
          179 @@ -45,6 +45,14 @@ typedef struct {
          180          signed char appcursor; /* application cursor */
          181  } Key;
          182  
          183 +/* Undercurl slope types */
          184 +enum undercurl_slope_type {
          185 +        UNDERCURL_SLOPE_ASCENDING = 0,
          186 +        UNDERCURL_SLOPE_TOP_CAP = 1,
          187 +        UNDERCURL_SLOPE_DESCENDING = 2,
          188 +        UNDERCURL_SLOPE_BOTTOM_CAP = 3
          189 +};
          190 +
          191  /* X modifiers */
          192  #define XK_ANY_MOD    UINT_MAX
          193  #define XK_NO_MOD     0
          194 @@ -1339,6 +1347,51 @@ xmakeglyphfontspecs(XftGlyphFontSpec *specs, const Glyph *glyphs, int len, int x
          195          return numspecs;
          196  }
          197  
          198 +static int isSlopeRising (int x, int iPoint, int waveWidth)
          199 +{
          200 +        //    .     .     .     .
          201 +        //   / \   / \   / \   / \
          202 +        //  /   \ /   \ /   \ /   \
          203 +        // .     .     .     .     .
          204 +
          205 +        // Find absolute `x` of point
          206 +        x += iPoint * (waveWidth/2);
          207 +
          208 +        // Find index of absolute wave
          209 +        int absSlope = x / ((float)waveWidth/2);
          210 +
          211 +        return (absSlope % 2);
          212 +}
          213 +
          214 +static int getSlope (int x, int iPoint, int waveWidth)
          215 +{
          216 +        // Sizes: Caps are half width of slopes
          217 +        //    1_2       1_2       1_2      1_2
          218 +        //   /   \     /   \     /   \    /   \
          219 +        //  /     \   /     \   /     \  /     \
          220 +        // 0       3_0       3_0      3_0       3_
          221 +        // <2->    <1>         <---6---->
          222 +
          223 +        // Find type of first point
          224 +        int firstType;
          225 +        x -= (x / waveWidth) * waveWidth;
          226 +        if (x < (waveWidth * (2.f/6.f)))
          227 +                firstType = UNDERCURL_SLOPE_ASCENDING;
          228 +        else if (x < (waveWidth * (3.f/6.f)))
          229 +                firstType = UNDERCURL_SLOPE_TOP_CAP;
          230 +        else if (x < (waveWidth * (5.f/6.f)))
          231 +                firstType = UNDERCURL_SLOPE_DESCENDING;
          232 +        else
          233 +                firstType = UNDERCURL_SLOPE_BOTTOM_CAP;
          234 +
          235 +        // Find type of given point
          236 +        int pointType = (iPoint % 4);
          237 +        pointType += firstType;
          238 +        pointType %= 4;
          239 +
          240 +        return pointType;
          241 +}
          242 +
          243  void
          244  xdrawglyphfontspecs(const XftGlyphFontSpec *specs, Glyph base, int len, int x, int y)
          245  {
          246 @@ -1461,8 +1514,357 @@ xdrawglyphfontspecs(const XftGlyphFontSpec *specs, Glyph base, int len, int x, i
          247  
          248          /* Render underline and strikethrough. */
          249          if (base.mode & ATTR_UNDERLINE) {
          250 -                XftDrawRect(xw.draw, fg, winx, winy + dc.font.ascent + 1,
          251 -                                width, 1);
          252 +                // Underline Color
          253 +                const int widthThreshold  = 28; // +1 width every widthThreshold px of font
          254 +                int wlw = (win.ch / widthThreshold) + 1; // Wave Line Width
          255 +                int linecolor;
          256 +                if ((base.ucolor[0] >= 0) &&
          257 +                        !(base.mode & ATTR_BLINK && win.mode & MODE_BLINK) &&
          258 +                        !(base.mode & ATTR_INVISIBLE)
          259 +                ) {
          260 +                        // Special color for underline
          261 +                        // Index
          262 +                        if (base.ucolor[1] < 0) {
          263 +                                linecolor = dc.col[base.ucolor[0]].pixel;
          264 +                        }
          265 +                        // RGB
          266 +                        else {
          267 +                                XColor lcolor;
          268 +                                lcolor.red = base.ucolor[0] * 257;
          269 +                                lcolor.green = base.ucolor[1] * 257;
          270 +                                lcolor.blue = base.ucolor[2] * 257;
          271 +                                lcolor.flags = DoRed | DoGreen | DoBlue;
          272 +                                XAllocColor(xw.dpy, xw.cmap, &lcolor);
          273 +                                linecolor = lcolor.pixel;
          274 +                        }
          275 +                } else {
          276 +                        // Foreground color for underline
          277 +                        linecolor = fg->pixel;
          278 +                }
          279 +
          280 +                XGCValues ugcv = {
          281 +                        .foreground = linecolor,
          282 +                        .line_width = wlw,
          283 +                        .line_style = LineSolid,
          284 +                        .cap_style = CapNotLast
          285 +                };
          286 +
          287 +                GC ugc = XCreateGC(xw.dpy, XftDrawDrawable(xw.draw),
          288 +                        GCForeground | GCLineWidth | GCLineStyle | GCCapStyle,
          289 +                        &ugcv);
          290 +
          291 +                // Underline Style
          292 +                if (base.ustyle != 3) {
          293 +                        //XftDrawRect(xw.draw, fg, winx, winy + dc.font.ascent + 1, width, 1);
          294 +                        XFillRectangle(xw.dpy, XftDrawDrawable(xw.draw), ugc, winx,
          295 +                                winy + dc.font.ascent + 1, width, wlw);
          296 +                } else if (base.ustyle == 3) {
          297 +                        int ww = win.cw;//width;
          298 +                        int wh = dc.font.descent - wlw/2 - 1;//r.height/7;
          299 +                        int wx = winx;
          300 +                        int wy = winy + win.ch - dc.font.descent;
          301 +
          302 +#if UNDERCURL_STYLE == UNDERCURL_CURLY
          303 +                        // Draw waves
          304 +                        int narcs = charlen * 2 + 1;
          305 +                        XArc *arcs = xmalloc(sizeof(XArc) * narcs);
          306 +
          307 +                        int i = 0;
          308 +                        for (i = 0; i < charlen-1; i++) {
          309 +                                arcs[i*2] = (XArc) {
          310 +                                        .x = wx + win.cw * i + ww / 4,
          311 +                                        .y = wy,
          312 +                                        .width = win.cw / 2,
          313 +                                        .height = wh,
          314 +                                        .angle1 = 0,
          315 +                                        .angle2 = 180 * 64
          316 +                                };
          317 +                                arcs[i*2+1] = (XArc) {
          318 +                                        .x = wx + win.cw * i + ww * 0.75,
          319 +                                        .y = wy,
          320 +                                        .width = win.cw/2,
          321 +                                        .height = wh,
          322 +                                        .angle1 = 180 * 64,
          323 +                                        .angle2 = 180 * 64
          324 +                                };
          325 +                        }
          326 +                        // Last wave
          327 +                        arcs[i*2] = (XArc) {wx + ww * i + ww / 4, wy, ww / 2, wh,
          328 +                        0, 180 * 64 };
          329 +                        // Last wave tail
          330 +                        arcs[i*2+1] = (XArc) {wx + ww * i + ww * 0.75, wy, ceil(ww / 2.),
          331 +                        wh, 180 * 64, 90 * 64};
          332 +                        // First wave tail
          333 +                        i++;
          334 +                        arcs[i*2] = (XArc) {wx - ww/4 - 1, wy, ceil(ww / 2.), wh, 270 * 64,
          335 +                        90 * 64 };
          336 +
          337 +                        XDrawArcs(xw.dpy, XftDrawDrawable(xw.draw), ugc, arcs, narcs);
          338 +
          339 +                        free(arcs);
          340 +#elif UNDERCURL_STYLE == UNDERCURL_SPIKY
          341 +                        // Make the underline corridor larger
          342 +                        /*
          343 +                        wy -= wh;
          344 +                        */
          345 +                        wh *= 2;
          346 +
          347 +                        // Set the angle of the slope to 45°
          348 +                        ww = wh;
          349 +
          350 +                        // Position of wave is independent of word, it's absolute
          351 +                        wx = (wx / (ww/2)) * (ww/2);
          352 +
          353 +                        int marginStart = winx - wx;
          354 +
          355 +                        // Calculate number of points with floating precision
          356 +                        float n = width;                                        // Width of word in pixels
          357 +                        n = (n / ww) * 2;                                        // Number of slopes (/ or \)
          358 +                        n += 2;                                                                // Add two last points
          359 +                        int npoints = n;                                        // Convert to int
          360 +
          361 +                        // Total length of underline
          362 +                        float waveLength = 0;
          363 +
          364 +                        if (npoints >= 3) {
          365 +                                // We add an aditional slot in case we use a bonus point
          366 +                                XPoint *points = xmalloc(sizeof(XPoint) * (npoints + 1));
          367 +
          368 +                                // First point (Starts with the word bounds)
          369 +                                points[0] = (XPoint) {
          370 +                                        .x = wx + marginStart,
          371 +                                        .y = (isSlopeRising(wx, 0, ww))
          372 +                                                ? (wy - marginStart + ww/2.f)
          373 +                                                : (wy + marginStart)
          374 +                                };
          375 +
          376 +                                // Second point (Goes back to the absolute point coordinates)
          377 +                                points[1] = (XPoint) {
          378 +                                        .x = (ww/2.f) - marginStart,
          379 +                                        .y = (isSlopeRising(wx, 1, ww))
          380 +                                                ? (ww/2.f - marginStart)
          381 +                                                : (-ww/2.f + marginStart)
          382 +                                };
          383 +                                waveLength += (ww/2.f) - marginStart;
          384 +
          385 +                                // The rest of the points
          386 +                                for (int i = 2; i < npoints-1; i++) {
          387 +                                        points[i] = (XPoint) {
          388 +                                                .x = ww/2,
          389 +                                                .y = (isSlopeRising(wx, i, ww))
          390 +                                                        ? wh/2
          391 +                                                        : -wh/2
          392 +                                        };
          393 +                                        waveLength += ww/2;
          394 +                                }
          395 +
          396 +                                // Last point
          397 +                                points[npoints-1] = (XPoint) {
          398 +                                        .x = ww/2,
          399 +                                        .y = (isSlopeRising(wx, npoints-1, ww))
          400 +                                                ? wh/2
          401 +                                                : -wh/2
          402 +                                };
          403 +                                waveLength += ww/2;
          404 +
          405 +                                // End
          406 +                                if (waveLength < width) { // Add a bonus point?
          407 +                                        int marginEnd = width - waveLength;
          408 +                                        points[npoints] = (XPoint) {
          409 +                                                .x = marginEnd,
          410 +                                                .y = (isSlopeRising(wx, npoints, ww))
          411 +                                                        ? (marginEnd)
          412 +                                                        : (-marginEnd)
          413 +                                        };
          414 +
          415 +                                        npoints++;
          416 +                                } else if (waveLength > width) { // Is last point too far?
          417 +                                        int marginEnd = waveLength - width;
          418 +                                        points[npoints-1].x -= marginEnd;
          419 +                                        if (isSlopeRising(wx, npoints-1, ww))
          420 +                                                points[npoints-1].y -= (marginEnd);
          421 +                                        else
          422 +                                                points[npoints-1].y += (marginEnd);
          423 +                                }
          424 +
          425 +                                // Draw the lines
          426 +                                XDrawLines(xw.dpy, XftDrawDrawable(xw.draw), ugc, points, npoints,
          427 +                                                CoordModePrevious);
          428 +
          429 +                                // Draw a second underline with an offset of 1 pixel
          430 +                                if ( ((win.ch / (widthThreshold/2)) % 2)) {
          431 +                                        points[0].x++;
          432 +
          433 +                                        XDrawLines(xw.dpy, XftDrawDrawable(xw.draw), ugc, points,
          434 +                                                        npoints, CoordModePrevious);
          435 +                                }
          436 +
          437 +                                // Free resources
          438 +                                free(points);
          439 +                        }
          440 +#else // UNDERCURL_CAPPED
          441 +                        // Cap is half of wave width
          442 +                        float capRatio = 0.5f;
          443 +
          444 +                        // Make the underline corridor larger
          445 +                        wh *= 2;
          446 +
          447 +                        // Set the angle of the slope to 45°
          448 +                        ww = wh;
          449 +                        ww *= 1 + capRatio; // Add a bit of width for the cap
          450 +
          451 +                        // Position of wave is independent of word, it's absolute
          452 +                        wx = (wx / ww) * ww;
          453 +
          454 +                        float marginStart;
          455 +                        switch(getSlope(winx, 0, ww)) {
          456 +                                case UNDERCURL_SLOPE_ASCENDING:
          457 +                                        marginStart = winx - wx;
          458 +                                        break;
          459 +                                case UNDERCURL_SLOPE_TOP_CAP:
          460 +                                        marginStart = winx - (wx + (ww * (2.f/6.f)));
          461 +                                        break;
          462 +                                case UNDERCURL_SLOPE_DESCENDING:
          463 +                                        marginStart = winx - (wx + (ww * (3.f/6.f)));
          464 +                                        break;
          465 +                                case UNDERCURL_SLOPE_BOTTOM_CAP:
          466 +                                        marginStart = winx - (wx + (ww * (5.f/6.f)));
          467 +                                        break;
          468 +                        }
          469 +
          470 +                        // Calculate number of points with floating precision
          471 +                        float n = width;                                        // Width of word in pixels
          472 +                                                                                                //                                           ._.
          473 +                        n = (n / ww) * 4;                                        // Number of points (./   \.)
          474 +                        n += 2;                                                                // Add two last points
          475 +                        int npoints = n;                                        // Convert to int
          476 +
          477 +                        // Position of the pen to draw the lines
          478 +                        float penX = 0;
          479 +                        float penY = 0;
          480 +
          481 +                        if (npoints >= 3) {
          482 +                                XPoint *points = xmalloc(sizeof(XPoint) * (npoints + 1));
          483 +
          484 +                                // First point (Starts with the word bounds)
          485 +                                penX = winx;
          486 +                                switch (getSlope(winx, 0, ww)) {
          487 +                                        case UNDERCURL_SLOPE_ASCENDING:
          488 +                                                penY = wy + wh/2.f - marginStart;
          489 +                                                break;
          490 +                                        case UNDERCURL_SLOPE_TOP_CAP:
          491 +                                                penY = wy;
          492 +                                                break;
          493 +                                        case UNDERCURL_SLOPE_DESCENDING:
          494 +                                                penY = wy + marginStart;
          495 +                                                break;
          496 +                                        case UNDERCURL_SLOPE_BOTTOM_CAP:
          497 +                                                penY = wy + wh/2.f;
          498 +                                                break;
          499 +                                }
          500 +                                points[0].x = penX;
          501 +                                points[0].y = penY;
          502 +
          503 +                                // Second point (Goes back to the absolute point coordinates)
          504 +                                switch (getSlope(winx, 1, ww)) {
          505 +                                        case UNDERCURL_SLOPE_ASCENDING:
          506 +                                                penX += ww * (1.f/6.f) - marginStart;
          507 +                                                penY += 0;
          508 +                                                break;
          509 +                                        case UNDERCURL_SLOPE_TOP_CAP:
          510 +                                                penX += ww * (2.f/6.f) - marginStart;
          511 +                                                penY += -wh/2.f + marginStart;
          512 +                                                break;
          513 +                                        case UNDERCURL_SLOPE_DESCENDING:
          514 +                                                penX += ww * (1.f/6.f) - marginStart;
          515 +                                                penY += 0;
          516 +                                                break;
          517 +                                        case UNDERCURL_SLOPE_BOTTOM_CAP:
          518 +                                                penX += ww * (2.f/6.f) - marginStart;
          519 +                                                penY += -marginStart + wh/2.f;
          520 +                                                break;
          521 +                                }
          522 +                                points[1].x = penX;
          523 +                                points[1].y = penY;
          524 +
          525 +                                // The rest of the points
          526 +                                for (int i = 2; i < npoints; i++) {
          527 +                                        switch (getSlope(winx, i, ww)) {
          528 +                                                case UNDERCURL_SLOPE_ASCENDING:
          529 +                                                case UNDERCURL_SLOPE_DESCENDING:
          530 +                                                        penX += ww * (1.f/6.f);
          531 +                                                        penY += 0;
          532 +                                                        break;
          533 +                                                case UNDERCURL_SLOPE_TOP_CAP:
          534 +                                                        penX += ww * (2.f/6.f);
          535 +                                                        penY += -wh / 2.f;
          536 +                                                        break;
          537 +                                                case UNDERCURL_SLOPE_BOTTOM_CAP:
          538 +                                                        penX += ww * (2.f/6.f);
          539 +                                                        penY += wh / 2.f;
          540 +                                                        break;
          541 +                                        }
          542 +                                        points[i].x = penX;
          543 +                                        points[i].y = penY;
          544 +                                }
          545 +
          546 +                                // End
          547 +                                float waveLength = penX - winx;
          548 +                                if (waveLength < width) { // Add a bonus point?
          549 +                                        int marginEnd = width - waveLength;
          550 +                                        penX += marginEnd;
          551 +                                        switch(getSlope(winx, npoints, ww)) {
          552 +                                                case UNDERCURL_SLOPE_ASCENDING:
          553 +                                                case UNDERCURL_SLOPE_DESCENDING:
          554 +                                                        //penY += 0;
          555 +                                                        break;
          556 +                                                case UNDERCURL_SLOPE_TOP_CAP:
          557 +                                                        penY += -marginEnd;
          558 +                                                        break;
          559 +                                                case UNDERCURL_SLOPE_BOTTOM_CAP:
          560 +                                                        penY += marginEnd;
          561 +                                                        break;
          562 +                                        }
          563 +
          564 +                                        points[npoints].x = penX;
          565 +                                        points[npoints].y = penY;
          566 +
          567 +                                        npoints++;
          568 +                                } else if (waveLength > width) { // Is last point too far?
          569 +                                        int marginEnd = waveLength - width;
          570 +                                        points[npoints-1].x -= marginEnd;
          571 +                                        switch(getSlope(winx, npoints-1, ww)) {
          572 +                                                case UNDERCURL_SLOPE_TOP_CAP:
          573 +                                                        points[npoints-1].y += marginEnd;
          574 +                                                        break;
          575 +                                                case UNDERCURL_SLOPE_BOTTOM_CAP:
          576 +                                                        points[npoints-1].y -= marginEnd;
          577 +                                                        break;
          578 +                                                default:
          579 +                                                        break;
          580 +                                        }
          581 +                                }
          582 +
          583 +                                // Draw the lines
          584 +                                XDrawLines(xw.dpy, XftDrawDrawable(xw.draw), ugc, points, npoints,
          585 +                                                CoordModeOrigin);
          586 +
          587 +                                // Draw a second underline with an offset of 1 pixel
          588 +                                if ( ((win.ch / (widthThreshold/2)) % 2)) {
          589 +                                        for (int i = 0; i < npoints; i++)
          590 +                                                points[i].x++;
          591 +
          592 +                                        XDrawLines(xw.dpy, XftDrawDrawable(xw.draw), ugc, points,
          593 +                                                        npoints, CoordModeOrigin);
          594 +                                }
          595 +
          596 +                                // Free resources
          597 +                                free(points);
          598 +                        }
          599 +#endif
          600 +                }
          601 +
          602 +                XFreeGC(xw.dpy, ugc);
          603          }
          604  
          605          if (base.mode & ATTR_STRUCK) {