st-clickurl-nocontrol-0.8.5.diff - sites - public wiki contents of suckless.org
 (HTM) git clone git://git.suckless.org/sites
 (DIR) Log
 (DIR) Files
 (DIR) Refs
       ---
       st-clickurl-nocontrol-0.8.5.diff (5591B)
       ---
            1 From 8d13e4a296f0b2a625df10c8ee6d2fc96ec97580 Mon Sep 17 00:00:00 2001
            2 From: Kyle Chui <kyle.chui+suckless@pm.me>
            3 Date: Tue, 9 Apr 2024 16:31:25 -0700
            4 Subject: [PATCH] Underline URLs and follow with click
            5 
            6 ---
            7  config.def.h | 11 +++++++
            8  st.c         | 88 ++++++++++++++++++++++++++++++++++++++++++++++++++++
            9  st.h         |  9 ++++++
           10  x.c          | 12 ++++++-
           11  4 files changed, 119 insertions(+), 1 deletion(-)
           12 
           13 diff --git a/config.def.h b/config.def.h
           14 index 91ab8ca..4961830 100644
           15 --- a/config.def.h
           16 +++ b/config.def.h
           17 @@ -472,3 +472,14 @@ static char ascii_printable[] =
           18          " !\"#$%&'()*+,-./0123456789:;<=>?"
           19          "@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_"
           20          "`abcdefghijklmnopqrstuvwxyz{|}~";
           21 +
           22 +/*
           23 + * Open urls starting with urlprefixes, contatining urlchars
           24 + * by passing as ARG1 to urlhandler.
           25 + */
           26 +char* urlhandler = "xdg-open";
           27 +char urlchars[] =
           28 +        "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
           29 +        "abcdefghijklmnopqrstuvwxyz"
           30 +        "0123456789-._~:/?#@!$&'*+,;=%";
           31 +char* urlprefixes[] = {"http://", "https://", NULL};
           32 diff --git a/st.c b/st.c
           33 index 51049ba..a7eb86e 100644
           34 --- a/st.c
           35 +++ b/st.c
           36 @@ -643,6 +643,92 @@ getsel(void)
           37          return str;
           38  }
           39  
           40 +char *
           41 +strstrany(char* s, char** strs) {
           42 +        char *match;
           43 +        for (int i = 0; strs[i]; i++) {
           44 +                if ((match = strstr(s, strs[i]))) {
           45 +                        return match;
           46 +                }
           47 +        }
           48 +        return NULL;
           49 +}
           50 +
           51 +void
           52 +highlighturlsline(int row)
           53 +{
           54 +        char *linestr = calloc(sizeof(char), term.col+1); /* assume ascii */
           55 +        char *match;
           56 +        for (int j = 0; j < term.col; j++) {
           57 +                if (term.line[row][j].u < 127) {
           58 +                        linestr[j] = term.line[row][j].u;
           59 +                }
           60 +                linestr[term.col] = '\0';
           61 +        }
           62 +        int url_start = -1;
           63 +        while ((match = strstrany(linestr + url_start + 1, urlprefixes))) {
           64 +                url_start = match - linestr;
           65 +                for (int c = url_start; c < term.col && strchr(urlchars, linestr[c]); c++) {
           66 +                        term.line[row][c].mode |= ATTR_URL;
           67 +                        tsetdirt(row, c);
           68 +                }
           69 +        }
           70 +        free(linestr);
           71 +}
           72 +
           73 +void
           74 +unhighlighturlsline(int row)
           75 +{
           76 +        for (int j = 0; j < term.col; j++) {
           77 +                Glyph* g = &term.line[row][j];
           78 +                if (g->mode & ATTR_URL) {
           79 +                        g->mode &= ~ATTR_URL;
           80 +                        tsetdirt(row, j);
           81 +                }
           82 +        }
           83 +        return;
           84 +}
           85 +
           86 +int
           87 +followurl(int col, int row) {
           88 +        char *linestr = calloc(sizeof(char), term.col+1); /* assume ascii */
           89 +        char *match;
           90 +        for (int i = 0; i < term.col; i++) {
           91 +                if (term.line[row][i].u < 127) {
           92 +                        linestr[i] = term.line[row][i].u;
           93 +                }
           94 +                linestr[term.col] = '\0';
           95 +        }
           96 +        int url_start = -1, found_url = 0;
           97 +        while ((match = strstrany(linestr + url_start + 1, urlprefixes))) {
           98 +                url_start = match - linestr;
           99 +                int url_end = url_start;
          100 +                for (int c = url_start; c < term.col && strchr(urlchars, linestr[c]); c++) {
          101 +                        url_end++;
          102 +                }
          103 +                if (url_start <= col && col < url_end) {
          104 +                        found_url = 1;
          105 +                        linestr[url_end] = '\0';
          106 +                        break;
          107 +                }
          108 +        }
          109 +        if (!found_url) {
          110 +                free(linestr);
          111 +                return 0;
          112 +        }
          113 +
          114 +        pid_t chpid;
          115 +        if ((chpid = fork()) == 0) {
          116 +                if (fork() == 0)
          117 +                        execlp(urlhandler, urlhandler, linestr + url_start, NULL);
          118 +                exit(1);
          119 +        }
          120 +        if (chpid > 0)
          121 +                waitpid(chpid, NULL, 0);
          122 +        free(linestr);
          123 +    return 1;
          124 +}
          125 +
          126  void
          127  selclear(void)
          128  {
          129 @@ -2652,6 +2738,8 @@ drawregion(int x1, int y1, int x2, int y2)
          130                          continue;
          131  
          132                  term.dirty[y] = 0;
          133 +                unhighlighturlsline(y);
          134 +                highlighturlsline(y);
          135                  xdrawline(term.line[y], x1, y, x2);
          136          }
          137  }
          138 diff --git a/st.h b/st.h
          139 index 519b9bd..5efc27e 100644
          140 --- a/st.h
          141 +++ b/st.h
          142 @@ -33,6 +33,7 @@ enum glyph_attribute {
          143          ATTR_WRAP       = 1 << 8,
          144          ATTR_WIDE       = 1 << 9,
          145          ATTR_WDUMMY     = 1 << 10,
          146 +        ATTR_URL        = 1 << 11,
          147          ATTR_BOLD_FAINT = ATTR_BOLD | ATTR_FAINT,
          148  };
          149  
          150 @@ -105,6 +106,10 @@ void selextend(int, int, int, int);
          151  int selected(int, int);
          152  char *getsel(void);
          153  
          154 +void highlighturlsline(int);
          155 +void unhighlighturlsline(int);
          156 +int followurl(int, int);
          157 +
          158  size_t utf8encode(Rune, char *);
          159  
          160  void *xmalloc(size_t);
          161 @@ -126,3 +131,7 @@ extern unsigned int tabspaces;
          162  extern unsigned int defaultfg;
          163  extern unsigned int defaultbg;
          164  extern unsigned int defaultcs;
          165 +extern char *urlhandler;
          166 +extern char urlchars[];
          167 +extern char *urlprefixes[];
          168 +extern int nurlprefixes;
          169 diff --git a/x.c b/x.c
          170 index 8a16faa..c721f8b 100644
          171 --- a/x.c
          172 +++ b/x.c
          173 @@ -191,6 +191,7 @@ static void usage(void);
          174  
          175  static void (*handler[LASTEvent])(XEvent *) = {
          176          [KeyPress] = kpress,
          177 +        [KeyRelease] = kpress,
          178          [ClientMessage] = cmessage,
          179          [ConfigureNotify] = resize,
          180          [VisibilityNotify] = visibility,
          181 @@ -445,6 +446,10 @@ mouseaction(XEvent *e, uint release)
          182          /* ignore Button<N>mask for Button<N> - it's set on release */
          183          uint state = e->xbutton.state & ~buttonmask(e->xbutton.button);
          184  
          185 +        if (release == 0 && e->xbutton.button == Button1) {
          186 +                return followurl(evcol(e), evrow(e));
          187 +        }
          188 +
          189          for (ms = mshortcuts; ms < mshortcuts + LEN(mshortcuts); ms++) {
          190                  if (ms->release == release &&
          191                      ms->button == e->xbutton.button &&
          192 @@ -1476,7 +1481,7 @@ xdrawglyphfontspecs(const XftGlyphFontSpec *specs, Glyph base, int len, int x, i
          193          XftDrawGlyphFontSpec(xw.draw, fg, specs, len);
          194  
          195          /* Render underline and strikethrough. */
          196 -        if (base.mode & ATTR_UNDERLINE) {
          197 +        if (base.mode & ATTR_UNDERLINE || base.mode & ATTR_URL) {
          198                  XftDrawRect(xw.draw, fg, winx, winy + dc.font.ascent + 1,
          199                                  width, 1);
          200          }
          201 @@ -1831,6 +1836,11 @@ kpress(XEvent *ev)
          202                  len = XmbLookupString(xw.ime.xic, e, buf, sizeof buf, &ksym, &status);
          203          else
          204                  len = XLookupString(e, buf, sizeof buf, &ksym, NULL);
          205 +
          206 +        /* KeyRelease not relevant to shortcuts */
          207 +        if (ev->type == KeyRelease)
          208 +                return;
          209 +
          210          /* 1. shortcuts */
          211          for (bp = shortcuts; bp < shortcuts + LEN(shortcuts); bp++) {
          212                  if (ksym == bp->keysym && match(bp->mod, e->state)) {
          213 -- 
          214 2.42.0
          215