diff options
| author | salmonik <raisersoft@proton.me> | 2026-02-06 02:14:51 +0100 |
|---|---|---|
| committer | salmonik <raisersoft@proton.me> | 2026-02-06 02:14:51 +0100 |
| commit | 931e5010d51d1640c61034ada767715a544fa40b (patch) | |
| tree | 46bd0b5c046a4aaf14c7a571425a745d2d12be2b | |
| parent | scrollback (diff) | |
| download | st-931e5010d51d1640c61034ada767715a544fa40b.tar.gz st-931e5010d51d1640c61034ada767715a544fa40b.zip | |
apply scrollback patch and urlclick patch
| -rw-r--r-- | config.def.h | 11 | ||||
| -rw-r--r-- | st.c | 92 | ||||
| -rw-r--r-- | st.h | 9 | ||||
| -rw-r--r-- | x.c | 24 |
4 files changed, 135 insertions, 1 deletions
diff --git a/config.def.h b/config.def.h index 4f0b27e..375c248 100644 --- a/config.def.h +++ b/config.def.h | |||
| @@ -481,3 +481,14 @@ static char ascii_printable[] = | |||
| 481 | " !\"#$%&'()*+,-./0123456789:;<=>?" | 481 | " !\"#$%&'()*+,-./0123456789:;<=>?" |
| 482 | "@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_" | 482 | "@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_" |
| 483 | "`abcdefghijklmnopqrstuvwxyz{|}~"; | 483 | "`abcdefghijklmnopqrstuvwxyz{|}~"; |
| 484 | |||
| 485 | /* | ||
| 486 | * Open urls starting with urlprefixes, contatining urlchars | ||
| 487 | * by passing as ARG1 to urlhandler. | ||
| 488 | */ | ||
| 489 | char* urlhandler = "xdg-open"; | ||
| 490 | char urlchars[] = | ||
| 491 | "ABCDEFGHIJKLMNOPQRSTUVWXYZ" | ||
| 492 | "abcdefghijklmnopqrstuvwxyz" | ||
| 493 | "0123456789-._~:/?#@!$&'*+,;=%"; | ||
| 494 | char* urlprefixes[] = {"http://", "https://", NULL}; | ||
| @@ -652,6 +652,98 @@ getsel(void) | |||
| 652 | return str; | 652 | return str; |
| 653 | } | 653 | } |
| 654 | 654 | ||
| 655 | char * | ||
| 656 | strstrany(char* s, char** strs) { | ||
| 657 | char *match; | ||
| 658 | for (int i = 0; strs[i]; i++) { | ||
| 659 | if ((match = strstr(s, strs[i]))) { | ||
| 660 | return match; | ||
| 661 | } | ||
| 662 | } | ||
| 663 | return NULL; | ||
| 664 | } | ||
| 665 | |||
| 666 | void | ||
| 667 | highlighturls(void) | ||
| 668 | { | ||
| 669 | char *match; | ||
| 670 | char *linestr = calloc(sizeof(char), term.col+1); /* assume ascii */ | ||
| 671 | for (int i = 0; i < term.row; i++) { | ||
| 672 | Line line = TLINE(i); | ||
| 673 | int url_start = -1; | ||
| 674 | for (int j = 0; j < term.col; j++) { | ||
| 675 | if (line[j].u < 127) { | ||
| 676 | linestr[j] = line[j].u; | ||
| 677 | } | ||
| 678 | linestr[term.col] = '\0'; | ||
| 679 | } | ||
| 680 | while ((match = strstrany(linestr + url_start + 1, urlprefixes))) { | ||
| 681 | url_start = match - linestr; | ||
| 682 | for (int c = url_start; c < term.col && strchr(urlchars, linestr[c]); c++) { | ||
| 683 | line[c].mode |= ATTR_URL; | ||
| 684 | tsetdirt(i, c); | ||
| 685 | } | ||
| 686 | } | ||
| 687 | } | ||
| 688 | free(linestr); | ||
| 689 | } | ||
| 690 | |||
| 691 | void | ||
| 692 | unhighlighturls(void) | ||
| 693 | { | ||
| 694 | for (int i = 0; i < term.row; i++) { | ||
| 695 | Line line = TLINE(i); | ||
| 696 | for (int j = 0; j < term.col; j++) { | ||
| 697 | Glyph* g = &line[j]; | ||
| 698 | if (g->mode & ATTR_URL) { | ||
| 699 | g->mode &= ~ATTR_URL; | ||
| 700 | tsetdirt(i, j); | ||
| 701 | } | ||
| 702 | } | ||
| 703 | } | ||
| 704 | return; | ||
| 705 | } | ||
| 706 | |||
| 707 | void | ||
| 708 | followurl(int x, int y) { | ||
| 709 | char *linestr = calloc(sizeof(char), term.col+1); /* assume ascii */ | ||
| 710 | char *match; | ||
| 711 | Line line = TLINE(x); | ||
| 712 | for (int i = 0; i < term.col; i++) { | ||
| 713 | if (line[i].u < 127) { | ||
| 714 | linestr[i] = line[i].u; | ||
| 715 | } | ||
| 716 | linestr[term.col] = '\0'; | ||
| 717 | } | ||
| 718 | int url_start = -1; | ||
| 719 | while ((match = strstrany(linestr + url_start + 1, urlprefixes))) { | ||
| 720 | url_start = match - linestr; | ||
| 721 | int url_end = url_start; | ||
| 722 | for (int c = url_start; c < term.col && strchr(urlchars, linestr[c]); c++) { | ||
| 723 | url_end++; | ||
| 724 | } | ||
| 725 | if (url_start <= y && y < url_end) { | ||
| 726 | linestr[url_end] = '\0'; | ||
| 727 | break; | ||
| 728 | } | ||
| 729 | } | ||
| 730 | if (url_start == -1) { | ||
| 731 | free(linestr); | ||
| 732 | return; | ||
| 733 | } | ||
| 734 | |||
| 735 | pid_t chpid; | ||
| 736 | if ((chpid = fork()) == 0) { | ||
| 737 | if (fork() == 0) | ||
| 738 | execlp(urlhandler, urlhandler, linestr + url_start, NULL); | ||
| 739 | exit(1); | ||
| 740 | } | ||
| 741 | if (chpid > 0) | ||
| 742 | waitpid(chpid, NULL, 0); | ||
| 743 | free(linestr); | ||
| 744 | unhighlighturls(); | ||
| 745 | } | ||
| 746 | |||
| 655 | void | 747 | void |
| 656 | selclear(void) | 748 | selclear(void) |
| 657 | { | 749 | { |
| @@ -35,6 +35,7 @@ enum glyph_attribute { | |||
| 35 | ATTR_WIDE = 1 << 9, | 35 | ATTR_WIDE = 1 << 9, |
| 36 | ATTR_WDUMMY = 1 << 10, | 36 | ATTR_WDUMMY = 1 << 10, |
| 37 | ATTR_BOLD_FAINT = ATTR_BOLD | ATTR_FAINT, | 37 | ATTR_BOLD_FAINT = ATTR_BOLD | ATTR_FAINT, |
| 38 | ATTR_URL = 1 << 14, | ||
| 38 | }; | 39 | }; |
| 39 | 40 | ||
| 40 | enum selection_mode { | 41 | enum selection_mode { |
| @@ -106,6 +107,10 @@ void selextend(int, int, int, int); | |||
| 106 | int selected(int, int); | 107 | int selected(int, int); |
| 107 | char *getsel(void); | 108 | char *getsel(void); |
| 108 | 109 | ||
| 110 | void highlighturls(void); | ||
| 111 | void unhighlighturls(void); | ||
| 112 | void followurl(int, int); | ||
| 113 | |||
| 109 | size_t utf8encode(Rune, char *); | 114 | size_t utf8encode(Rune, char *); |
| 110 | 115 | ||
| 111 | void *xmalloc(size_t); | 116 | void *xmalloc(size_t); |
| @@ -125,3 +130,7 @@ extern unsigned int tabspaces; | |||
| 125 | extern unsigned int defaultfg; | 130 | extern unsigned int defaultfg; |
| 126 | extern unsigned int defaultbg; | 131 | extern unsigned int defaultbg; |
| 127 | extern unsigned int defaultcs; | 132 | extern unsigned int defaultcs; |
| 133 | extern char *urlhandler; | ||
| 134 | extern char urlchars[]; | ||
| 135 | extern char *urlprefixes[]; | ||
| 136 | extern int nurlprefixes; | ||
| @@ -193,6 +193,7 @@ static void usage(void); | |||
| 193 | 193 | ||
| 194 | static void (*handler[LASTEvent])(XEvent *) = { | 194 | static void (*handler[LASTEvent])(XEvent *) = { |
| 195 | [KeyPress] = kpress, | 195 | [KeyPress] = kpress, |
| 196 | [KeyRelease] = kpress, | ||
| 196 | [ClientMessage] = cmessage, | 197 | [ClientMessage] = cmessage, |
| 197 | [ConfigureNotify] = resize, | 198 | [ConfigureNotify] = resize, |
| 198 | [VisibilityNotify] = visibility, | 199 | [VisibilityNotify] = visibility, |
| @@ -455,6 +456,15 @@ mouseaction(XEvent *e, uint release) | |||
| 455 | /* ignore Button<N>mask for Button<N> - it's set on release */ | 456 | /* ignore Button<N>mask for Button<N> - it's set on release */ |
| 456 | uint state = e->xbutton.state & ~buttonmask(e->xbutton.button); | 457 | uint state = e->xbutton.state & ~buttonmask(e->xbutton.button); |
| 457 | 458 | ||
| 459 | if (release == 0 && | ||
| 460 | e->xbutton.button == Button1 && | ||
| 461 | (match(ControlMask, state) || | ||
| 462 | match(ControlMask, state & ~forcemousemod))) { | ||
| 463 | followurl(evrow(e), evcol(e)); | ||
| 464 | return 1; | ||
| 465 | } | ||
| 466 | |||
| 467 | |||
| 458 | for (ms = mshortcuts; ms < mshortcuts + LEN(mshortcuts); ms++) { | 468 | for (ms = mshortcuts; ms < mshortcuts + LEN(mshortcuts); ms++) { |
| 459 | if (ms->release == release && | 469 | if (ms->release == release && |
| 460 | ms->button == e->xbutton.button && | 470 | ms->button == e->xbutton.button && |
| @@ -1498,7 +1508,7 @@ xdrawglyphfontspecs(const XftGlyphFontSpec *specs, Glyph base, int len, int x, i | |||
| 1498 | XftDrawGlyphFontSpec(xw.draw, fg, specs, len); | 1508 | XftDrawGlyphFontSpec(xw.draw, fg, specs, len); |
| 1499 | 1509 | ||
| 1500 | /* Render underline and strikethrough. */ | 1510 | /* Render underline and strikethrough. */ |
| 1501 | if (base.mode & ATTR_UNDERLINE) { | 1511 | if (base.mode & ATTR_UNDERLINE || base.mode & ATTR_URL) { |
| 1502 | XftDrawRect(xw.draw, fg, winx, winy + dc.font.ascent * chscale + 1, | 1512 | XftDrawRect(xw.draw, fg, winx, winy + dc.font.ascent * chscale + 1, |
| 1503 | width, 1); | 1513 | width, 1); |
| 1504 | } | 1514 | } |
| @@ -1880,6 +1890,18 @@ kpress(XEvent *ev) | |||
| 1880 | } else { | 1890 | } else { |
| 1881 | len = XLookupString(e, buf, sizeof buf, &ksym, NULL); | 1891 | len = XLookupString(e, buf, sizeof buf, &ksym, NULL); |
| 1882 | } | 1892 | } |
| 1893 | |||
| 1894 | /* 0. highlight URLs when control held */ | ||
| 1895 | if (ksym == XK_Control_L) { | ||
| 1896 | highlighturls(); | ||
| 1897 | } else if (ev->type == KeyRelease && e->keycode == XKeysymToKeycode(e->display, XK_Control_L)) { | ||
| 1898 | unhighlighturls(); | ||
| 1899 | } | ||
| 1900 | |||
| 1901 | /* KeyRelease not relevant to shortcuts */ | ||
| 1902 | if (ev->type == KeyRelease) | ||
| 1903 | return; | ||
| 1904 | |||
| 1883 | /* 1. shortcuts */ | 1905 | /* 1. shortcuts */ |
| 1884 | for (bp = shortcuts; bp < shortcuts + LEN(shortcuts); bp++) { | 1906 | for (bp = shortcuts; bp < shortcuts + LEN(shortcuts); bp++) { |
| 1885 | if (ksym == bp->keysym && match(bp->mod, e->state)) { | 1907 | if (ksym == bp->keysym && match(bp->mod, e->state)) { |
