Add scrollback support to the openurlonclick patch
The openurlonclick and scrollback patches are now working together, so links can be clicked in the scrollback buffer too. This update also adds url underlining and other improvements to the openurlonclick patch. The full list of changes in the openurlonclick patch: - Adds scrollback support - Adds modkey option - Better url detection - Underlines url when the mouse pointer is over a link - Opens a browser as a background process, so it won't lock the terminal anymore - Fixes a segmentation fault bug
This commit is contained in:
parent
9e0e419781
commit
3eb170a9a5
|
@ -33,6 +33,8 @@ static int borderpx = 2;
|
|||
#endif // RELATIVEBORDER_PATCH
|
||||
|
||||
#if OPENURLONCLICK_PATCH
|
||||
/* modkey options: ControlMask, ShiftMask or XK_ANY_MOD */
|
||||
static uint url_opener_modkey = XK_ANY_MOD;
|
||||
static char *url_opener = "xdg-open";
|
||||
#endif // OPENURLONCLICK_PATCH
|
||||
|
||||
|
|
|
@ -1,59 +1,140 @@
|
|||
#if SCROLLBACK_PATCH && !VIM_BROWSE_PATCH
|
||||
#define TLINEURL(y) TLINE(y)
|
||||
#else
|
||||
#define TLINEURL(y) term.line[y]
|
||||
#endif // SCROLLBACK_PATCH
|
||||
|
||||
#if VIM_BROWSE_PATCH
|
||||
extern int buffCols;
|
||||
#endif // VIM_BROWSE_PATCH
|
||||
|
||||
int url_x1, url_y1, url_x2, url_y2 = -1;
|
||||
int url_draw, url_click, url_maxcol;
|
||||
|
||||
static int
|
||||
isvalidurlchar(Rune u)
|
||||
{
|
||||
/* () and [] can appear in urls, but excluding them here will reduce false
|
||||
* positives when figuring out where a given url ends. See copyurl patch.
|
||||
*/
|
||||
static char urlchars[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
|
||||
"abcdefghijklmnopqrstuvwxyz"
|
||||
"0123456789-._~:/?#@!$&'*+,;=%";
|
||||
return u < 128 && strchr(urlchars, (int)u) != NULL;
|
||||
}
|
||||
|
||||
/* find the end of the wrapped line */
|
||||
static int
|
||||
findeowl(int row)
|
||||
{
|
||||
#if VIM_BROWSE_PATCH
|
||||
int col = buffCols - 1;
|
||||
#elif COLUMNS_PATCH
|
||||
int col = term.maxcol - 1;
|
||||
#else
|
||||
int col = term.col - 1;
|
||||
#endif // VIM_BROWSE_PATCH
|
||||
|
||||
do {
|
||||
if (TLINEURL(row)[col].mode & ATTR_WRAP)
|
||||
return col;
|
||||
} while (TLINEURL(row)[col].u == ' ' && --col >= 0);
|
||||
return -1;
|
||||
}
|
||||
|
||||
void
|
||||
clearurl(void)
|
||||
{
|
||||
while (url_y1 <= url_y2)
|
||||
term.dirty[url_y1++] = 1;
|
||||
url_y2 = -1;
|
||||
}
|
||||
|
||||
char *
|
||||
detecturl(int col, int row, int draw)
|
||||
{
|
||||
static char url[2048];
|
||||
int x1, y1, x2, y2, wrapped;
|
||||
int row_start = row;
|
||||
int col_start = col;
|
||||
int i = sizeof(url)/2+1, j = sizeof(url)/2;
|
||||
#if SCROLLBACK_PATCH && !VIM_BROWSE_PATCH
|
||||
int minrow = term.scr - term.histn, maxrow = term.scr + term.row - 1;
|
||||
/* Fixme: MODE_ALTSCREEN is not defined here, I had to use the magic number 1<<2 */
|
||||
if ((term.mode & (1 << 2)) != 0)
|
||||
minrow = 0, maxrow = term.row - 1;
|
||||
#else
|
||||
int minrow = 0, maxrow = term.row - 1;
|
||||
#endif // scrollback_patch
|
||||
url_maxcol = 0;
|
||||
|
||||
/* clear previously underlined url */
|
||||
if (draw)
|
||||
clearurl();
|
||||
|
||||
if (!isvalidurlchar(TLINEURL(row)[col].u))
|
||||
return NULL;
|
||||
|
||||
/* find the first character of url */
|
||||
do {
|
||||
x1 = col_start, y1 = row_start;
|
||||
url_maxcol = MAX(url_maxcol, x1);
|
||||
url[--i] = TLINEURL(row_start)[col_start].u;
|
||||
if (--col_start < 0) {
|
||||
if (--row_start < minrow || (col_start = findeowl(row_start)) < 0)
|
||||
break;
|
||||
}
|
||||
} while (i > 0 && isvalidurlchar(TLINEURL(row_start)[col_start].u));
|
||||
|
||||
/* early detection */
|
||||
if (url[i] != 'h')
|
||||
return NULL;
|
||||
|
||||
/* find the last character of url */
|
||||
do {
|
||||
x2 = col, y2 = row;
|
||||
url_maxcol = MAX(url_maxcol, x2);
|
||||
url[j++] = TLINEURL(row)[col].u;
|
||||
wrapped = TLINEURL(row)[col].mode & ATTR_WRAP;
|
||||
#if VIM_BROWSE_PATCH
|
||||
if (++col >= buffCols || wrapped) {
|
||||
#elif COLUMNS_PATCH
|
||||
if (++col >= term.maxcol || wrapped) {
|
||||
#else
|
||||
if (++col >= term.col || wrapped) {
|
||||
#endif // VIM_BROWSE_PATCH
|
||||
col = 0;
|
||||
if (++row > maxrow || !wrapped)
|
||||
break;
|
||||
}
|
||||
} while (j < sizeof(url)-1 && isvalidurlchar(TLINEURL(row)[col].u));
|
||||
|
||||
url[j] = 0;
|
||||
|
||||
if (strncmp("https://", &url[i], 8) && strncmp("http://", &url[i], 7))
|
||||
return NULL;
|
||||
|
||||
/* underline url (see xdrawglyphfontspecs() in x.c) */
|
||||
if (draw) {
|
||||
url_x1 = (y1 >= 0) ? x1 : 0;
|
||||
url_x2 = (y2 < term.row) ? x2 : url_maxcol;
|
||||
url_y1 = MAX(y1, 0);
|
||||
url_y2 = MIN(y2, term.row-1);
|
||||
url_draw = 1;
|
||||
for (y1 = url_y1; y1 <= url_y2; y1++)
|
||||
term.dirty[y1] = 1;
|
||||
}
|
||||
|
||||
return &url[i];
|
||||
}
|
||||
|
||||
void
|
||||
openUrlOnClick(int col, int row, char* url_opener)
|
||||
{
|
||||
int row_start = row;
|
||||
int col_start = col;
|
||||
int row_end = row;
|
||||
int col_end = col;
|
||||
|
||||
if (term.line[row][col].u == ' ')
|
||||
return;
|
||||
|
||||
/* while previous character is not space */
|
||||
while (term.line[row_start][col_start-1].u != ' ') {
|
||||
if (col_start == 0)
|
||||
{
|
||||
// Before moving start pointer to the previous line we check if it ends with space
|
||||
if (term.line[row_start - 1][term.col - 1].u == ' ')
|
||||
break;
|
||||
col_start=term.col - 1;
|
||||
row_start--;
|
||||
} else {
|
||||
col_start--;
|
||||
}
|
||||
}
|
||||
|
||||
/* while next character is not space nor end of line */
|
||||
while (term.line[row_end][col_end].u != ' ') {
|
||||
col_end++;
|
||||
if (col_end == term.col - 1)
|
||||
{
|
||||
if (term.line[row_end + 1][0].u == ' ')
|
||||
break;
|
||||
col_end=0;
|
||||
row_end++;
|
||||
}
|
||||
}
|
||||
|
||||
char url[200] = "";
|
||||
int url_index=0;
|
||||
do {
|
||||
url[url_index] = term.line[row_start][col_start].u;
|
||||
url_index++;
|
||||
col_start++;
|
||||
if (col_start == term.col)
|
||||
{
|
||||
col_start = 0;
|
||||
row_start++;
|
||||
}
|
||||
} while (url_index < (sizeof(url)-1) &&
|
||||
(row_start != row_end || col_start != col_end));
|
||||
|
||||
if (strncmp("http", url, 4) != 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
char command[strlen(url_opener)+strlen(url)+2];
|
||||
sprintf(command, "%s %s", url_opener, url);
|
||||
system(command);
|
||||
char *url = detecturl(col, row, 1);
|
||||
if (url) {
|
||||
char command[strlen(url_opener) + strlen(url) + 5];
|
||||
sprintf(command, "%s \"%s\"&", url_opener, url);
|
||||
system(command);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1 +1,6 @@
|
|||
static inline void restoremousecursor(void) {
|
||||
if (!(win.mode & MODE_MOUSE) && xw.pointerisvisible)
|
||||
XDefineCursor(xw.dpy, xw.win, xw.vpointer);
|
||||
}
|
||||
static void clearurl(void);
|
||||
static void openUrlOnClick(int col, int row, char* url_opener);
|
|
@ -18,6 +18,11 @@ kscrolldown(const Arg* a)
|
|||
#if SIXEL_PATCH
|
||||
scroll_images(-1*n);
|
||||
#endif // SIXEL_PATCH
|
||||
|
||||
#if OPENURLONCLICK_PATCH
|
||||
if (n > 0)
|
||||
restoremousecursor();
|
||||
#endif // OPENURLONCLICK_PATCH
|
||||
}
|
||||
|
||||
void
|
||||
|
@ -42,4 +47,9 @@ kscrollup(const Arg* a)
|
|||
#if SIXEL_PATCH
|
||||
scroll_images(n);
|
||||
#endif // SIXEL_PATCH
|
||||
|
||||
#if OPENURLONCLICK_PATCH
|
||||
if (n > 0)
|
||||
restoremousecursor();
|
||||
#endif // OPENURLONCLICK_PATCH
|
||||
}
|
||||
|
|
|
@ -11,6 +11,9 @@
|
|||
#if KEYBOARDSELECT_PATCH
|
||||
#include "keyboardselect_st.h"
|
||||
#endif
|
||||
#if OPENURLONCLICK_PATCH
|
||||
#include "openurlonclick.h"
|
||||
#endif
|
||||
#if RIGHTCLICKTOPLUMB_PATCH
|
||||
#include "rightclicktoplumb_st.h"
|
||||
#endif
|
||||
|
|
|
@ -20,9 +20,6 @@
|
|||
#if NETWMICON_PATCH
|
||||
#include "netwmicon.h"
|
||||
#endif
|
||||
#if OPENURLONCLICK_PATCH
|
||||
#include "openurlonclick.h"
|
||||
#endif
|
||||
#if RIGHTCLICKTOPLUMB_PATCH
|
||||
#include "rightclicktoplumb_x.h"
|
||||
#endif
|
||||
|
|
8
st.c
8
st.c
|
@ -1387,6 +1387,10 @@ tswapscreen(void)
|
|||
void
|
||||
tscrolldown(int orig, int n)
|
||||
{
|
||||
#if OPENURLONCLICK_PATCH
|
||||
restoremousecursor();
|
||||
#endif //OPENURLONCLICK_PATCH
|
||||
|
||||
#if VIM_BROWSE_PATCH
|
||||
if (!orig && historyBufferScroll(-n))
|
||||
return;
|
||||
|
@ -1435,6 +1439,10 @@ tscrollup(int orig, int n, int copyhist)
|
|||
tscrollup(int orig, int n)
|
||||
#endif // SCROLLBACK_PATCH
|
||||
{
|
||||
#if OPENURLONCLICK_PATCH
|
||||
restoremousecursor();
|
||||
#endif //OPENURLONCLICK_PATCH
|
||||
|
||||
#if VIM_BROWSE_PATCH
|
||||
if (!orig && historyBufferScroll(n))
|
||||
return;
|
||||
|
|
5
st.h
5
st.h
|
@ -219,13 +219,16 @@ typedef struct {
|
|||
#endif // BACKGROUND_IMAGE_PATCH
|
||||
Visual *vis;
|
||||
XSetWindowAttributes attrs;
|
||||
#if HIDECURSOR_PATCH
|
||||
#if HIDECURSOR_PATCH || OPENURLONCLICK_PATCH
|
||||
/* Here, we use the term *pointer* to differentiate the cursor
|
||||
* one sees when hovering the mouse over the terminal from, e.g.,
|
||||
* a green rectangle where text would be entered. */
|
||||
Cursor vpointer, bpointer; /* visible and hidden pointers */
|
||||
int pointerisvisible;
|
||||
#endif // HIDECURSOR_PATCH
|
||||
#if OPENURLONCLICK_PATCH
|
||||
Cursor upointer;
|
||||
#endif // OPENURLONCLICK_PATCH
|
||||
int scr;
|
||||
int isfixed; /* is fixed geometry? */
|
||||
#if ALPHA_PATCH
|
||||
|
|
87
x.c
87
x.c
|
@ -536,6 +536,11 @@ bpress(XEvent *e)
|
|||
#if !VIM_BROWSE_PATCH
|
||||
selstart(evcol(e), evrow(e), snap);
|
||||
#endif // VIM_BROWSE_PATCH
|
||||
|
||||
#if OPENURLONCLICK_PATCH
|
||||
clearurl();
|
||||
url_click = 1;
|
||||
#endif // OPENURLONCLICK_PATCH
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -786,14 +791,16 @@ brelease(XEvent *e)
|
|||
if (btn == Button1 && !IS_SET(MODE_NORMAL)) {
|
||||
mousesel(e, 1);
|
||||
#if OPENURLONCLICK_PATCH
|
||||
openUrlOnClick(evcol(e), evrow(e), url_opener);
|
||||
if (url_click && e->xkey.state & url_opener_modkey)
|
||||
openUrlOnClick(evcol(e), evrow(e), url_opener);
|
||||
#endif // OPENURLONCLICK_PATCH
|
||||
}
|
||||
#else
|
||||
if (btn == Button1) {
|
||||
mousesel(e, 1);
|
||||
#if OPENURLONCLICK_PATCH
|
||||
openUrlOnClick(evcol(e), evrow(e), url_opener);
|
||||
if (url_click && e->xkey.state & url_opener_modkey)
|
||||
openUrlOnClick(evcol(e), evrow(e), url_opener);
|
||||
#endif // OPENURLONCLICK_PATCH
|
||||
}
|
||||
#endif // VIM_BROWSE_PATCH
|
||||
|
@ -821,6 +828,18 @@ bmotion(XEvent *e)
|
|||
xsetpointermotion(0);
|
||||
}
|
||||
#endif // HIDECURSOR_PATCH
|
||||
#if OPENURLONCLICK_PATCH
|
||||
#if VIM_BROWSE_PATCH
|
||||
if (!IS_SET(MODE_NORMAL))
|
||||
#endif // VIM_BROWSE_PATCH
|
||||
if (!IS_SET(MODE_MOUSE)) {
|
||||
if (!(e->xbutton.state & Button1Mask) && detecturl(evcol(e), evrow(e), 1))
|
||||
XDefineCursor(xw.dpy, xw.win, xw.upointer);
|
||||
else
|
||||
XDefineCursor(xw.dpy, xw.win, xw.vpointer);
|
||||
}
|
||||
url_click = 0;
|
||||
#endif // OPENURLONCLICK_PATCH
|
||||
|
||||
if (IS_SET(MODE_MOUSE) && !(e->xbutton.state & forcemousemod)) {
|
||||
mousereport(e);
|
||||
|
@ -911,6 +930,10 @@ xloadcolor(int i, const char *name, Color *ncolor)
|
|||
#if VIM_BROWSE_PATCH
|
||||
void normalMode()
|
||||
{
|
||||
#if OPENURLONCLICK_PATCH
|
||||
clearurl();
|
||||
restoremousecursor();
|
||||
#endif // OPENURLONCLICK_PATCH
|
||||
historyModeToggle((win.mode ^=MODE_NORMAL) & MODE_NORMAL);
|
||||
}
|
||||
#endif // VIM_BROWSE_PATCH
|
||||
|
@ -1443,6 +1466,9 @@ xinit(int cols, int rows)
|
|||
#endif // ST_EMBEDDER_PATCH
|
||||
;
|
||||
xw.attrs.colormap = xw.cmap;
|
||||
#if OPENURLONCLICK_PATCH
|
||||
xw.attrs.event_mask |= PointerMotionMask;
|
||||
#endif // OPENURLONCLICK_PATCH
|
||||
|
||||
#if !ALPHA_PATCH
|
||||
if (!(opt_embed && (parent = strtol(opt_embed, NULL, 0))))
|
||||
|
@ -1534,6 +1560,14 @@ xinit(int cols, int rows)
|
|||
XRecolorCursor(xw.dpy, cursor, &xmousefg, &xmousebg);
|
||||
#endif // HIDECURSOR_PATCH
|
||||
|
||||
#if OPENURLONCLICK_PATCH
|
||||
xw.upointer = XCreateFontCursor(xw.dpy, XC_hand2);
|
||||
#if !HIDECURSOR_PATCH
|
||||
xw.vpointer = cursor;
|
||||
xw.pointerisvisible = 1;
|
||||
#endif // HIDECURSOR_PATCH
|
||||
#endif // OPENURLONCLICK_PATCH
|
||||
|
||||
xw.xembed = XInternAtom(xw.dpy, "_XEMBED", False);
|
||||
xw.wmdeletewin = XInternAtom(xw.dpy, "WM_DELETE_WINDOW", False);
|
||||
xw.netwmname = XInternAtom(xw.dpy, "_NET_WM_NAME", False);
|
||||
|
@ -2365,6 +2399,28 @@ xdrawglyphfontspecs(const XftGlyphFontSpec *specs, Glyph base, int len, int x, i
|
|||
}
|
||||
#endif // WIDE_GLYPHS_PATCH
|
||||
|
||||
#if OPENURLONCLICK_PATCH
|
||||
if (url_draw && y >= url_y1 && y <= url_y2) {
|
||||
int x1 = (y == url_y1) ? url_x1 : 0;
|
||||
int x2 = (y == url_y2) ? MIN(url_x2, term.col-1) : url_maxcol;
|
||||
if (x + charlen > x1 && x <= x2) {
|
||||
int xu = MAX(x, x1);
|
||||
int wu = (x2 - xu + 1) * win.cw;
|
||||
#if ANYSIZE_PATCH
|
||||
xu = win.hborderpx + xu * win.cw;
|
||||
#else
|
||||
xu = borderpx + xu * win.cw;
|
||||
#endif // ANYSIZE_PATCH
|
||||
#if VERTCENTER_PATCH
|
||||
XftDrawRect(xw.draw, fg, xu, winy + win.cyo + dc.font.ascent * chscale + 2, wu, 1);
|
||||
#else
|
||||
XftDrawRect(xw.draw, fg, xu, winy + dc.font.ascent * chscale + 2, wu, 1);
|
||||
#endif // VERTCENTER_PATCH
|
||||
url_draw = (y != url_y2 || x + charlen <= x2);
|
||||
}
|
||||
}
|
||||
#endif // OPENURLONCLICK_PATCH
|
||||
|
||||
#if !WIDE_GLYPHS_PATCH
|
||||
/* Reset clip to none. */
|
||||
XftDrawSetClip(xw.draw, 0);
|
||||
|
@ -2865,6 +2921,9 @@ xsetpointermotion(int set)
|
|||
if (!set && !xw.pointerisvisible)
|
||||
return;
|
||||
#endif // HIDECURSOR_PATCH
|
||||
#if OPENURLONCLICK_PATCH
|
||||
set = 1; /* keep MotionNotify event enabled */
|
||||
#endif // OPENURLONCLICK_PATCH
|
||||
MODBIT(xw.attrs.event_mask, set, PointerMotionMask);
|
||||
XChangeWindowAttributes(xw.dpy, xw.win, CWEventMask, &xw.attrs);
|
||||
}
|
||||
|
@ -2883,8 +2942,15 @@ xsetmode(int set, unsigned int flags)
|
|||
if (win.mode & MODE_MOUSE)
|
||||
XUndefineCursor(xw.dpy, xw.win);
|
||||
else
|
||||
#if HIDECURSOR_PATCH
|
||||
XDefineCursor(xw.dpy, xw.win, xw.vpointer);
|
||||
#else
|
||||
XDefineCursor(xw.dpy, xw.win, cursor);
|
||||
#endif // HIDECURSOR_PATCH
|
||||
}
|
||||
#elif OPENURLONCLICK_PATCH
|
||||
if (win.mode & MODE_MOUSE && xw.pointerisvisible)
|
||||
XDefineCursor(xw.dpy, xw.win, xw.vpointer);
|
||||
#endif // SWAPMOUSE_PATCH
|
||||
if ((win.mode & MODE_REVERSE) != (mode & MODE_REVERSE))
|
||||
redraw();
|
||||
|
@ -3042,9 +3108,26 @@ kpress(XEvent *ev)
|
|||
|
||||
#if HIDECURSOR_PATCH
|
||||
if (xw.pointerisvisible) {
|
||||
#if OPENURLONCLICK_PATCH
|
||||
#if ANYSIZE_PATCH
|
||||
int x = e->x - win.hborderpx;
|
||||
int y = e->y - win.vborderpx;
|
||||
#else
|
||||
int x = e->x - borderpx;
|
||||
int y = e->y - borderpx;
|
||||
#endif // ANYSIZE_PATCH
|
||||
LIMIT(x, 0, win.tw - 1);
|
||||
LIMIT(y, 0, win.th - 1);
|
||||
if (!detecturl(x / win.cw, y / win.ch, 0)) {
|
||||
XDefineCursor(xw.dpy, xw.win, xw.bpointer);
|
||||
xsetpointermotion(1);
|
||||
xw.pointerisvisible = 0;
|
||||
}
|
||||
#else
|
||||
XDefineCursor(xw.dpy, xw.win, xw.bpointer);
|
||||
xsetpointermotion(1);
|
||||
xw.pointerisvisible = 0;
|
||||
#endif // OPENURLONCLICK_PATCH
|
||||
}
|
||||
#endif // HIDECURSOR_PATCH
|
||||
|
||||
|
|
Loading…
Reference in New Issue