st-clickurl-nocontrol-0.8.5.diff - 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 <[email protected]> | |
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 asc… | |
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… | |
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 asc… | |
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… | |
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_st… | |
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,… | |
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 + … | |
199 width, 1); | |
200 } | |
201 @@ -1831,6 +1836,11 @@ kpress(XEvent *ev) | |
202 len = XmbLookupString(xw.ime.xic, e, buf, sizeof buf, &… | |
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 |