From 137bb152b301a0ad7f952e3e9ceb5ee0d5f6719c Mon Sep 17 00:00:00 2001 From: Bakkeby Date: Thu, 20 Feb 2025 10:18:17 +0100 Subject: [PATCH 01/17] Adding drag-n-drop patch --- README.md | 5 ++ config.def.h | 9 ++ patch/drag-n-drop.c | 204 ++++++++++++++++++++++++++++++++++++++++++++ patch/drag-n-drop.h | 5 ++ patch/x_include.c | 3 + patch/x_include.h | 3 + patches.def.h | 5 ++ st.h | 14 +++ x.c | 44 ++++++++++ 9 files changed, 292 insertions(+) create mode 100644 patch/drag-n-drop.c create mode 100644 patch/drag-n-drop.h diff --git a/README.md b/README.md index 3710d13..fcf54db 100644 --- a/README.md +++ b/README.md @@ -15,6 +15,8 @@ Refer to [https://st.suckless.org/](https://st.suckless.org/) for details on the ### Changelog: +2025-02-20 - Added the drag-n-drop patch + 2024-05-31 - Added the anygeometry patch 2024-03-13 - Added the reflow patch and upgraded the netwmicon patch @@ -162,6 +164,9 @@ Refer to [https://st.suckless.org/](https://st.suckless.org/) for details on the - [disable-fonts](https://st.suckless.org/patches/disable_bold_italic_fonts/) - this patch adds the option of disabling bold/italic/roman fonts globally + - [drag-n-drop](https://st.suckless.org/patches/drag-n-drop) + - allows dragging a file into the terminal and have the path printed + - [dynamic-cursor-color](https://st.suckless.org/patches/dynamic-cursor-color/) - this patch makes the cursor color the inverse of the current cell color diff --git a/config.def.h b/config.def.h index 163e042..fb5d57b 100644 --- a/config.def.h +++ b/config.def.h @@ -172,6 +172,15 @@ float alphaUnfocused = 0.6; #endif // ALPHA_FOCUS_HIGHLIGHT_PATCH #endif // ALPHA_PATCH +#if DRAG_AND_DROP_PATCH +/* + * drag and drop escape characters + * + * this will add a '\' before any characters specified in the string. + */ +char *xdndescchar = " !\"#$&'()*;<>?[\\]^`{|}~"; +#endif // DRAG_AND_DROP_PATCH + /* Terminal colors (16 first used in escape sequence) */ static char *colorname[] = { "#011627", /* hard contrast: #1d2021 / soft contrast: #32302f */ diff --git a/patch/drag-n-drop.c b/patch/drag-n-drop.c new file mode 100644 index 0000000..917a9aa --- /dev/null +++ b/patch/drag-n-drop.c @@ -0,0 +1,204 @@ +const char XdndVersion = 5; + +void +xdndsel(XEvent *e) +{ + char* data; + unsigned long result; + + Atom actualType; + int32_t actualFormat; + unsigned long bytesAfter; + XEvent reply = { ClientMessage }; + + reply.xclient.window = xw.XdndSourceWin; + reply.xclient.format = 32; + reply.xclient.data.l[0] = (long) xw.win; + reply.xclient.data.l[2] = 0; + reply.xclient.data.l[3] = 0; + + XGetWindowProperty((Display*) xw.dpy, e->xselection.requestor, + e->xselection.property, 0, LONG_MAX, False, + e->xselection.target, &actualType, &actualFormat, &result, + &bytesAfter, (unsigned char**) &data); + + if (result == 0) + return; + + if (data) { + xdndpastedata(data); + XFree(data); + } + + if (xw.XdndSourceVersion >= 2) { + reply.xclient.message_type = xw.XdndFinished; + reply.xclient.data.l[1] = result; + reply.xclient.data.l[2] = xw.XdndActionCopy; + + XSendEvent((Display*) xw.dpy, xw.XdndSourceWin, False, NoEventMask, + &reply); + XFlush((Display*) xw.dpy); + } +} + +int +xdndurldecode(char *src, char *dest) +{ + char c; + int i = 0; + + while (*src) { + if (*src == '%' && HEX_TO_INT(src[1]) != -1 && HEX_TO_INT(src[2]) != -1) { + /* handle %xx escape sequences in url e.g. %20 == ' ' */ + c = (char)((HEX_TO_INT(src[1]) << 4) | HEX_TO_INT(src[2])); + src += 3; + } else { + c = *src++; + } + if (strchr(xdndescchar, c) != NULL) { + *dest++ = '\\'; + i++; + } + *dest++ = c; + i++; + } + *dest++ = ' '; + *dest = '\0'; + return i + 1; +} + +void +xdndpastedata(char *data) +{ + char *pastedata, *t; + int i = 0; + + pastedata = (char *)malloc(strlen(data) * 2 + 1); + *pastedata = '\0'; + + t = strtok(data, "\n\r"); + while(t != NULL) { + /* Remove 'file://' prefix if it exists */ + if (strncmp(data, "file://", 7) == 0) { + t += 7; + } + i += xdndurldecode(t, pastedata + i); + t = strtok(NULL, "\n\r"); + } + + xsetsel(pastedata); + selpaste(0); +} + +void +xdndenter(XEvent *e) +{ + unsigned long count; + Atom* formats; + Atom real_formats[6]; + Bool list; + Atom actualType; + int32_t actualFormat; + unsigned long bytesAfter; + unsigned long i; + + list = e->xclient.data.l[1] & 1; + + if (list) { + XGetWindowProperty((Display*) xw.dpy, + xw.XdndSourceWin, + xw.XdndTypeList, + 0, + LONG_MAX, + False, + 4, + &actualType, + &actualFormat, + &count, + &bytesAfter, + (unsigned char**) &formats); + } else { + count = 0; + + if (e->xclient.data.l[2] != None) + real_formats[count++] = e->xclient.data.l[2]; + if (e->xclient.data.l[3] != None) + real_formats[count++] = e->xclient.data.l[3]; + if (e->xclient.data.l[4] != None) + real_formats[count++] = e->xclient.data.l[4]; + + formats = real_formats; + } + + for (i = 0; i < count; i++) { + if (formats[i] == xw.XtextUriList || formats[i] == xw.XtextPlain) { + xw.XdndSourceFormat = formats[i]; + break; + } + } + + if (list) + XFree(formats); +} + +void +xdndpos(XEvent *e) +{ + const int32_t xabs = (e->xclient.data.l[2] >> 16) & 0xffff; + const int32_t yabs = (e->xclient.data.l[2]) & 0xffff; + Window dummy; + int32_t xpos, ypos; + XEvent reply = { ClientMessage }; + + reply.xclient.window = xw.XdndSourceWin; + reply.xclient.format = 32; + reply.xclient.data.l[0] = (long) xw.win; + reply.xclient.data.l[2] = 0; + reply.xclient.data.l[3] = 0; + + XTranslateCoordinates((Display*) xw.dpy, + XDefaultRootWindow((Display*) xw.dpy), + (Window) xw.win, + xabs, yabs, + &xpos, &ypos, + &dummy); + + reply.xclient.message_type = xw.XdndStatus; + + if (xw.XdndSourceFormat) { + reply.xclient.data.l[1] = 1; + if (xw.XdndSourceVersion >= 2) + reply.xclient.data.l[4] = xw.XdndActionCopy; + } + + XSendEvent((Display*) xw.dpy, xw.XdndSourceWin, False, NoEventMask, + &reply); + XFlush((Display*) xw.dpy); +} + +void +xdnddrop(XEvent *e) +{ + Time time = CurrentTime; + XEvent reply = { ClientMessage }; + + reply.xclient.window = xw.XdndSourceWin; + reply.xclient.format = 32; + reply.xclient.data.l[0] = (long) xw.win; + reply.xclient.data.l[2] = 0; + reply.xclient.data.l[3] = 0; + + if (xw.XdndSourceFormat) { + if (xw.XdndSourceVersion >= 1) + time = e->xclient.data.l[2]; + + XConvertSelection((Display*) xw.dpy, xw.XdndSelection, + xw.XdndSourceFormat, xw.XdndSelection, (Window) xw.win, time); + } else if (xw.XdndSourceVersion >= 2) { + reply.xclient.message_type = xw.XdndFinished; + + XSendEvent((Display*) xw.dpy, xw.XdndSourceWin, + False, NoEventMask, &reply); + XFlush((Display*) xw.dpy); + } +} diff --git a/patch/drag-n-drop.h b/patch/drag-n-drop.h new file mode 100644 index 0000000..ee8e82b --- /dev/null +++ b/patch/drag-n-drop.h @@ -0,0 +1,5 @@ +static void xdndenter(XEvent *); +static void xdndpos(XEvent *); +static void xdnddrop(XEvent *); +static void xdndsel(XEvent *); +static void xdndpastedata(char *); diff --git a/patch/x_include.c b/patch/x_include.c index 30b77da..20f083b 100644 --- a/patch/x_include.c +++ b/patch/x_include.c @@ -8,6 +8,9 @@ #if BOXDRAW_PATCH #include "boxdraw.c" #endif +#if DRAG_AND_DROP_PATCH +#include "drag-n-drop.c" +#endif #if OPENCOPIED_PATCH #include "opencopied.c" #endif diff --git a/patch/x_include.h b/patch/x_include.h index 78290e1..fef502d 100644 --- a/patch/x_include.h +++ b/patch/x_include.h @@ -8,6 +8,9 @@ #if BOXDRAW_PATCH #include "boxdraw.h" #endif +#if DRAG_AND_DROP_PATCH +#include "drag-n-drop.h" +#endif #if OPENCOPIED_PATCH #include "opencopied.h" #endif diff --git a/patches.def.h b/patches.def.h index 46834e8..6f02a04 100644 --- a/patches.def.h +++ b/patches.def.h @@ -144,6 +144,11 @@ */ #define DISABLE_ROMAN_FONTS_PATCH 0 +/* Allows dragging a file into the terminal and have the path printed. + * https://st.suckless.org/patches/drag-n-drop + */ +#define DRAG_AND_DROP_PATCH 0 + /* This patch makes the cursor color the inverse of the current cell color. * https://st.suckless.org/patches/dynamic-cursor-color/ */ diff --git a/st.h b/st.h index 0682a0e..0464f24 100644 --- a/st.h +++ b/st.h @@ -37,6 +37,12 @@ #define HISTSIZE 2000 #endif // SCROLLBACK_PATCH | REFLOW_PATCH +#if DRAG_AND_DROP_PATCH +#define HEX_TO_INT(c) ((c) >= '0' && (c) <= '9' ? (c) - '0' : \ + (c) >= 'a' && (c) <= 'f' ? (c) - 'a' + 10 : \ + (c) >= 'A' && (c) <= 'F' ? (c) - 'A' + 10 : -1) +#endif // DRAG_AND_DROP_PATCH + enum glyph_attribute { ATTR_NULL = 0, ATTR_SET = 1 << 0, @@ -247,6 +253,14 @@ typedef struct { GlyphFontSeq *specseq; #endif // LIGATURES_PATCH Atom xembed, wmdeletewin, netwmname, netwmiconname, netwmpid; + #if DRAG_AND_DROP_PATCH + Atom XdndTypeList, XdndSelection, XdndEnter, XdndPosition, XdndStatus, + XdndLeave, XdndDrop, XdndFinished, XdndActionCopy, XdndActionMove, + XdndActionLink, XdndActionAsk, XdndActionPrivate, XtextUriList, + XtextPlain, XdndAware; + int64_t XdndSourceWin, XdndSourceVersion; + int32_t XdndSourceFormat; + #endif // DRAG_AND_DROP_PATCH #if FULLSCREEN_PATCH Atom netwmstate, netwmfullscreen; #endif // FULLSCREEN_PATCH diff --git a/x.c b/x.c index 7bf72e9..124e30a 100644 --- a/x.c +++ b/x.c @@ -603,6 +603,13 @@ selnotify(XEvent *e) if (property == None) return; + #if DRAG_AND_DROP_PATCH + if (property == xw.XdndSelection) { + xdndsel(e); + return; + } + #endif // DRAG_AND_DROP_PATCH + do { if (XGetWindowProperty(xw.dpy, xw.win, property, ofs, BUFSIZ/4, False, AnyPropertyType, @@ -1683,6 +1690,28 @@ xinit(int cols, int rows) xw.netwmfullscreen = XInternAtom(xw.dpy, "_NET_WM_STATE_FULLSCREEN", False); #endif // FULLSCREEN_PATCH + #if DRAG_AND_DROP_PATCH + /* Xdnd setup */ + xw.XdndTypeList = XInternAtom(xw.dpy, "XdndTypeList", 0); + xw.XdndSelection = XInternAtom(xw.dpy, "XdndSelection", 0); + xw.XdndEnter = XInternAtom(xw.dpy, "XdndEnter", 0); + xw.XdndPosition = XInternAtom(xw.dpy, "XdndPosition", 0); + xw.XdndStatus = XInternAtom(xw.dpy, "XdndStatus", 0); + xw.XdndLeave = XInternAtom(xw.dpy, "XdndLeave", 0); + xw.XdndDrop = XInternAtom(xw.dpy, "XdndDrop", 0); + xw.XdndFinished = XInternAtom(xw.dpy, "XdndFinished", 0); + xw.XdndActionCopy = XInternAtom(xw.dpy, "XdndActionCopy", 0); + xw.XdndActionMove = XInternAtom(xw.dpy, "XdndActionMove", 0); + xw.XdndActionLink = XInternAtom(xw.dpy, "XdndActionLink", 0); + xw.XdndActionAsk = XInternAtom(xw.dpy, "XdndActionAsk", 0); + xw.XdndActionPrivate = XInternAtom(xw.dpy, "XdndActionPrivate", 0); + xw.XtextUriList = XInternAtom((Display*) xw.dpy, "text/uri-list", 0); + xw.XtextPlain = XInternAtom((Display*) xw.dpy, "text/plain", 0); + xw.XdndAware = XInternAtom(xw.dpy, "XdndAware", 0); + XChangeProperty(xw.dpy, xw.win, xw.XdndAware, 4, 32, PropModeReplace, + &XdndVersion, 1); + #endif // DRAG_AND_DROP_PATCH + win.mode = MODE_NUMLOCK; resettitle(); xhints(); @@ -3713,6 +3742,21 @@ cmessage(XEvent *e) } else if (e->xclient.data.l[0] == xw.wmdeletewin) { ttyhangup(); exit(0); + #if DRAG_AND_DROP_PATCH + } else if (e->xclient.message_type == xw.XdndEnter) { + xw.XdndSourceWin = e->xclient.data.l[0]; + xw.XdndSourceVersion = e->xclient.data.l[1] >> 24; + xw.XdndSourceFormat = None; + if (xw.XdndSourceVersion > 5) + return; + xdndenter(e); + } else if (e->xclient.message_type == xw.XdndPosition + && xw.XdndSourceVersion <= 5) { + xdndpos(e); + } else if (e->xclient.message_type == xw.XdndDrop + && xw.XdndSourceVersion <= 5) { + xdnddrop(e); + #endif // DRAG_AND_DROP_PATCH } } From c7503f57378e6d85e484a32f822ce002817dd03b Mon Sep 17 00:00:00 2001 From: Bakkeby Date: Thu, 20 Feb 2025 22:36:51 +0100 Subject: [PATCH 02/17] Adding open selected text patch --- README.md | 5 ++++- config.def.h | 3 +++ patch/openselectedtext.c | 13 +++++++++++++ patch/openselectedtext.h | 3 +++ patch/x_include.c | 3 +++ patch/x_include.h | 3 +++ patches.def.h | 5 +++++ 7 files changed, 34 insertions(+), 1 deletion(-) create mode 100644 patch/openselectedtext.c create mode 100644 patch/openselectedtext.h diff --git a/README.md b/README.md index fcf54db..44d7b6e 100644 --- a/README.md +++ b/README.md @@ -15,7 +15,7 @@ Refer to [https://st.suckless.org/](https://st.suckless.org/) for details on the ### Changelog: -2025-02-20 - Added the drag-n-drop patch +2025-02-20 - Added the drag-n-drop and open-selected-text patches 2024-05-31 - Added the anygeometry patch @@ -235,6 +235,9 @@ Refer to [https://st.suckless.org/](https://st.suckless.org/) for details on the - [open-copied-url](https://st.suckless.org/patches/open_copied_url/) - open contents of the clipboard in a user-defined browser + - [open-selected-text](https://st.suckless.org/patches/open_selected_text) + - open the selected text using `xdg-open` + - [openurlonclick](https://www.reddit.com/r/suckless/comments/cc83om/st_open_url/) - allows for URLs to be opened directly when you click on them diff --git a/config.def.h b/config.def.h index fb5d57b..3a98fb2 100644 --- a/config.def.h +++ b/config.def.h @@ -401,6 +401,9 @@ static uint forcemousemod = ShiftMask; */ static MouseShortcut mshortcuts[] = { /* mask button function argument release screen */ + #if OPEN_SELECTED_TEXT_PATCH + { ControlMask, Button2, selopen, {.i = 0}, 1 }, + #endif // OPEN_SELECTED_TEXT_PATCH #if CLIPBOARD_PATCH { XK_ANY_MOD, Button2, clippaste, {.i = 0}, 1 }, #else diff --git a/patch/openselectedtext.c b/patch/openselectedtext.c new file mode 100644 index 0000000..42d40e2 --- /dev/null +++ b/patch/openselectedtext.c @@ -0,0 +1,13 @@ +void +selopen(const Arg *dummy) +{ + pid_t chpid; + + if ((chpid = fork()) == 0) { + if (fork() == 0) + execlp("xdg-open", "xdg-open", getsel(), NULL); + exit(1); + } + if (chpid > 0) + waitpid(chpid, NULL, 0); +} diff --git a/patch/openselectedtext.h b/patch/openselectedtext.h new file mode 100644 index 0000000..3dccbc6 --- /dev/null +++ b/patch/openselectedtext.h @@ -0,0 +1,3 @@ +#include + +static void selopen(const Arg *); diff --git a/patch/x_include.c b/patch/x_include.c index 20f083b..5a0ba58 100644 --- a/patch/x_include.c +++ b/patch/x_include.c @@ -38,6 +38,9 @@ #elif NETWMICON_LEGACY_PATCH #include "netwmicon_legacy.c" #endif +#if OPEN_SELECTED_TEXT_PATCH +#include "openselectedtext.c" +#endif #if OPENURLONCLICK_PATCH #include "openurlonclick.c" #endif diff --git a/patch/x_include.h b/patch/x_include.h index fef502d..ed10e8c 100644 --- a/patch/x_include.h +++ b/patch/x_include.h @@ -35,6 +35,9 @@ #if NETWMICON_PATCH || NETWMICON_FF_PATCH || NETWMICON_LEGACY_PATCH #include "netwmicon.h" #endif +#if OPEN_SELECTED_TEXT_PATCH +#include "openselectedtext.h" +#endif #if RIGHTCLICKTOPLUMB_PATCH #include "rightclicktoplumb_x.h" #endif diff --git a/patches.def.h b/patches.def.h index 6f02a04..b340aaa 100644 --- a/patches.def.h +++ b/patches.def.h @@ -306,6 +306,11 @@ */ #define OPENCOPIED_PATCH 0 +/* Open the selected text using xdg-open. + * https://st.suckless.org/patches/open_selected_text/ + */ +#define OPEN_SELECTED_TEXT_PATCH 0 + /* This patch allows for URLs to be opened directly when you click on them. This may not work with * all terminal applications. * From 50286bb86111af84b9a24200fe30c50179948bf5 Mon Sep 17 00:00:00 2001 From: Bakkeby Date: Wed, 26 Feb 2025 21:23:09 +0100 Subject: [PATCH 03/17] reflow: addressing selection clearing bug when selection includes the prompt ref. #166 --- patch/reflow.c | 4 ++-- st.c | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/patch/reflow.c b/patch/reflow.c index bf145a6..dac7b9d 100644 --- a/patch/reflow.c +++ b/patch/reflow.c @@ -619,8 +619,8 @@ tclearregion(int x1, int y1, int x2, int y2, int usecurattr) int x, y; /* regionselected() takes relative coordinates */ - if (regionselected(x1+term.scr, y1+term.scr, x2+term.scr, y2+term.scr)) - selremove(); + if (regionselected(x1, y1+term.scr, x2, y2+term.scr)) + selclear(); for (y = y1; y <= y2; y++) { term.dirty[y] = 1; diff --git a/st.c b/st.c index 5efe122..6a7a614 100644 --- a/st.c +++ b/st.c @@ -3434,7 +3434,7 @@ check_control_code: #if REFLOW_PATCH /* selected() takes relative coordinates */ - if (selected(term.c.x + term.scr, term.c.y + term.scr)) + if (selected(term.c.x, term.c.y + term.scr)) selclear(); #else if (selected(term.c.x, term.c.y)) From 3c7bcf616d4b51c0efad016357235da8061b3b4c Mon Sep 17 00:00:00 2001 From: Bakkeby Date: Wed, 26 Feb 2025 21:58:05 +0100 Subject: [PATCH 04/17] selection colors vs dynamic cursor color patch compatibility issue ref. #167 --- x.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/x.c b/x.c index 124e30a..a07b8cd 100644 --- a/x.c +++ b/x.c @@ -2747,7 +2747,7 @@ xdrawcursor(int cx, int cy, Glyph g, int ox, int oy, Glyph og) XRenderColor colbg; #endif // DYNAMIC_CURSOR_COLOR_PATCH - #if !DYNAMIC_CURSOR_COLOR_PATCH + #if !DYNAMIC_CURSOR_COLOR_PATCH || SELECTION_COLORS_PATCH /* remove the old cursor */ if (selected(ox, oy)) #if SELECTION_COLORS_PATCH @@ -2804,7 +2804,7 @@ xdrawcursor(int cx, int cy, Glyph g, int ox, int oy, Glyph og) } #endif // SELECTION_COLORS_PATCH } else { - #if SELECTION_COLORS_PATCH + #if SELECTION_COLORS_PATCH && !DYNAMIC_CURSOR_COLOR_PATCH g.fg = defaultbg; g.bg = defaultcs; drawcol = dc.col[defaultcs]; From 9860867e1091bf5b5559d5bcdf37ff121e99bf75 Mon Sep 17 00:00:00 2001 From: Bakkeby Date: Wed, 26 Feb 2025 22:36:19 +0100 Subject: [PATCH 05/17] Adding dynamic padding patch - a variant of anysize that do not alter size hints ref. #168 --- patches.def.h | 9 +++++++++ st.h | 5 +++++ x.c | 2 +- 3 files changed, 15 insertions(+), 1 deletion(-) diff --git a/patches.def.h b/patches.def.h index b340aaa..ae7ac56 100644 --- a/patches.def.h +++ b/patches.def.h @@ -154,6 +154,15 @@ */ #define DYNAMIC_CURSOR_COLOR_PATCH 0 +/* This is a variant of the anysize patch that explicitly do not change the size increment hints, + * i.e. only keeping the dynamic padding which is the main thing the anysize patch introduces. + * In practice this means that the dynamic padding / anysize functionality only ever comes into + * effect when the size hints are intentionally ignored. + * An example of this would be dwm respecting the size hints of floating windows, but disrespecting + * the size hints when the window is tiled (provided that resizehints config is set to 0). + */ +#define DYNAMIC_PADDING_PATCH 0 + /* Reading and writing st's screen through a pipe, e.g. pass info to dmenu. * https://st.suckless.org/patches/externalpipe/ */ diff --git a/st.h b/st.h index 0464f24..4af046c 100644 --- a/st.h +++ b/st.h @@ -11,6 +11,11 @@ #include #include "patches.h" +#if DYNAMIC_PADDING_PATCH +#undef ANYSIZE_PATCH +#define ANYSIZE_PATCH 1 +#endif // DYNAMIC_PADDING_PATCH + /* macros */ #define MIN(a, b) ((a) < (b) ? (a) : (b)) #define MAX(a, b) ((a) < (b) ? (b) : (a)) diff --git a/x.c b/x.c index a07b8cd..dde2900 100644 --- a/x.c +++ b/x.c @@ -1164,7 +1164,7 @@ xhints(void) sizeh->flags = PSize | PResizeInc | PBaseSize | PMinSize; sizeh->height = win.h; sizeh->width = win.w; - #if ANYSIZE_PATCH || ANYSIZE_SIMPLE_PATCH + #if ANYSIZE_PATCH && !DYNAMIC_PADDING_PATCH || ANYSIZE_SIMPLE_PATCH sizeh->height_inc = 1; sizeh->width_inc = 1; #else From 5d5fe7679504fb8ad1ceff443410fb42176894cf Mon Sep 17 00:00:00 2001 From: Bakkeby Date: Thu, 27 Feb 2025 21:57:48 +0100 Subject: [PATCH 06/17] dynamic cursor color: visual bug on selection ref. #169 --- x.c | 14 ++++++-------- 1 file changed, 6 insertions(+), 8 deletions(-) diff --git a/x.c b/x.c index dde2900..1d5d1e9 100644 --- a/x.c +++ b/x.c @@ -2747,21 +2747,19 @@ xdrawcursor(int cx, int cy, Glyph g, int ox, int oy, Glyph og) XRenderColor colbg; #endif // DYNAMIC_CURSOR_COLOR_PATCH - #if !DYNAMIC_CURSOR_COLOR_PATCH || SELECTION_COLORS_PATCH - /* remove the old cursor */ + #if LIGATURES_PATCH + /* Redraw the line where cursor was previously. + * It will restore the ligatures broken by the cursor. */ + xdrawline(line, 0, oy, len); + #else + /* Remove the old cursor */ if (selected(ox, oy)) #if SELECTION_COLORS_PATCH og.mode |= ATTR_SELECTED; #else og.mode ^= ATTR_REVERSE; #endif // SELECTION_COLORS_PATCH - #endif // DYNAMIC_CURSOR_COLOR_PATCH - #if LIGATURES_PATCH - /* Redraw the line where cursor was previously. - * It will restore the ligatures broken by the cursor. */ - xdrawline(line, 0, oy, len); - #else xdrawglyph(og, ox, oy); #endif // LIGATURES_PATCH From 0aad70af586003c4e81571cf7677d335ca02f13b Mon Sep 17 00:00:00 2001 From: Bakkeby Date: Tue, 4 Mar 2025 10:12:43 +0100 Subject: [PATCH 07/17] dynamic padding - add explicit dependency on the ANYSIZE_PATCH ref. #168 --- patches.def.h | 2 ++ st.h | 5 ----- 2 files changed, 2 insertions(+), 5 deletions(-) diff --git a/patches.def.h b/patches.def.h index ae7ac56..1e92a9f 100644 --- a/patches.def.h +++ b/patches.def.h @@ -160,6 +160,8 @@ * effect when the size hints are intentionally ignored. * An example of this would be dwm respecting the size hints of floating windows, but disrespecting * the size hints when the window is tiled (provided that resizehints config is set to 0). + * + * Note that this patch depends on ANYSIZE_PATCH being enabled to have an effect. */ #define DYNAMIC_PADDING_PATCH 0 diff --git a/st.h b/st.h index 4af046c..0464f24 100644 --- a/st.h +++ b/st.h @@ -11,11 +11,6 @@ #include #include "patches.h" -#if DYNAMIC_PADDING_PATCH -#undef ANYSIZE_PATCH -#define ANYSIZE_PATCH 1 -#endif // DYNAMIC_PADDING_PATCH - /* macros */ #define MIN(a, b) ((a) < (b) ? (a) : (b)) #define MAX(a, b) ((a) < (b) ? (b) : (a)) From 49ac4d1e14a0ac9c45c44efe9ae97bf1c4971f76 Mon Sep 17 00:00:00 2001 From: veltza <106755522+veltza@users.noreply.github.com> Date: Tue, 22 Apr 2025 10:53:53 +0300 Subject: [PATCH 08/17] Prevent SelectionRequests from interfering with blinking cursor (#173) Some old clipboard managers, such as greenclip and parcellite, constantly poll applications with SelectionRequest events, which breaks the internal timer and blinking cursor. This fix prevents these events from causing any disruption. Fixes #172 --- x.c | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/x.c b/x.c index 1d5d1e9..249e056 100644 --- a/x.c +++ b/x.c @@ -3851,8 +3851,12 @@ run(void) xev = 0; while (XPending(xw.dpy)) { - xev = 1; XNextEvent(xw.dpy, &ev); + #if BLINKING_CURSOR_PATCH + xev = (!xev || xev == SelectionRequest) ? ev.type : xev; + #else + xev = 1; + #endif // BLINKING_CURSOR_PATCH if (XFilterEvent(&ev, None)) continue; if (handler[ev.type]) @@ -3879,10 +3883,10 @@ run(void) if (!drawing) { trigger = now; #if BLINKING_CURSOR_PATCH - if (IS_SET(MODE_BLINK)) { - win.mode ^= MODE_BLINK; + if (xev != SelectionRequest) { + win.mode &= ~MODE_BLINK; + lastblink = now; } - lastblink = now; #endif // BLINKING_CURSOR_PATCH drawing = 1; } From a12b73a36fc19941938adafdec1cd1c9f861f3b5 Mon Sep 17 00:00:00 2001 From: Bakkeby Date: Tue, 6 May 2025 14:41:20 +0200 Subject: [PATCH 09/17] Refactoring Xft clipping ref. #175 --- x.c | 35 +++++++++++++++++++---------------- 1 file changed, 19 insertions(+), 16 deletions(-) diff --git a/x.c b/x.c index 249e056..e540322 100644 --- a/x.c +++ b/x.c @@ -2281,20 +2281,9 @@ xdrawglyphfontspecs(const XftGlyphFontSpec *specs, Glyph base, int len, int x, i else #endif // BACKGROUND_IMAGE_PATCH - #if !WIDE_GLYPHS_PATCH - XftDrawRect(xw.draw, bg, winx, winy, width, win.ch); - #endif // WIDE_GLYPHS_PATCH - - /* Set the clip region because Xft is sometimes dirty. */ - r.x = 0; - r.y = 0; - r.height = win.ch; - r.width = width; - XftDrawSetClipRectangles(xw.draw, winx, winy, &r, 1); - - #if WIDE_GLYPHS_PATCH /* Fill the background */ XftDrawRect(xw.draw, bg, winx, winy, width, win.ch); + #if WIDE_GLYPHS_PATCH } #endif // WIDE_GLYPHS_PATCH @@ -2305,13 +2294,27 @@ xdrawglyphfontspecs(const XftGlyphFontSpec *specs, Glyph base, int len, int x, i if (base.mode & ATTR_BOXDRAW) { drawboxes(winx, winy, width / len, win.ch, fg, bg, specs, len); } else { - /* Render the glyphs. */ - XftDrawGlyphFontSpec(xw.draw, fg, specs, len); - } + #endif // BOXDRAW_PATCH + /* Set the clip region because Xft is sometimes dirty. */ + #if WIDE_GLYPHS_PATCH + r.x = 0; + r.y = 0; + r.height = win.ch; + r.width = win.w; + XftDrawSetClipRectangles(xw.draw, 0, winy, &r, 1); #else + r.x = 0; + r.y = 0; + r.height = win.ch; + r.width = width; + XftDrawSetClipRectangles(xw.draw, winx, winy, &r, 1); + #endif // WIDE_GLYPHS_PATCH + #if BOXDRAW_PATCH + } + #endif // BOXDRAW_PATCH + /* Render the glyphs. */ XftDrawGlyphFontSpec(xw.draw, fg, specs, len); - #endif // BOXDRAW_PATCH /* Render underline and strikethrough. */ if (base.mode & ATTR_UNDERLINE) { From 821244c2aefdb2495e2746e84242a6992f13afb3 Mon Sep 17 00:00:00 2001 From: Bakkeby Date: Mon, 16 Jun 2025 22:53:48 +0200 Subject: [PATCH 10/17] OpenBSD compatibility changes --- config.mk | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/config.mk b/config.mk index 832bcff..260fff0 100644 --- a/config.mk +++ b/config.mk @@ -38,7 +38,7 @@ INCS = -I$(X11INC) \ `$(PKG_CONFIG) --cflags fontconfig` \ `$(PKG_CONFIG) --cflags freetype2` \ $(LIGATURES_INC) -LIBS = -L$(X11LIB) -lm -lrt -lX11 -lutil -lXft ${SIXEL_LIBS} ${XRENDER} ${XCURSOR}\ +LIBS = -L$(X11LIB) -lm -lX11 -lutil -lXft ${SIXEL_LIBS} ${XRENDER} ${XCURSOR}\ `$(PKG_CONFIG) --libs fontconfig` \ `$(PKG_CONFIG) --libs freetype2` \ $(LIGATURES_LIBS) \ @@ -50,10 +50,7 @@ STCFLAGS = $(INCS) $(STCPPFLAGS) $(CPPFLAGS) $(CFLAGS) STLDFLAGS = $(LIBS) $(LDFLAGS) # OpenBSD: -#CPPFLAGS = -DVERSION=\"$(VERSION)\" -D_XOPEN_SOURCE=600 -D_BSD_SOURCE -#LIBS = -L$(X11LIB) -lm -lX11 -lutil -lXft \ -# `pkg-config --libs fontconfig` \ -# `pkg-config --libs freetype2` +#CPPFLAGS = $(STCPPFLAGS) -D_XOPEN_SOURCE=600 #MANPREFIX = ${PREFIX}/man # compiler and linker From 4da3d6d87c83c70dae784da28deed9f980950e2f Mon Sep 17 00:00:00 2001 From: Bakkeby Date: Tue, 8 Jul 2025 10:27:50 +0200 Subject: [PATCH 11/17] boxdraw: fix for rendering errors following commit f8e451e ref. #180 --- x.c | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/x.c b/x.c index e540322..0de0ac7 100644 --- a/x.c +++ b/x.c @@ -2309,13 +2309,14 @@ xdrawglyphfontspecs(const XftGlyphFontSpec *specs, Glyph base, int len, int x, i r.width = width; XftDrawSetClipRectangles(xw.draw, winx, winy, &r, 1); #endif // WIDE_GLYPHS_PATCH - #if BOXDRAW_PATCH - } - #endif // BOXDRAW_PATCH /* Render the glyphs. */ XftDrawGlyphFontSpec(xw.draw, fg, specs, len); + #if BOXDRAW_PATCH + } + #endif // BOXDRAW_PATCH + /* Render underline and strikethrough. */ if (base.mode & ATTR_UNDERLINE) { #if UNDERCURL_PATCH From f7354190f7a5329c3a615f3edc01cdc4123feaf9 Mon Sep 17 00:00:00 2001 From: Bakkeby Date: Tue, 8 Jul 2025 10:42:24 +0200 Subject: [PATCH 12/17] sync: adding mode 2026 for the sync patch to allow syncing to be controlled by programs ref. #179 --- st.c | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/st.c b/st.c index 6a7a614..d5d6eca 100644 --- a/st.c +++ b/st.c @@ -2039,6 +2039,15 @@ tsetmode(int priv, int set, const int *args, int narg) MODBIT(term.mode, set, MODE_SIXEL_CUR_RT); break; #endif // SIXEL_PATCH + #if SYNC_PATCH + case 2026: + if (set == 1) { + tsync_begin(); + } else if (set == 2) { + tsync_end(); + } + break; + #endif // SYNC_PATCH default: fprintf(stderr, "erresc: unknown private set/reset mode %d\n", From 9be6da03707c2bbde735b7f84a48591159244eff Mon Sep 17 00:00:00 2001 From: Bakkeby Date: Tue, 8 Jul 2025 15:11:17 +0200 Subject: [PATCH 13/17] sync: mode 2026 correction ref. #179 --- st.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/st.c b/st.c index d5d6eca..794fdcb 100644 --- a/st.c +++ b/st.c @@ -2041,9 +2041,9 @@ tsetmode(int priv, int set, const int *args, int narg) #endif // SIXEL_PATCH #if SYNC_PATCH case 2026: - if (set == 1) { + if (set) { tsync_begin(); - } else if (set == 2) { + } else { tsync_end(); } break; From ad3c3e774cfbc3b0eaa14854cedfc6e109ee579d Mon Sep 17 00:00:00 2001 From: Bakkeby Date: Tue, 8 Jul 2025 19:16:24 +0200 Subject: [PATCH 14/17] sync: add support for DECRQM queries for synchronization ref. #179 --- st.c | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/st.c b/st.c index 794fdcb..fadacf2 100644 --- a/st.c +++ b/st.c @@ -2444,6 +2444,23 @@ csihandle(void) goto unknown; } break; + #if SYNC_PATCH + case '$': /* DECRQM -- DEC Request Mode (private) */ + if (csiescseq.mode[1] == 'p' && csiescseq.priv) { + switch (csiescseq.arg[0]) { + #if SYNC_PATCH + case 2026: + /* https://gist.github.com/christianparpart/d8a62cc1ab659194337d73e399004036 */ + ttywrite(su ? "\033[?2026;1$y" : "\033[?2026;2$y", 11, 0); + break; + #endif // SYNC_PATCH + default: + goto unknown; + } + break; + } + goto unknown; + #endif // SYNC_PATCH case 'r': /* DECSTBM -- Set Scrolling Region */ if (csiescseq.priv) { goto unknown; From 51d32a9a77d50a0c2e7730d46dc989e228b2292b Mon Sep 17 00:00:00 2001 From: Laurent Cheylus Date: Wed, 9 Jul 2025 09:45:24 +0200 Subject: [PATCH 15/17] Do not interpret CSI ? u as DECRC (#181) The kitty keyboard protocol docs recommend CSI ? u to query support for that protocol, see https://sw.kovidgoyal.net/kitty/keyboard-protocol/ For better or worse, fish shell uses this query to work around bugs in other terminals triggered by requesting that protocol via CSI = 5 u. Unfortunately, st interprets CSI ? u as DECRC (restore cursor position). reproduce with 'printf "\x1b[?u"; cat'. fish could work around this by switching to the alternate screen before running this query; but that might cause tearing on terminals that don't support Synchronized Output. I'm not sure. In the meantime, let's correct our parser. This adds a redundant else-after-return, for consistency with the surrounding code. ref. https://git.suckless.org/st/commit/98610fcd37f655d44586323dc86c1d013c2798ce.html Signed-off-by: Laurent Cheylus --- st.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/st.c b/st.c index fadacf2..1be93f2 100644 --- a/st.c +++ b/st.c @@ -2524,7 +2524,11 @@ csihandle(void) break; #endif // CSI_22_23_PATCH | SIXEL_PATCH case 'u': /* DECRC -- Restore cursor position (ANSI.SYS) */ - tcursor(CURSOR_LOAD); + if (csiescseq.priv) { + goto unknown; + } else { + tcursor(CURSOR_LOAD); + } break; case ' ': switch (csiescseq.mode[1]) { From 02627756f1916a289b5f5f0f9c4d56764914b19d Mon Sep 17 00:00:00 2001 From: Bakkeby Date: Wed, 9 Jul 2025 09:47:04 +0200 Subject: [PATCH 16/17] Bump to 98610fc --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 44d7b6e..ec590cb 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,4 @@ -Similar to [dwm-flexipatch](https://github.com/bakkeby/dwm-flexipatch) this st 0.9.2 (6009e6e, 2024-11-25) project has a different take on st patching. It uses preprocessor directives to decide whether or not to include a patch during build time. Essentially this means that this build, for better or worse, contains both the patched _and_ the original code. The aim being that you can select which patches to include and the build will contain that code and nothing more. +Similar to [dwm-flexipatch](https://github.com/bakkeby/dwm-flexipatch) this st 0.9.2 (98610fc, 2025-01-26) project has a different take on st patching. It uses preprocessor directives to decide whether or not to include a patch during build time. Essentially this means that this build, for better or worse, contains both the patched _and_ the original code. The aim being that you can select which patches to include and the build will contain that code and nothing more. For example to include the `alpha` patch then you would only need to flip this setting from 0 to 1 in [patches.h](https://github.com/bakkeby/st-flexipatch/blob/master/patches.def.h): ```c From 0eeee01496e62a629bba52b0d6514d5437865048 Mon Sep 17 00:00:00 2001 From: Antoine Vaure Date: Fri, 3 Oct 2025 13:19:00 +0200 Subject: [PATCH 17/17] change default colors for light --- config.def.h | 42 +++++++++++++++++++++--------------------- 1 file changed, 21 insertions(+), 21 deletions(-) diff --git a/config.def.h b/config.def.h index 3a98fb2..426a496 100644 --- a/config.def.h +++ b/config.def.h @@ -162,7 +162,7 @@ unsigned int tabspaces = 8; #if ALPHA_PATCH /* bg opacity */ -float alpha = 0.8; +float alpha = 0.9; #if ALPHA_GRADIENT_PATCH float grad_alpha = 0.54; //alpha value that'll change float stat_alpha = 0.46; //constant alpha value that'll get added to grad_alpha @@ -183,28 +183,28 @@ char *xdndescchar = " !\"#$&'()*;<>?[\\]^`{|}~"; /* Terminal colors (16 first used in escape sequence) */ static char *colorname[] = { - "#011627", /* hard contrast: #1d2021 / soft contrast: #32302f */ - "#d3423e", - "#2aa298", - "#daaa01", - "#4876d6", - "#403f53", - "#08916a", - "#7a8181", - "#7a8181", - "#f76e6e", - "#49d0c5", - "#dac26b", - "#5ca7e4", - "#697098", - "#00c990", - "#989fb1", + "#000000", + "#ff3333", + "#86b200", + "#f19618", + "#41a6d9", + "#f07078", + "#4cbe99", + "#ffffff", + "#323232", + "#ff6565", + "#b8e532", + "#ffc849", + "#73d7ff", + "#ffa3aa", + "#7ff0cb", + "#ffffff", [255] = 0, /* more colors can be added after 255 to use with DefaultXX */ - "#403f53", /* 256 -> cursor */ - "#f2f2f2", /* 257 -> rev cursor*/ - "#ffffff", /* 258 -> bg */ - "#403f53", /* 259 -> fg */ + "#ff6900", /* 256 -> cursor */ + "#f0ede4", /* 257 -> rev cursor*/ + "#fafafa", /* 258 -> bg */ + "#5b6673", /* 259 -> fg */ }; #if DARKMAN_PATCH