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