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