st-clickurl-nocontrol-0.9.2.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.9.2.diff (5520B)
---
1 From a5ac9200f9146b212c0cd87fcb3fae357f39cc53 Mon Sep 17 00:00:00 2001
2 From: kroovy <me@kroovy.de>
3 Date: Sat, 1 Mar 2025 23:44:55 +0100
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 2cd740a..0eadbce 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 03b9bc8..75347c6 100644
34 --- a/st.c
35 +++ b/st.c
36 @@ -636,6 +636,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 @@ -2644,6 +2730,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 fd3b0d8..ee0db89 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 @@ -124,3 +129,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 d73152b..e2d4d56 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 @@ -452,6 +453,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 @@ -1495,7 +1500,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 * chscale + 1,
199 width, 1);
200 }
201 @@ -1859,6 +1864,11 @@ kpress(XEvent *ev)
202 } else {
203 len = XLookupString(e, buf, sizeof buf, &ksym, NULL);
204 }
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.48.1
215