diff --git a/README.md b/README.md index 8af34be..64de0a3 100644 --- a/README.md +++ b/README.md @@ -11,7 +11,7 @@ Refer to [https://dwm.suckless.org/](https://st.suckless.org/) for details on th ### Changelog: -2019-09-16 - Added alpha, anysize and bold-is-not-bright patches +2019-09-16 - Added alpha, anysize, bold-is-not-bright, clipboard, copyurl and disable-fonts patches ### Patches included: @@ -23,4 +23,15 @@ Refer to [https://dwm.suckless.org/](https://st.suckless.org/) for details on th - [bold-is-not-bright](https://st.suckless.org/patches/bold-is-not-bright/) - by default bold text is rendered with a bold font in the bright variant of the current color - - this patch makes bold text rendered simply as bold, leaving the color unaffected \ No newline at end of file + - this patch makes bold text rendered simply as bold, leaving the color unaffected + + - [clipboard](https://st.suckless.org/patches/clipboard/) + - by default st only sets PRIMARY on selection + - this patch makes st set CLIPBOARD on selection + + - [copyurl](https://st.suckless.org/patches/copyurl/) + - this patch allows you to select and copy the last URL displayed with Mod+l + - multiple invocations cycle through the available URLs + + - [disable-fonts](https://st.suckless.org/patches/disable_bold_italic_fonts/) + - this patch adds the option of disabling bold/italic/roman fonts globally \ No newline at end of file diff --git a/config.def.h b/config.def.h index 1dd981a..8d38275 100644 --- a/config.def.h +++ b/config.def.h @@ -195,6 +195,9 @@ static Shortcut shortcuts[] = { { ShiftMask, XK_Insert, selpaste, {.i = 0} }, #endif // CLIPBOARD_PATCH { TERMMOD, XK_Num_Lock, numlock, {.i = 0} }, + #if COPYURL_PATCH || COPYURL_HIGHLIGHT_SELECTED_URLS_PATCH + { MODKEY, XK_l, copyurl, {.i = 0} }, + #endif // COPYURL_PATCH }; /* diff --git a/patch/copyurl.c b/patch/copyurl.c new file mode 100644 index 0000000..ded3238 --- /dev/null +++ b/patch/copyurl.c @@ -0,0 +1,185 @@ +#if COPYURL_HIGHLIGHT_SELECTED_URLS_PATCH +void +tsetcolor( int row, int start, int end, uint32_t fg, uint32_t bg ) +{ + int i = start; + for( ; i < end; ++i ) + { + term.line[row][i].fg = fg; + term.line[row][i].bg = bg; + } +} + +char * +findlastany(char *str, const char** find, size_t len) +{ + char* found = NULL; + int i = 0; + for(found = str + strlen(str) - 1; found >= str; --found) { + for(i = 0; i < len; i++) { + if(strncmp(found, find[i], strlen(find[i])) == 0) { + return found; + } + } + } + + return NULL; +} + +/* +** Select and copy the previous url on screen (do nothing if there's no url). +** +** FIXME: doesn't handle urls that span multiple lines; will need to add support +** for multiline "getsel()" first +*/ +void +copyurl(const Arg *arg) { + /* () and [] can appear in urls, but excluding them here will reduce false + * positives when figuring out where a given url ends. + */ + static char URLCHARS[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZ" + "abcdefghijklmnopqrstuvwxyz" + "0123456789-._~:/?#@!$&'*+,;=%"; + + static const char* URLSTRINGS[] = {"http://", "https://"}; + + /* remove highlighting from previous selection if any */ + if(sel.ob.x >= 0 && sel.oe.x >= 0) + tsetcolor(sel.nb.y, sel.ob.x, sel.oe.x + 1, defaultfg, defaultbg); + + int i = 0, + row = 0, /* row of current URL */ + col = 0, /* column of current URL start */ + startrow = 0, /* row of last occurrence */ + colend = 0, /* column of last occurrence */ + passes = 0; /* how many rows have been scanned */ + + char *linestr = calloc(sizeof(char), term.col+1); /* assume ascii */ + char *c = NULL, + *match = NULL; + + row = (sel.ob.x >= 0 && sel.nb.y > 0) ? sel.nb.y : term.bot; + LIMIT(row, term.top, term.bot); + startrow = row; + + colend = (sel.ob.x >= 0 && sel.nb.y > 0) ? sel.nb.x : term.col; + LIMIT(colend, 0, term.col); + + /* + ** Scan from (term.bot,term.col) to (0,0) and find + ** next occurrance of a URL + */ + while (passes !=term.bot + 2) { + /* Read in each column of every row until + ** we hit previous occurrence of URL + */ + for (col = 0, i = 0; col < colend; ++col,++i) { + /* assume ascii */ + if (term.line[row][col].u > 127) + continue; + linestr[i] = term.line[row][col].u; + } + linestr[term.col] = '\0'; + + if ((match = findlastany(linestr, URLSTRINGS, + sizeof(URLSTRINGS)/sizeof(URLSTRINGS[0])))) + break; + + if (--row < term.top) + row = term.bot; + + colend = term.col; + passes++; + }; + + if (match) { + /* must happen before trim */ + selclear(); + sel.ob.x = strlen(linestr) - strlen(match); + + /* trim the rest of the line from the url match */ + for (c = match; *c != '\0'; ++c) + if (!strchr(URLCHARS, *c)) { + *c = '\0'; + break; + } + + /* highlight selection by inverting terminal colors */ + tsetcolor(row, sel.ob.x, sel.ob.x + strlen( match ), defaultbg, defaultfg); + + /* select and copy */ + sel.mode = 1; + sel.type = SEL_REGULAR; + sel.oe.x = sel.ob.x + strlen(match)-1; + sel.ob.y = sel.oe.y = row; + selnormalize(); + tsetdirt(sel.nb.y, sel.ne.y); + xsetsel(getsel()); + xclipcopy(); + } + + free(linestr); +} +#else +/* select and copy the previous url on screen (do nothing if there's no url). + * known bug: doesn't handle urls that span multiple lines (wontfix), depends on multiline "getsel()" + * known bug: only finds first url on line (mightfix) + */ +void +copyurl(const Arg *arg) { + /* () and [] can appear in urls, but excluding them here will reduce false + * positives when figuring out where a given url ends. + */ + static char URLCHARS[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZ" + "abcdefghijklmnopqrstuvwxyz" + "0123456789-._~:/?#@!$&'*+,;=%"; + + int i, row, startrow; + char *linestr = calloc(sizeof(char), term.col+1); /* assume ascii */ + char *c, *match = NULL; + + row = (sel.ob.x >= 0 && sel.nb.y > 0) ? sel.nb.y-1 : term.bot; + LIMIT(row, term.top, term.bot); + startrow = row; + + /* find the start of the last url before selection */ + do { + for (i = 0; i < term.col; ++i) { + if (term.line[row][i].u > 127) /* assume ascii */ + continue; + linestr[i] = term.line[row][i].u; + } + linestr[term.col] = '\0'; + if ((match = strstr(linestr, "http://")) + || (match = strstr(linestr, "https://"))) + break; + if (--row < term.top) + row = term.bot; + } while (row != startrow); + + if (match) { + /* must happen before trim */ + selclear(); + sel.ob.x = strlen(linestr) - strlen(match); + + /* trim the rest of the line from the url match */ + for (c = match; *c != '\0'; ++c) + if (!strchr(URLCHARS, *c)) { + *c = '\0'; + break; + } + + /* select and copy */ + sel.mode = 1; + sel.type = SEL_REGULAR; + sel.oe.x = sel.ob.x + strlen(match)-1; + sel.ob.y = sel.oe.y = row; + selnormalize(); + tsetdirt(sel.nb.y, sel.ne.y); + xsetsel(getsel()); + xclipcopy(); + } + + free(linestr); +} +#endif // COPYURL_HIGHLIGHT_SELECTED_URLS_PATCH \ No newline at end of file diff --git a/patch/copyurl.h b/patch/copyurl.h new file mode 100644 index 0000000..88059af --- /dev/null +++ b/patch/copyurl.h @@ -0,0 +1,5 @@ +void copyurl(const Arg *); +#if COPYURL_HIGHLIGHT_SELECTED_URLS_PATCH +static void tsetcolor(int, int, int, uint32_t, uint32_t); +static char * findlastany(char *, const char**, size_t); +#endif // COPYURL_HIGHLIGHT_SELECTED_URLS_PATCH \ No newline at end of file diff --git a/patch/include.c b/patch/include.c new file mode 100644 index 0000000..31b3cd0 --- /dev/null +++ b/patch/include.c @@ -0,0 +1,5 @@ +/* Patches */ + +#if COPYURL_PATCH || COPYURL_HIGHLIGHT_SELECTED_URLS_PATCH +#include "copyurl.c" +#endif \ No newline at end of file diff --git a/patch/include.h b/patch/include.h new file mode 100644 index 0000000..6fe70bb --- /dev/null +++ b/patch/include.h @@ -0,0 +1,5 @@ +/* Patches */ + +#if COPYURL_PATCH || COPYURL_HIGHLIGHT_SELECTED_URLS_PATCH +#include "copyurl.h" +#endif \ No newline at end of file diff --git a/patches.h b/patches.h index 48c8a5d..920d91f 100644 --- a/patches.h +++ b/patches.h @@ -29,4 +29,31 @@ * This patch makes st set CLIPBOARD on selection. * https://st.suckless.org/patches/clipboard/ */ -#define CLIPBOARD_PATCH 1 \ No newline at end of file +#define CLIPBOARD_PATCH 1 + +/* Select and copy the last URL displayed with Mod+l. Multiple invocations cycle through the + * available URLs. + * https://st.suckless.org/patches/copyurl/ + */ +#define COPYURL_PATCH 1 + +/* Select and copy the last URL displayed with Mod+l. Multiple invocations cycle through the + * available URLs. This variant also highlights the selected URLs. + * https://st.suckless.org/patches/copyurl/ + */ +#define COPYURL_HIGHLIGHT_SELECTED_URLS_PATCH 1 + +/* This patch adds the option of disabling bold fonts globally. + * https://st.suckless.org/patches/disable_bold_italic_fonts/ + */ +#define DISABLE_BOLD_FONTS_PATCH 0 + +/* This patch adds the option of disabling italic fonts globally. + * https://st.suckless.org/patches/disable_bold_italic_fonts/ + */ +#define DISABLE_ITALIC_FONTS_PATCH 0 + +/* This patch adds the option of disabling roman fonts globally. + * https://st.suckless.org/patches/disable_bold_italic_fonts/ + */ +#define DISABLE_ROMAN_FONTS_PATCH 0 diff --git a/st.c b/st.c index b8e6077..6daa853 100644 --- a/st.c +++ b/st.c @@ -232,6 +232,8 @@ static uchar utfmask[UTF_SIZ + 1] = {0xC0, 0x80, 0xE0, 0xF0, 0xF8}; static Rune utfmin[UTF_SIZ + 1] = { 0, 0, 0x80, 0x800, 0x10000}; static Rune utfmax[UTF_SIZ + 1] = {0x10FFFF, 0x7F, 0x7FF, 0xFFFF, 0x10FFFF}; +#include "patch/include.h" + ssize_t xwrite(int fd, const char *s, size_t len) { @@ -2602,3 +2604,5 @@ redraw(void) tfulldirt(); draw(); } + +#include "patch/include.c" \ No newline at end of file diff --git a/x.c b/x.c index e8ab079..4892ff2 100644 --- a/x.c +++ b/x.c @@ -57,9 +57,13 @@ static void zoom(const Arg *); static void zoomabs(const Arg *); static void zoomreset(const Arg *); +#include "patch/include.h" + /* config.h for applying patches and the configuration. */ #include "config.h" +//#include "patch/include.c" + /* XEMBED messages */ #define XEMBED_FOCUS_IN 4 #define XEMBED_FOCUS_OUT 5 @@ -1005,17 +1009,23 @@ xloadfonts(char *fontstr, double fontsize) win.ch = ceilf(dc.font.height * chscale); FcPatternDel(pattern, FC_SLANT); + #if !DISABLE_ITALIC_FONTS_PATCH FcPatternAddInteger(pattern, FC_SLANT, FC_SLANT_ITALIC); + #endif // DISABLE_ITALIC_FONTS_PATCH if (xloadfont(&dc.ifont, pattern)) die("can't open font %s\n", fontstr); FcPatternDel(pattern, FC_WEIGHT); + #if !DISABLE_BOLD_FONTS_PATCH FcPatternAddInteger(pattern, FC_WEIGHT, FC_WEIGHT_BOLD); + #endif // DISABLE_BOLD_FONTS_PATCH if (xloadfont(&dc.ibfont, pattern)) die("can't open font %s\n", fontstr); FcPatternDel(pattern, FC_SLANT); + #if !DISABLE_ROMAN_FONTS_PATCH FcPatternAddInteger(pattern, FC_SLANT, FC_SLANT_ROMAN); + #endif // DISABLE_ROMAN_FONTS_PATCH if (xloadfont(&dc.bfont, pattern)) die("can't open font %s\n", fontstr);