st-clickurl-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-0.8.5.diff (5778B)
       ---
            1 From d5b492049f48dc411b0dd7dc01a403304c20438d Mon Sep 17 00:00:00 2001
            2 From: Jishnu Sen <jishnu1@gmail.com>
            3 Date: Sun, 7 Apr 2024 22:54:46 -0700
            4 Subject: [PATCH] Highlight URLs with control and follow with click
            5 
            6 ---
            7  config.def.h | 11 +++++++
            8  st.c         | 89 ++++++++++++++++++++++++++++++++++++++++++++++++++++
            9  st.h         |  9 ++++++
           10  x.c          | 24 +++++++++++++-
           11  4 files changed, 132 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..8f2156c 100644
           34 --- a/st.c
           35 +++ b/st.c
           36 @@ -643,6 +643,95 @@ 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 +highlighturls(void)
           53 +{
           54 +        char *match;
           55 +        char *linestr = calloc(sizeof(char), term.col+1); /* assume ascii */
           56 +        for (int i = term.top; i < term.bot; i++) {
           57 +                int url_start = -1;
           58 +                for (int j = 0; j < term.col; j++) {
           59 +                        if (term.line[i][j].u < 127) {
           60 +                                linestr[j] = term.line[i][j].u;
           61 +                        }
           62 +                        linestr[term.col] = '\0';
           63 +                }
           64 +                while ((match = strstrany(linestr + url_start + 1, urlprefixes))) {
           65 +                        url_start = match - linestr;
           66 +                        for (int c = url_start; c < term.col && strchr(urlchars, linestr[c]); c++) {
           67 +                                term.line[i][c].mode |= ATTR_URL;
           68 +                                tsetdirt(i, c);
           69 +                        }
           70 +                }
           71 +        }
           72 +        free(linestr);
           73 +}
           74 +
           75 +void
           76 +unhighlighturls(void)
           77 +{
           78 +        for (int i = term.top; i < term.bot; i++) {
           79 +                for (int j = 0; j < term.col; j++) {
           80 +                        Glyph* g = &term.line[i][j];
           81 +                        if (g->mode & ATTR_URL) {
           82 +                                g->mode &= ~ATTR_URL;
           83 +                                tsetdirt(i, j);
           84 +                        }
           85 +                }
           86 +        }
           87 +        return;
           88 +}
           89 +
           90 +void
           91 +followurl(int x, int y) {
           92 +        char *linestr = calloc(sizeof(char), term.col+1); /* assume ascii */
           93 +        char *match;
           94 +        for (int i = 0; i < term.col; i++) {
           95 +                if (term.line[x][i].u < 127) {
           96 +                        linestr[i] = term.line[x][i].u;
           97 +                }
           98 +                linestr[term.col] = '\0';
           99 +        }
          100 +        int url_start = -1;
          101 +        while ((match = strstrany(linestr + url_start + 1, urlprefixes))) {
          102 +                url_start = match - linestr;
          103 +                int url_end = url_start;
          104 +                for (int c = url_start; c < term.col && strchr(urlchars, linestr[c]); c++) {
          105 +                        url_end++;
          106 +                }
          107 +                if (url_start <= y && y < url_end) {
          108 +                        linestr[url_end] = '\0';
          109 +                        break;
          110 +                }
          111 +        }
          112 +        if (url_start == -1) {
          113 +                free(linestr);
          114 +                return;
          115 +        }
          116 +
          117 +        pid_t chpid;
          118 +        if ((chpid = fork()) == 0) {
          119 +                if (fork() == 0)
          120 +                        execlp(urlhandler, urlhandler, linestr + url_start, NULL);
          121 +                exit(1);
          122 +        }
          123 +        if (chpid > 0)
          124 +                waitpid(chpid, NULL, 0);
          125 +        free(linestr);
          126 +        unhighlighturls();
          127 +}
          128 +
          129  void
          130  selclear(void)
          131  {
          132 diff --git a/st.h b/st.h
          133 index 519b9bd..354e7f9 100644
          134 --- a/st.h
          135 +++ b/st.h
          136 @@ -34,6 +34,7 @@ enum glyph_attribute {
          137          ATTR_WIDE       = 1 << 9,
          138          ATTR_WDUMMY     = 1 << 10,
          139          ATTR_BOLD_FAINT = ATTR_BOLD | ATTR_FAINT,
          140 +        ATTR_URL        = 1 << 14,
          141  };
          142  
          143  enum selection_mode {
          144 @@ -105,6 +106,10 @@ void selextend(int, int, int, int);
          145  int selected(int, int);
          146  char *getsel(void);
          147  
          148 +void highlighturls(void);
          149 +void unhighlighturls(void);
          150 +void followurl(int, int);
          151 +
          152  size_t utf8encode(Rune, char *);
          153  
          154  void *xmalloc(size_t);
          155 @@ -126,3 +131,7 @@ extern unsigned int tabspaces;
          156  extern unsigned int defaultfg;
          157  extern unsigned int defaultbg;
          158  extern unsigned int defaultcs;
          159 +extern char *urlhandler;
          160 +extern char urlchars[];
          161 +extern char *urlprefixes[];
          162 +extern int nurlprefixes;
          163 diff --git a/x.c b/x.c
          164 index 8a16faa..13f68e4 100644
          165 --- a/x.c
          166 +++ b/x.c
          167 @@ -191,6 +191,7 @@ static void usage(void);
          168  
          169  static void (*handler[LASTEvent])(XEvent *) = {
          170          [KeyPress] = kpress,
          171 +        [KeyRelease] = kpress,
          172          [ClientMessage] = cmessage,
          173          [ConfigureNotify] = resize,
          174          [VisibilityNotify] = visibility,
          175 @@ -445,6 +446,15 @@ mouseaction(XEvent *e, uint release)
          176          /* ignore Button<N>mask for Button<N> - it's set on release */
          177          uint state = e->xbutton.state & ~buttonmask(e->xbutton.button);
          178  
          179 +        if (release == 0 &&
          180 +            e->xbutton.button == Button1 &&
          181 +            (match(ControlMask, state) ||
          182 +             match(ControlMask, state & ~forcemousemod))) {
          183 +                followurl(evrow(e), evcol(e));
          184 +                return 1;
          185 +        }
          186 +
          187 +
          188          for (ms = mshortcuts; ms < mshortcuts + LEN(mshortcuts); ms++) {
          189                  if (ms->release == release &&
          190                      ms->button == e->xbutton.button &&
          191 @@ -1476,7 +1486,7 @@ xdrawglyphfontspecs(const XftGlyphFontSpec *specs, Glyph base, int len, int x, i
          192          XftDrawGlyphFontSpec(xw.draw, fg, specs, len);
          193  
          194          /* Render underline and strikethrough. */
          195 -        if (base.mode & ATTR_UNDERLINE) {
          196 +        if (base.mode & ATTR_UNDERLINE || base.mode & ATTR_URL) {
          197                  XftDrawRect(xw.draw, fg, winx, winy + dc.font.ascent + 1,
          198                                  width, 1);
          199          }
          200 @@ -1831,6 +1841,18 @@ kpress(XEvent *ev)
          201                  len = XmbLookupString(xw.ime.xic, e, buf, sizeof buf, &ksym, &status);
          202          else
          203                  len = XLookupString(e, buf, sizeof buf, &ksym, NULL);
          204 +
          205 +        /* 0. highlight URLs when control held */
          206 +        if (ksym == XK_Control_L) {
          207 +                highlighturls();
          208 +        } else if (ev->type == KeyRelease && e->keycode == XKeysymToKeycode(e->display, XK_Control_L)) {
          209 +                unhighlighturls();
          210 +        }
          211 +
          212 +        /* KeyRelease not relevant to shortcuts */
          213 +        if (ev->type == KeyRelease)
          214 +                return;
          215 +
          216          /* 1. shortcuts */
          217          for (bp = shortcuts; bp < shortcuts + LEN(shortcuts); bp++) {
          218                  if (ksym == bp->keysym && match(bp->mod, e->state)) {
          219 -- 
          220 2.44.0
          221