sites

public wiki contents of suckless.org
git clone git://git.suckless.org/sites
Log | Files | 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