From e2ee5ee6114eb74bb08cb9abe5a3020203e92688 Mon Sep 17 00:00:00 2001 From: Michael Forney Date: Fri, 20 Jan 2017 00:06:39 -0800 Subject: [PATCH 001/185] Split X-specific code into x.c --- Makefile | 5 +- config.def.h | 64 +- st.c | 2024 ++------------------------------------------------ st.h | 272 +++++++ win.h | 29 + x.c | 1766 +++++++++++++++++++++++++++++++++++++++++++ 6 files changed, 2167 insertions(+), 1993 deletions(-) create mode 100644 st.h create mode 100644 win.h create mode 100644 x.c diff --git a/Makefile b/Makefile index fb026c4..d8595fe 100644 --- a/Makefile +++ b/Makefile @@ -3,7 +3,7 @@ include config.mk -SRC = st.c +SRC = st.c x.c OBJ = ${SRC:.c=.o} all: options st @@ -21,6 +21,9 @@ config.h: @echo CC $< @${CC} -c ${CFLAGS} $< +st.o: config.h st.h win.h +x.o: arg.h st.h win.h + ${OBJ}: config.h config.mk st: ${OBJ} diff --git a/config.def.h b/config.def.h index a719e36..fd80923 100644 --- a/config.def.h +++ b/config.def.h @@ -5,8 +5,8 @@ * * font: see http://freedesktop.org/software/fontconfig/fontconfig-user.html */ -static char font[] = "Liberation Mono:pixelsize=12:antialias=true:autohint=true"; -static int borderpx = 2; +char font[] = "Liberation Mono:pixelsize=12:antialias=true:autohint=true"; +int borderpx = 2; /* * What program is execed by st depends of these precedence rules: @@ -24,8 +24,8 @@ static char stty_args[] = "stty raw pass8 nl -echo -iexten -cstopb 38400"; static char vtiden[] = "\033[?6c"; /* Kerning / character bounding-box multipliers */ -static float cwscale = 1.0; -static float chscale = 1.0; +float cwscale = 1.0; +float chscale = 1.0; /* * word delimiter string @@ -35,26 +35,26 @@ static float chscale = 1.0; static char worddelimiters[] = " "; /* selection timeouts (in milliseconds) */ -static unsigned int doubleclicktimeout = 300; -static unsigned int tripleclicktimeout = 600; +unsigned int doubleclicktimeout = 300; +unsigned int tripleclicktimeout = 600; /* alt screens */ -static int allowaltscreen = 1; +int allowaltscreen = 1; /* frames per second st should at maximum draw to the screen */ -static unsigned int xfps = 120; -static unsigned int actionfps = 30; +unsigned int xfps = 120; +unsigned int actionfps = 30; /* * blinking timeout (set to 0 to disable blinking) for the terminal blinking * attribute. */ -static unsigned int blinktimeout = 800; +unsigned int blinktimeout = 800; /* * thickness of underline and bar cursors */ -static unsigned int cursorthickness = 2; +unsigned int cursorthickness = 2; /* * bell volume. It must be a value between -100 and 100. Use 0 for disabling @@ -63,7 +63,7 @@ static unsigned int cursorthickness = 2; static int bellvolume = 0; /* default TERM value */ -static char termname[] = "st-256color"; +char termname[] = "st-256color"; /* * spaces per tab @@ -83,7 +83,7 @@ static char termname[] = "st-256color"; static unsigned int tabspaces = 8; /* Terminal colors (16 first used in escape sequence) */ -static const char *colorname[] = { +const char *colorname[] = { /* 8 normal colors */ "black", "red3", @@ -116,10 +116,10 @@ static const char *colorname[] = { * Default colors (colorname index) * foreground, background, cursor, reverse cursor */ -static unsigned int defaultfg = 7; -static unsigned int defaultbg = 0; -static unsigned int defaultcs = 256; -static unsigned int defaultrcs = 257; +unsigned int defaultfg = 7; +unsigned int defaultbg = 0; +unsigned int defaultcs = 256; +unsigned int defaultrcs = 257; /* * Default shape of cursor @@ -128,33 +128,33 @@ static unsigned int defaultrcs = 257; * 6: Bar ("|") * 7: Snowman ("☃") */ -static unsigned int cursorshape = 2; +unsigned int cursorshape = 2; /* * Default columns and rows numbers */ -static unsigned int cols = 80; -static unsigned int rows = 24; +unsigned int cols = 80; +unsigned int rows = 24; /* * Default colour and shape of the mouse cursor */ -static unsigned int mouseshape = XC_xterm; -static unsigned int mousefg = 7; -static unsigned int mousebg = 0; +unsigned int mouseshape = XC_xterm; +unsigned int mousefg = 7; +unsigned int mousebg = 0; /* * Color used to display font attributes when fontconfig selected a font which * doesn't match the ones requested. */ -static unsigned int defaultattr = 11; +unsigned int defaultattr = 11; /* * Internal mouse shortcuts. * Beware that overloading Button1 will disable the selection. */ -static MouseShortcut mshortcuts[] = { +MouseShortcut mshortcuts[] = { /* button mask string */ { Button4, XK_ANY_MOD, "\031" }, { Button5, XK_ANY_MOD, "\005" }, @@ -163,15 +163,15 @@ static MouseShortcut mshortcuts[] = { /* Internal keyboard shortcuts. */ #define MODKEY Mod1Mask -static Shortcut shortcuts[] = { +Shortcut shortcuts[] = { /* mask keysym function argument */ { XK_ANY_MOD, XK_Break, sendbreak, {.i = 0} }, { ControlMask, XK_Print, toggleprinter, {.i = 0} }, { ShiftMask, XK_Print, printscreen, {.i = 0} }, { XK_ANY_MOD, XK_Print, printsel, {.i = 0} }, - { MODKEY|ShiftMask, XK_Prior, xzoom, {.f = +1} }, - { MODKEY|ShiftMask, XK_Next, xzoom, {.f = -1} }, - { MODKEY|ShiftMask, XK_Home, xzoomreset, {.f = 0} }, + { MODKEY|ShiftMask, XK_Prior, zoom, {.f = +1} }, + { MODKEY|ShiftMask, XK_Next, zoom, {.f = -1} }, + { MODKEY|ShiftMask, XK_Home, zoomreset, {.f = 0} }, { ShiftMask, XK_Insert, selpaste, {.i = 0} }, { MODKEY|ShiftMask, XK_Insert, clippaste, {.i = 0} }, { MODKEY|ShiftMask, XK_C, clipcopy, {.i = 0} }, @@ -222,7 +222,7 @@ static uint ignoremod = Mod2Mask|XK_SWITCH_MOD; * Note that if you want to use ShiftMask with selmasks, set this to an other * modifier, set to 0 to not use it. */ -static uint forceselmod = ShiftMask; +uint forceselmod = ShiftMask; /* * This is the huge key array which defines all compatibility to the Linux @@ -451,7 +451,7 @@ static Key key[] = { * ButtonRelease and MotionNotify. * If no match is found, regular selection is used. */ -static uint selmasks[] = { +uint selmasks[] = { [SEL_RECTANGULAR] = Mod1Mask, }; @@ -459,7 +459,7 @@ static uint selmasks[] = { * Printable characters in ASCII, used to estimate the advance width * of single wide characters. */ -static char ascii_printable[] = +char ascii_printable[] = " !\"#$%&'()*+,-./0123456789:;<=>?" "@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_" "`abcdefghijklmnopqrstuvwxyz{|}~"; diff --git a/st.c b/st.c index fbcd9e0..1e4196e 100644 --- a/st.c +++ b/st.c @@ -21,23 +21,21 @@ #include #include #include -#include -#include -#include -#include -#include -#include -#include #include #include -#include "arg.h" +/* X11 */ +#include +#include char *argv0; #define Glyph Glyph_ #define Font Font_ +#include "win.h" +#include "st.h" + #if defined(__linux) #include #elif defined(__OpenBSD__) || defined(__NetBSD__) || defined(__APPLE__) @@ -46,67 +44,24 @@ char *argv0; #include #endif - -/* XEMBED messages */ -#define XEMBED_FOCUS_IN 4 -#define XEMBED_FOCUS_OUT 5 - /* Arbitrary sizes */ #define UTF_INVALID 0xFFFD -#define UTF_SIZ 4 #define ESC_BUF_SIZ (128*UTF_SIZ) #define ESC_ARG_SIZ 16 #define STR_BUF_SIZ ESC_BUF_SIZ #define STR_ARG_SIZ ESC_ARG_SIZ -#define XK_ANY_MOD UINT_MAX -#define XK_NO_MOD 0 -#define XK_SWITCH_MOD (1<<13) /* macros */ -#define MIN(a, b) ((a) < (b) ? (a) : (b)) -#define MAX(a, b) ((a) < (b) ? (b) : (a)) -#define LEN(a) (sizeof(a) / sizeof(a)[0]) #define NUMMAXLEN(x) ((int)(sizeof(x) * 2.56 + 0.5) + 1) #define DEFAULT(a, b) (a) = (a) ? (a) : (b) -#define BETWEEN(x, a, b) ((a) <= (x) && (x) <= (b)) -#define DIVCEIL(n, d) (((n) + ((d) - 1)) / (d)) #define ISCONTROLC0(c) (BETWEEN(c, 0, 0x1f) || (c) == '\177') #define ISCONTROLC1(c) (BETWEEN(c, 0x80, 0x9f)) #define ISCONTROL(c) (ISCONTROLC0(c) || ISCONTROLC1(c)) #define ISDELIM(u) (utf8strchr(worddelimiters, u) != NULL) -#define LIMIT(x, a, b) (x) = (x) < (a) ? (a) : (x) > (b) ? (b) : (x) -#define ATTRCMP(a, b) ((a).mode != (b).mode || (a).fg != (b).fg || \ - (a).bg != (b).bg) -#define IS_SET(flag) ((term.mode & (flag)) != 0) -#define TIMEDIFF(t1, t2) ((t1.tv_sec-t2.tv_sec)*1000 + \ - (t1.tv_nsec-t2.tv_nsec)/1E6) -#define MODBIT(x, set, bit) ((set) ? ((x) |= (bit)) : ((x) &= ~(bit))) - -#define TRUECOLOR(r,g,b) (1 << 24 | (r) << 16 | (g) << 8 | (b)) -#define IS_TRUECOL(x) (1 << 24 & (x)) -#define TRUERED(x) (((x) & 0xff0000) >> 8) -#define TRUEGREEN(x) (((x) & 0xff00)) -#define TRUEBLUE(x) (((x) & 0xff) << 8) /* constants */ #define ISO14755CMD "dmenu -w %lu -p codepoint: ] [;]] []] */ typedef struct { @@ -235,56 +115,6 @@ typedef struct { int narg; /* nb of args */ } STREscape; -/* Internal representation of the screen */ -typedef struct { - int row; /* nb row */ - int col; /* nb col */ - Line *line; /* screen */ - Line *alt; /* alternate screen */ - int *dirty; /* dirtyness of lines */ - XftGlyphFontSpec *specbuf; /* font spec buffer used for rendering */ - TCursor c; /* cursor */ - int top; /* top scroll limit */ - int bot; /* bottom scroll limit */ - int mode; /* terminal mode flags */ - int esc; /* escape state flags */ - char trantbl[4]; /* charset table translation */ - int charset; /* current charset */ - int icharset; /* selected charset for sequence */ - int numlock; /* lock numbers in keyboard */ - int *tabs; -} Term; - -/* Purely graphic info */ -typedef struct { - Display *dpy; - Colormap cmap; - Window win; - Drawable buf; - Atom xembed, wmdeletewin, netwmname, netwmpid; - XIM xim; - XIC xic; - Draw draw; - Visual *vis; - XSetWindowAttributes attrs; - int scr; - int isfixed; /* is fixed geometry? */ - int l, t; /* left and top offset */ - int gm; /* geometry mask */ - int tw, th; /* tty width and height */ - int w, h; /* window width and height */ - int ch; /* char height */ - int cw; /* char width */ - char state; /* focus, redraw, visible */ - int cursor; /* cursor style */ -} XWindow; - -typedef struct { - uint b; - uint mask; - char *s; -} MouseShortcut; - typedef struct { KeySym k; uint mask; @@ -295,89 +125,26 @@ typedef struct { signed char crlf; /* crlf mode */ } Key; -typedef struct { - int mode; - int type; - int snap; - /* - * Selection variables: - * nb – normalized coordinates of the beginning of the selection - * ne – normalized coordinates of the end of the selection - * ob – original coordinates of the beginning of the selection - * oe – original coordinates of the end of the selection - */ - struct { - int x, y; - } nb, ne, ob, oe; - - char *primary, *clipboard; - Atom xtarget; - int alt; - struct timespec tclick1; - struct timespec tclick2; -} Selection; - -typedef union { - int i; - uint ui; - float f; - const void *v; -} Arg; - -typedef struct { - uint mod; - KeySym keysym; - void (*func)(const Arg *); - const Arg arg; -} Shortcut; - /* function definitions used in config.h */ static void clipcopy(const Arg *); static void clippaste(const Arg *); static void numlock(const Arg *); static void selpaste(const Arg *); -static void xzoom(const Arg *); -static void xzoomabs(const Arg *); -static void xzoomreset(const Arg *); +static void zoom(const Arg *); +static void zoomabs(const Arg *); +static void zoomreset(const Arg *); static void printsel(const Arg *); static void printscreen(const Arg *) ; static void iso14755(const Arg *); static void toggleprinter(const Arg *); static void sendbreak(const Arg *); -/* Config.h for applying patches and the configuration. */ +/* config.h for applying patches and the configuration. */ #include "config.h" -/* Font structure */ -typedef struct { - int height; - int width; - int ascent; - int descent; - int badslant; - int badweight; - short lbearing; - short rbearing; - XftFont *match; - FcFontSet *set; - FcPattern *pattern; -} Font; - -/* Drawing Context */ -typedef struct { - Color col[MAX(LEN(colorname), 256)]; - Font font, bfont, ifont, ibfont; - GC gc; -} DC; - -static void die(const char *, ...); -static void draw(void); -static void redraw(void); -static void drawregion(int, int, int, int); static void execsh(void); static void stty(void); static void sigchld(int); -static void run(void); static void csidump(void); static void csihandle(void); @@ -389,7 +156,6 @@ static void strhandle(void); static void strparse(void); static void strreset(void); -static int tattrset(int); static void tprinter(char *, size_t); static void tdumpsel(void); static void tdumpline(int); @@ -403,7 +169,6 @@ static void tinsertblankline(int); static int tlinelen(int); static void tmoveto(int, int); static void tmoveato(int, int); -static void tnew(int, int); static void tnewline(int); static void tputtab(int); static void tputc(Rune); @@ -415,8 +180,6 @@ static void tsetattr(int *, int); static void tsetchar(Rune, Glyph *, int, int); static void tsetscroll(int, int); static void tswapscreen(void); -static void tsetdirt(int, int); -static void tsetdirtattr(int); static void tsetmode(int, int, int *, int); static void tfulldirt(void); static void techo(Rune); @@ -425,151 +188,53 @@ static void tdectest(char ); static void tdefutf8(char); static int32_t tdefcolor(int *, int *, int); static void tdeftran(char); -static inline int match(uint, uint); -static void ttynew(void); -static size_t ttyread(void); -static void ttyresize(void); -static void ttysend(char *, size_t); -static void ttywrite(const char *, size_t); static void tstrsequence(uchar); -static inline ushort sixd_to_16bit(int); -static int xmakeglyphfontspecs(XftGlyphFontSpec *, const Glyph *, int, int, int); -static void xdrawglyphfontspecs(const XftGlyphFontSpec *, Glyph, int, int, int); -static void xdrawglyph(Glyph, int, int); -static void xhints(void); -static void xclear(int, int, int, int); -static void xdrawcursor(void); -static void xinit(void); -static void xloadcols(void); -static int xsetcolorname(int, const char *); -static int xgeommasktogravity(int); -static int xloadfont(Font *, FcPattern *); -static void xloadfonts(char *, double); -static void xsettitle(char *); -static void xresettitle(void); -static void xsetpointermotion(int); -static void xseturgency(int); -static void xsetsel(char *, Time); -static void xunloadfont(Font *); -static void xunloadfonts(void); -static void xresize(int, int); - -static void expose(XEvent *); -static void visibility(XEvent *); -static void unmap(XEvent *); -static char *kmap(KeySym, uint); -static void kpress(XEvent *); -static void cmessage(XEvent *); -static void cresize(int, int); -static void resize(XEvent *); -static void focus(XEvent *); -static void brelease(XEvent *); -static void bpress(XEvent *); -static void bmotion(XEvent *); -static void propnotify(XEvent *); -static void selnotify(XEvent *); -static void selclear(XEvent *); -static void selrequest(XEvent *); - -static void selinit(void); -static void selnormalize(void); -static inline int selected(int, int); -static char *getsel(void); -static void selcopy(Time); static void selscroll(int, int); static void selsnap(int *, int *, int); -static int x2col(int); -static int y2row(int); -static void getbuttoninfo(XEvent *); -static void mousereport(XEvent *); -static size_t utf8decode(char *, Rune *, size_t); static Rune utf8decodebyte(char, size_t *); -static size_t utf8encode(Rune, char *); static char utf8encodebyte(Rune, size_t); static char *utf8strchr(char *s, Rune u); static size_t utf8validate(Rune *, size_t); static ssize_t xwrite(int, const char *, size_t); -static void *xmalloc(size_t); static void *xrealloc(void *, size_t); -static char *xstrdup(char *); - -static void usage(void); - -static void (*handler[LASTEvent])(XEvent *) = { - [KeyPress] = kpress, - [ClientMessage] = cmessage, - [ConfigureNotify] = resize, - [VisibilityNotify] = visibility, - [UnmapNotify] = unmap, - [Expose] = expose, - [FocusIn] = focus, - [FocusOut] = focus, - [MotionNotify] = bmotion, - [ButtonPress] = bpress, - [ButtonRelease] = brelease, -/* - * Uncomment if you want the selection to disappear when you select something - * different in another window. - */ -/* [SelectionClear] = selclear, */ - [SelectionNotify] = selnotify, -/* - * PropertyNotify is only turned on when there is some INCR transfer happening - * for the selection retrieval. - */ - [PropertyNotify] = propnotify, - [SelectionRequest] = selrequest, -}; /* Globals */ -static DC dc; -static XWindow xw; -static Term term; +TermWindow win; +Term term; +Selection sel; +int cmdfd; +pid_t pid; +char **opt_cmd = NULL; +char *opt_class = NULL; +char *opt_embed = NULL; +char *opt_font = NULL; +char *opt_io = NULL; +char *opt_line = NULL; +char *opt_name = NULL; +char *opt_title = NULL; +int oldbutton = 3; /* button event on startup: 3 = release */ + static CSIEscape csiescseq; static STREscape strescseq; -static int cmdfd; -static pid_t pid; -static Selection sel; static int iofd = 1; -static char **opt_cmd = NULL; -static char *opt_class = NULL; -static char *opt_embed = NULL; -static char *opt_font = NULL; -static char *opt_io = NULL; -static char *opt_line = NULL; -static char *opt_name = NULL; -static char *opt_title = NULL; -static int oldbutton = 3; /* button event on startup: 3 = release */ -static char *usedfont = NULL; -static double usedfontsize = 0; -static double defaultfontsize = 0; +char *usedfont = NULL; +double usedfontsize = 0; +double defaultfontsize = 0; static uchar utfbyte[UTF_SIZ + 1] = {0x80, 0, 0xC0, 0xE0, 0xF0}; 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}; -/* Font Ring Cache */ -enum { - FRC_NORMAL, - FRC_ITALIC, - FRC_BOLD, - FRC_ITALICBOLD -}; - -typedef struct { - XftFont *font; - int flags; - Rune unicodep; -} Fontcache; - -/* Fontcache is an array now. A new font will be appended to the array. */ -static Fontcache frc[16]; -static int frclen = 0; +/* config.h array lengths */ +size_t colornamelen = LEN(colorname); +size_t mshortcutslen = LEN(mshortcuts); +size_t shortcutslen = LEN(shortcuts); +size_t selmaskslen = LEN(selmasks); ssize_t xwrite(int fd, const char *s, size_t len) @@ -714,16 +379,13 @@ selinit(void) sel.ob.x = -1; sel.primary = NULL; sel.clipboard = NULL; - sel.xtarget = XInternAtom(xw.dpy, "UTF8_STRING", 0); - if (sel.xtarget == None) - sel.xtarget = XA_STRING; } int x2col(int x) { x -= borderpx; - x /= xw.cw; + x /= win.cw; return LIMIT(x, 0, term.col-1); } @@ -732,7 +394,7 @@ int y2row(int y) { y -= borderpx; - y /= xw.ch; + y /= win.ch; return LIMIT(y, 0, term.row-1); } @@ -867,141 +529,6 @@ selsnap(int *x, int *y, int direction) } } -void -getbuttoninfo(XEvent *e) -{ - int type; - uint state = e->xbutton.state & ~(Button1Mask | forceselmod); - - sel.alt = IS_SET(MODE_ALTSCREEN); - - sel.oe.x = x2col(e->xbutton.x); - sel.oe.y = y2row(e->xbutton.y); - selnormalize(); - - sel.type = SEL_REGULAR; - for (type = 1; type < LEN(selmasks); ++type) { - if (match(selmasks[type], state)) { - sel.type = type; - break; - } - } -} - -void -mousereport(XEvent *e) -{ - int x = x2col(e->xbutton.x), y = y2row(e->xbutton.y), - button = e->xbutton.button, state = e->xbutton.state, - len; - char buf[40]; - static int ox, oy; - - /* from urxvt */ - if (e->xbutton.type == MotionNotify) { - if (x == ox && y == oy) - return; - if (!IS_SET(MODE_MOUSEMOTION) && !IS_SET(MODE_MOUSEMANY)) - return; - /* MOUSE_MOTION: no reporting if no button is pressed */ - if (IS_SET(MODE_MOUSEMOTION) && oldbutton == 3) - return; - - button = oldbutton + 32; - ox = x; - oy = y; - } else { - if (!IS_SET(MODE_MOUSESGR) && e->xbutton.type == ButtonRelease) { - button = 3; - } else { - button -= Button1; - if (button >= 3) - button += 64 - 3; - } - if (e->xbutton.type == ButtonPress) { - oldbutton = button; - ox = x; - oy = y; - } else if (e->xbutton.type == ButtonRelease) { - oldbutton = 3; - /* MODE_MOUSEX10: no button release reporting */ - if (IS_SET(MODE_MOUSEX10)) - return; - if (button == 64 || button == 65) - return; - } - } - - if (!IS_SET(MODE_MOUSEX10)) { - button += ((state & ShiftMask ) ? 4 : 0) - + ((state & Mod4Mask ) ? 8 : 0) - + ((state & ControlMask) ? 16 : 0); - } - - if (IS_SET(MODE_MOUSESGR)) { - len = snprintf(buf, sizeof(buf), "\033[<%d;%d;%d%c", - button, x+1, y+1, - e->xbutton.type == ButtonRelease ? 'm' : 'M'); - } else if (x < 223 && y < 223) { - len = snprintf(buf, sizeof(buf), "\033[M%c%c%c", - 32+button, 32+x+1, 32+y+1); - } else { - return; - } - - ttywrite(buf, len); -} - -void -bpress(XEvent *e) -{ - struct timespec now; - MouseShortcut *ms; - - if (IS_SET(MODE_MOUSE) && !(e->xbutton.state & forceselmod)) { - mousereport(e); - return; - } - - for (ms = mshortcuts; ms < mshortcuts + LEN(mshortcuts); ms++) { - if (e->xbutton.button == ms->b - && match(ms->mask, e->xbutton.state)) { - ttysend(ms->s, strlen(ms->s)); - return; - } - } - - if (e->xbutton.button == Button1) { - clock_gettime(CLOCK_MONOTONIC, &now); - - /* Clear previous selection, logically and visually. */ - selclear(NULL); - sel.mode = SEL_EMPTY; - sel.type = SEL_REGULAR; - sel.oe.x = sel.ob.x = x2col(e->xbutton.x); - sel.oe.y = sel.ob.y = y2row(e->xbutton.y); - - /* - * If the user clicks below predefined timeouts specific - * snapping behaviour is exposed. - */ - if (TIMEDIFF(now, sel.tclick2) <= tripleclicktimeout) { - sel.snap = SNAP_LINE; - } else if (TIMEDIFF(now, sel.tclick1) <= doubleclicktimeout) { - sel.snap = SNAP_WORD; - } else { - sel.snap = 0; - } - selnormalize(); - - if (sel.snap != 0) - sel.mode = SEL_READY; - tsetdirt(sel.nb.y, sel.ne.y); - sel.tclick2 = sel.tclick1; - sel.tclick1 = now; - } -} - char * getsel(void) { @@ -1056,149 +583,26 @@ getsel(void) return str; } -void -selcopy(Time t) -{ - xsetsel(getsel(), t); -} - -void -propnotify(XEvent *e) -{ - XPropertyEvent *xpev; - Atom clipboard = XInternAtom(xw.dpy, "CLIPBOARD", 0); - - xpev = &e->xproperty; - if (xpev->state == PropertyNewValue && - (xpev->atom == XA_PRIMARY || - xpev->atom == clipboard)) { - selnotify(e); - } -} - -void -selnotify(XEvent *e) -{ - ulong nitems, ofs, rem; - int format; - uchar *data, *last, *repl; - Atom type, incratom, property; - - incratom = XInternAtom(xw.dpy, "INCR", 0); - - ofs = 0; - if (e->type == SelectionNotify) { - property = e->xselection.property; - } else if(e->type == PropertyNotify) { - property = e->xproperty.atom; - } else { - return; - } - if (property == None) - return; - - do { - if (XGetWindowProperty(xw.dpy, xw.win, property, ofs, - BUFSIZ/4, False, AnyPropertyType, - &type, &format, &nitems, &rem, - &data)) { - fprintf(stderr, "Clipboard allocation failed\n"); - return; - } - - if (e->type == PropertyNotify && nitems == 0 && rem == 0) { - /* - * If there is some PropertyNotify with no data, then - * this is the signal of the selection owner that all - * data has been transferred. We won't need to receive - * PropertyNotify events anymore. - */ - MODBIT(xw.attrs.event_mask, 0, PropertyChangeMask); - XChangeWindowAttributes(xw.dpy, xw.win, CWEventMask, - &xw.attrs); - } - - if (type == incratom) { - /* - * Activate the PropertyNotify events so we receive - * when the selection owner does send us the next - * chunk of data. - */ - MODBIT(xw.attrs.event_mask, 1, PropertyChangeMask); - XChangeWindowAttributes(xw.dpy, xw.win, CWEventMask, - &xw.attrs); - - /* - * Deleting the property is the transfer start signal. - */ - XDeleteProperty(xw.dpy, xw.win, (int)property); - continue; - } - - /* - * As seen in getsel: - * Line endings are inconsistent in the terminal and GUI world - * copy and pasting. When receiving some selection data, - * replace all '\n' with '\r'. - * FIXME: Fix the computer world. - */ - repl = data; - last = data + nitems * format / 8; - while ((repl = memchr(repl, '\n', last - repl))) { - *repl++ = '\r'; - } - - if (IS_SET(MODE_BRCKTPASTE) && ofs == 0) - ttywrite("\033[200~", 6); - ttysend((char *)data, nitems * format / 8); - if (IS_SET(MODE_BRCKTPASTE) && rem == 0) - ttywrite("\033[201~", 6); - XFree(data); - /* number of 32-bit chunks returned */ - ofs += nitems * format / 32; - } while (rem > 0); - - /* - * Deleting the property again tells the selection owner to send the - * next data chunk in the property. - */ - XDeleteProperty(xw.dpy, xw.win, (int)property); -} - void selpaste(const Arg *dummy) { - XConvertSelection(xw.dpy, XA_PRIMARY, sel.xtarget, XA_PRIMARY, - xw.win, CurrentTime); + xselpaste(); } void clipcopy(const Arg *dummy) { - Atom clipboard; - - if (sel.clipboard != NULL) - free(sel.clipboard); - - if (sel.primary != NULL) { - sel.clipboard = xstrdup(sel.primary); - clipboard = XInternAtom(xw.dpy, "CLIPBOARD", 0); - XSetSelectionOwner(xw.dpy, clipboard, xw.win, CurrentTime); - } + xclipcopy(); } void clippaste(const Arg *dummy) { - Atom clipboard; - - clipboard = XInternAtom(xw.dpy, "CLIPBOARD", 0); - XConvertSelection(xw.dpy, clipboard, sel.xtarget, clipboard, - xw.win, CurrentTime); + xclippaste(); } void -selclear(XEvent *e) +selclear(void) { if (sel.ob.x == -1) return; @@ -1207,120 +611,6 @@ selclear(XEvent *e) tsetdirt(sel.nb.y, sel.ne.y); } -void -selrequest(XEvent *e) -{ - XSelectionRequestEvent *xsre; - XSelectionEvent xev; - Atom xa_targets, string, clipboard; - char *seltext; - - xsre = (XSelectionRequestEvent *) e; - xev.type = SelectionNotify; - xev.requestor = xsre->requestor; - xev.selection = xsre->selection; - xev.target = xsre->target; - xev.time = xsre->time; - if (xsre->property == None) - xsre->property = xsre->target; - - /* reject */ - xev.property = None; - - xa_targets = XInternAtom(xw.dpy, "TARGETS", 0); - if (xsre->target == xa_targets) { - /* respond with the supported type */ - string = sel.xtarget; - XChangeProperty(xsre->display, xsre->requestor, xsre->property, - XA_ATOM, 32, PropModeReplace, - (uchar *) &string, 1); - xev.property = xsre->property; - } else if (xsre->target == sel.xtarget || xsre->target == XA_STRING) { - /* - * xith XA_STRING non ascii characters may be incorrect in the - * requestor. It is not our problem, use utf8. - */ - clipboard = XInternAtom(xw.dpy, "CLIPBOARD", 0); - if (xsre->selection == XA_PRIMARY) { - seltext = sel.primary; - } else if (xsre->selection == clipboard) { - seltext = sel.clipboard; - } else { - fprintf(stderr, - "Unhandled clipboard selection 0x%lx\n", - xsre->selection); - return; - } - if (seltext != NULL) { - XChangeProperty(xsre->display, xsre->requestor, - xsre->property, xsre->target, - 8, PropModeReplace, - (uchar *)seltext, strlen(seltext)); - xev.property = xsre->property; - } - } - - /* all done, send a notification to the listener */ - if (!XSendEvent(xsre->display, xsre->requestor, 1, 0, (XEvent *) &xev)) - fprintf(stderr, "Error sending SelectionNotify event\n"); -} - -void -xsetsel(char *str, Time t) -{ - free(sel.primary); - sel.primary = str; - - XSetSelectionOwner(xw.dpy, XA_PRIMARY, xw.win, t); - if (XGetSelectionOwner(xw.dpy, XA_PRIMARY) != xw.win) - selclear(0); -} - -void -brelease(XEvent *e) -{ - if (IS_SET(MODE_MOUSE) && !(e->xbutton.state & forceselmod)) { - mousereport(e); - return; - } - - if (e->xbutton.button == Button2) { - selpaste(NULL); - } else if (e->xbutton.button == Button1) { - if (sel.mode == SEL_READY) { - getbuttoninfo(e); - selcopy(e->xbutton.time); - } else - selclear(NULL); - sel.mode = SEL_IDLE; - tsetdirt(sel.nb.y, sel.ne.y); - } -} - -void -bmotion(XEvent *e) -{ - int oldey, oldex, oldsby, oldsey; - - if (IS_SET(MODE_MOUSE) && !(e->xbutton.state & forceselmod)) { - mousereport(e); - return; - } - - if (!sel.mode) - return; - - sel.mode = SEL_READY; - oldey = sel.oe.y; - oldex = sel.oe.x; - oldsby = sel.nb.y; - oldsey = sel.ne.y; - getbuttoninfo(e); - - if (oldey != sel.oe.y || oldex != sel.oe.x) - tsetdirt(MIN(sel.nb.y, oldsby), MAX(sel.ne.y, oldsey)); -} - void die(const char *errstr, ...) { @@ -1337,7 +627,6 @@ execsh(void) { char **args, *sh, *prog; const struct passwd *pw; - char buf[sizeof(long) * 8 + 1]; errno = 0; if ((pw = getpwuid(getuid())) == NULL) { @@ -1358,8 +647,6 @@ execsh(void) prog = sh; args = (opt_cmd) ? opt_cmd : (char *[]) {prog, NULL}; - snprintf(buf, sizeof(buf), "%lu", xw.win); - unsetenv("COLUMNS"); unsetenv("LINES"); unsetenv("TERMCAP"); @@ -1368,7 +655,7 @@ execsh(void) setenv("SHELL", sh, 1); setenv("HOME", pw->pw_dir, 1); setenv("TERM", termname, 1); - setenv("WINDOWID", buf, 1); + xsetenv(); signal(SIGCHLD, SIG_DFL); signal(SIGHUP, SIG_DFL); @@ -1606,8 +893,8 @@ ttyresize(void) w.ws_row = term.row; w.ws_col = term.col; - w.ws_xpixel = xw.tw; - w.ws_ypixel = xw.th; + w.ws_xpixel = win.tw; + w.ws_ypixel = win.th; if (ioctl(cmdfd, TIOCSWINSZ, &w) < 0) fprintf(stderr, "Couldn't set window size: %s\n", strerror(errno)); } @@ -1771,7 +1058,7 @@ selscroll(int orig, int n) if (BETWEEN(sel.ob.y, orig, term.bot) || BETWEEN(sel.oe.y, orig, term.bot)) { if ((sel.ob.y += n) > term.bot || (sel.oe.y += n) < term.top) { - selclear(NULL); + selclear(); return; } if (sel.type == SEL_RECTANGULAR) { @@ -1917,7 +1204,7 @@ tclearregion(int x1, int y1, int x2, int y2) for (x = x1; x <= x2; x++) { gp = &term.line[y][x]; if (selected(x, y)) - selclear(NULL); + selclear(); gp->fg = term.c.attr.fg; gp->bg = term.c.attr.bg; gp->mode = 0; @@ -2368,7 +1655,7 @@ csihandle(void) tputtab(csiescseq.arg[0]); break; case 'J': /* ED -- Clear screen */ - selclear(NULL); + selclear(); switch (csiescseq.arg[0]) { case 0: /* below */ tclearregion(term.c.x, term.c.y, term.col-1, term.c.y); @@ -2475,7 +1762,7 @@ csihandle(void) if (!BETWEEN(csiescseq.arg[0], 0, 6)) { goto unknown; } - xw.cursor = csiescseq.arg[0]; + win.cursor = csiescseq.arg[0]; break; default: goto unknown; @@ -2642,12 +1929,13 @@ tprinter(char *s, size_t len) void iso14755(const Arg *arg) { - char cmd[sizeof(ISO14755CMD) + NUMMAXLEN(xw.win)]; + unsigned long id = xwinid(); + char cmd[sizeof(ISO14755CMD) + NUMMAXLEN(id)]; FILE *p; char *us, *e, codepoint[9], uc[UTF_SIZ]; unsigned long utf32; - snprintf(cmd, sizeof(cmd), ISO14755CMD, xw.win); + snprintf(cmd, sizeof(cmd), ISO14755CMD, id); if (!(p = popen(cmd, "r"))) return; @@ -2833,10 +2121,10 @@ tcontrolcode(uchar ascii) /* backwards compatibility to xterm */ strhandle(); } else { - if (!(xw.state & WIN_FOCUSED)) + if (!(win.state & WIN_FOCUSED)) xseturgency(1); if (bellvolume) - XkbBell(xw.dpy, xw.win, bellvolume, (Atom)NULL); + xbell(bellvolume); } break; case '\033': /* ESC */ @@ -2968,7 +2256,7 @@ eschandle(uchar ascii) break; case 'c': /* RIS -- Reset to inital state */ treset(); - xresettitle(); + resettitle(); xloadcols(); break; case '=': /* DECPAM -- Application keypad */ @@ -3109,7 +2397,7 @@ check_control_code: return; } if (sel.ob.x != -1 && BETWEEN(term.c.y, sel.ob.y, sel.oe.y)) - selclear(NULL); + selclear(); gp = &term.line[term.c.y][term.c.x]; if (IS_SET(MODE_WRAP) && (term.c.state & CURSOR_WRAPNEXT)) { @@ -3177,7 +2465,7 @@ tresize(int col, int row) } /* resize to new width */ - term.specbuf = xrealloc(term.specbuf, col * sizeof(XftGlyphFontSpec)); + term.specbuf = xrealloc(term.specbuf, col * sizeof(GlyphFontSpec)); /* resize to new height */ term.line = xrealloc(term.line, row * sizeof(Line)); @@ -3228,326 +2516,16 @@ tresize(int col, int row) } void -xresize(int col, int row) -{ - xw.tw = MAX(1, col * xw.cw); - xw.th = MAX(1, row * xw.ch); - - XFreePixmap(xw.dpy, xw.buf); - xw.buf = XCreatePixmap(xw.dpy, xw.win, xw.w, xw.h, - DefaultDepth(xw.dpy, xw.scr)); - XftDrawChange(xw.draw, xw.buf); - xclear(0, 0, xw.w, xw.h); -} - -ushort -sixd_to_16bit(int x) -{ - return x == 0 ? 0 : 0x3737 + 0x2828 * x; -} - -int -xloadcolor(int i, const char *name, Color *ncolor) -{ - XRenderColor color = { .alpha = 0xffff }; - - if (!name) { - if (BETWEEN(i, 16, 255)) { /* 256 color */ - if (i < 6*6*6+16) { /* same colors as xterm */ - color.red = sixd_to_16bit( ((i-16)/36)%6 ); - color.green = sixd_to_16bit( ((i-16)/6) %6 ); - color.blue = sixd_to_16bit( ((i-16)/1) %6 ); - } else { /* greyscale */ - color.red = 0x0808 + 0x0a0a * (i - (6*6*6+16)); - color.green = color.blue = color.red; - } - return XftColorAllocValue(xw.dpy, xw.vis, - xw.cmap, &color, ncolor); - } else - name = colorname[i]; - } - - return XftColorAllocName(xw.dpy, xw.vis, xw.cmap, name, ncolor); -} - -void -xloadcols(void) -{ - int i; - static int loaded; - Color *cp; - - if (loaded) { - for (cp = dc.col; cp < &dc.col[LEN(dc.col)]; ++cp) - XftColorFree(xw.dpy, xw.vis, xw.cmap, cp); - } - - for (i = 0; i < LEN(dc.col); i++) - if (!xloadcolor(i, NULL, &dc.col[i])) { - if (colorname[i]) - die("Could not allocate color '%s'\n", colorname[i]); - else - die("Could not allocate color %d\n", i); - } - loaded = 1; -} - -int -xsetcolorname(int x, const char *name) -{ - Color ncolor; - - if (!BETWEEN(x, 0, LEN(dc.col))) - return 1; - - - if (!xloadcolor(x, name, &ncolor)) - return 1; - - XftColorFree(xw.dpy, xw.vis, xw.cmap, &dc.col[x]); - dc.col[x] = ncolor; - - return 0; -} - -/* - * Absolute coordinates. - */ -void -xclear(int x1, int y1, int x2, int y2) -{ - XftDrawRect(xw.draw, - &dc.col[IS_SET(MODE_REVERSE)? defaultfg : defaultbg], - x1, y1, x2-x1, y2-y1); -} - -void -xhints(void) -{ - XClassHint class = {opt_name ? opt_name : termname, - opt_class ? opt_class : termname}; - XWMHints wm = {.flags = InputHint, .input = 1}; - XSizeHints *sizeh = NULL; - - sizeh = XAllocSizeHints(); - - sizeh->flags = PSize | PResizeInc | PBaseSize; - sizeh->height = xw.h; - sizeh->width = xw.w; - sizeh->height_inc = xw.ch; - sizeh->width_inc = xw.cw; - sizeh->base_height = 2 * borderpx; - sizeh->base_width = 2 * borderpx; - if (xw.isfixed) { - sizeh->flags |= PMaxSize | PMinSize; - sizeh->min_width = sizeh->max_width = xw.w; - sizeh->min_height = sizeh->max_height = xw.h; - } - if (xw.gm & (XValue|YValue)) { - sizeh->flags |= USPosition | PWinGravity; - sizeh->x = xw.l; - sizeh->y = xw.t; - sizeh->win_gravity = xgeommasktogravity(xw.gm); - } - - XSetWMProperties(xw.dpy, xw.win, NULL, NULL, NULL, 0, sizeh, &wm, - &class); - XFree(sizeh); -} - -int -xgeommasktogravity(int mask) -{ - switch (mask & (XNegative|YNegative)) { - case 0: - return NorthWestGravity; - case XNegative: - return NorthEastGravity; - case YNegative: - return SouthWestGravity; - } - - return SouthEastGravity; -} - -int -xloadfont(Font *f, FcPattern *pattern) -{ - FcPattern *configured; - FcPattern *match; - FcResult result; - XGlyphInfo extents; - int wantattr, haveattr; - - /* - * Manually configure instead of calling XftMatchFont - * so that we can use the configured pattern for - * "missing glyph" lookups. - */ - configured = FcPatternDuplicate(pattern); - if (!configured) - return 1; - - FcConfigSubstitute(NULL, configured, FcMatchPattern); - XftDefaultSubstitute(xw.dpy, xw.scr, configured); - - match = FcFontMatch(NULL, configured, &result); - if (!match) { - FcPatternDestroy(configured); - return 1; - } - - if (!(f->match = XftFontOpenPattern(xw.dpy, match))) { - FcPatternDestroy(configured); - FcPatternDestroy(match); - return 1; - } - - if ((XftPatternGetInteger(pattern, "slant", 0, &wantattr) == - XftResultMatch)) { - /* - * Check if xft was unable to find a font with the appropriate - * slant but gave us one anyway. Try to mitigate. - */ - if ((XftPatternGetInteger(f->match->pattern, "slant", 0, - &haveattr) != XftResultMatch) || haveattr < wantattr) { - f->badslant = 1; - fputs("st: font slant does not match\n", stderr); - } - } - - if ((XftPatternGetInteger(pattern, "weight", 0, &wantattr) == - XftResultMatch)) { - if ((XftPatternGetInteger(f->match->pattern, "weight", 0, - &haveattr) != XftResultMatch) || haveattr != wantattr) { - f->badweight = 1; - fputs("st: font weight does not match\n", stderr); - } - } - - XftTextExtentsUtf8(xw.dpy, f->match, - (const FcChar8 *) ascii_printable, - strlen(ascii_printable), &extents); - - f->set = NULL; - f->pattern = configured; - - f->ascent = f->match->ascent; - f->descent = f->match->descent; - f->lbearing = 0; - f->rbearing = f->match->max_advance_width; - - f->height = f->ascent + f->descent; - f->width = DIVCEIL(extents.xOff, strlen(ascii_printable)); - - return 0; -} - -void -xloadfonts(char *fontstr, double fontsize) -{ - FcPattern *pattern; - double fontval; - float ceilf(float); - - if (fontstr[0] == '-') { - pattern = XftXlfdParse(fontstr, False, False); - } else { - pattern = FcNameParse((FcChar8 *)fontstr); - } - - if (!pattern) - die("st: can't open font %s\n", fontstr); - - if (fontsize > 1) { - FcPatternDel(pattern, FC_PIXEL_SIZE); - FcPatternDel(pattern, FC_SIZE); - FcPatternAddDouble(pattern, FC_PIXEL_SIZE, (double)fontsize); - usedfontsize = fontsize; - } else { - if (FcPatternGetDouble(pattern, FC_PIXEL_SIZE, 0, &fontval) == - FcResultMatch) { - usedfontsize = fontval; - } else if (FcPatternGetDouble(pattern, FC_SIZE, 0, &fontval) == - FcResultMatch) { - usedfontsize = -1; - } else { - /* - * Default font size is 12, if none given. This is to - * have a known usedfontsize value. - */ - FcPatternAddDouble(pattern, FC_PIXEL_SIZE, 12); - usedfontsize = 12; - } - defaultfontsize = usedfontsize; - } - - if (xloadfont(&dc.font, pattern)) - die("st: can't open font %s\n", fontstr); - - if (usedfontsize < 0) { - FcPatternGetDouble(dc.font.match->pattern, - FC_PIXEL_SIZE, 0, &fontval); - usedfontsize = fontval; - if (fontsize == 0) - defaultfontsize = fontval; - } - - /* Setting character width and height. */ - xw.cw = ceilf(dc.font.width * cwscale); - xw.ch = ceilf(dc.font.height * chscale); - - FcPatternDel(pattern, FC_SLANT); - FcPatternAddInteger(pattern, FC_SLANT, FC_SLANT_ITALIC); - if (xloadfont(&dc.ifont, pattern)) - die("st: can't open font %s\n", fontstr); - - FcPatternDel(pattern, FC_WEIGHT); - FcPatternAddInteger(pattern, FC_WEIGHT, FC_WEIGHT_BOLD); - if (xloadfont(&dc.ibfont, pattern)) - die("st: can't open font %s\n", fontstr); - - FcPatternDel(pattern, FC_SLANT); - FcPatternAddInteger(pattern, FC_SLANT, FC_SLANT_ROMAN); - if (xloadfont(&dc.bfont, pattern)) - die("st: can't open font %s\n", fontstr); - - FcPatternDestroy(pattern); -} - -void -xunloadfont(Font *f) -{ - XftFontClose(xw.dpy, f->match); - FcPatternDestroy(f->pattern); - if (f->set) - FcFontSetDestroy(f->set); -} - -void -xunloadfonts(void) -{ - /* Free the loaded fonts in the font cache. */ - while (frclen > 0) - XftFontClose(xw.dpy, frc[--frclen].font); - - xunloadfont(&dc.font); - xunloadfont(&dc.bfont); - xunloadfont(&dc.ifont); - xunloadfont(&dc.ibfont); -} - -void -xzoom(const Arg *arg) +zoom(const Arg *arg) { Arg larg; larg.f = usedfontsize + arg->f; - xzoomabs(&larg); + zoomabs(&larg); } void -xzoomabs(const Arg *arg) +zoomabs(const Arg *arg) { xunloadfonts(); xloadfonts(usedfont, arg->f); @@ -3558,520 +2536,18 @@ xzoomabs(const Arg *arg) } void -xzoomreset(const Arg *arg) +zoomreset(const Arg *arg) { Arg larg; if (defaultfontsize > 0) { larg.f = defaultfontsize; - xzoomabs(&larg); + zoomabs(&larg); } } void -xinit(void) -{ - XGCValues gcvalues; - Cursor cursor; - Window parent; - pid_t thispid = getpid(); - XColor xmousefg, xmousebg; - - if (!(xw.dpy = XOpenDisplay(NULL))) - die("Can't open display\n"); - xw.scr = XDefaultScreen(xw.dpy); - xw.vis = XDefaultVisual(xw.dpy, xw.scr); - - /* font */ - if (!FcInit()) - die("Could not init fontconfig.\n"); - - usedfont = (opt_font == NULL)? font : opt_font; - xloadfonts(usedfont, 0); - - /* colors */ - xw.cmap = XDefaultColormap(xw.dpy, xw.scr); - xloadcols(); - - /* adjust fixed window geometry */ - xw.w = 2 * borderpx + term.col * xw.cw; - xw.h = 2 * borderpx + term.row * xw.ch; - if (xw.gm & XNegative) - xw.l += DisplayWidth(xw.dpy, xw.scr) - xw.w - 2; - if (xw.gm & YNegative) - xw.t += DisplayHeight(xw.dpy, xw.scr) - xw.h - 2; - - /* Events */ - xw.attrs.background_pixel = dc.col[defaultbg].pixel; - xw.attrs.border_pixel = dc.col[defaultbg].pixel; - xw.attrs.bit_gravity = NorthWestGravity; - xw.attrs.event_mask = FocusChangeMask | KeyPressMask - | ExposureMask | VisibilityChangeMask | StructureNotifyMask - | ButtonMotionMask | ButtonPressMask | ButtonReleaseMask; - xw.attrs.colormap = xw.cmap; - - if (!(opt_embed && (parent = strtol(opt_embed, NULL, 0)))) - parent = XRootWindow(xw.dpy, xw.scr); - xw.win = XCreateWindow(xw.dpy, parent, xw.l, xw.t, - xw.w, xw.h, 0, XDefaultDepth(xw.dpy, xw.scr), InputOutput, - xw.vis, CWBackPixel | CWBorderPixel | CWBitGravity - | CWEventMask | CWColormap, &xw.attrs); - - memset(&gcvalues, 0, sizeof(gcvalues)); - gcvalues.graphics_exposures = False; - dc.gc = XCreateGC(xw.dpy, parent, GCGraphicsExposures, - &gcvalues); - xw.buf = XCreatePixmap(xw.dpy, xw.win, xw.w, xw.h, - DefaultDepth(xw.dpy, xw.scr)); - XSetForeground(xw.dpy, dc.gc, dc.col[defaultbg].pixel); - XFillRectangle(xw.dpy, xw.buf, dc.gc, 0, 0, xw.w, xw.h); - - /* Xft rendering context */ - xw.draw = XftDrawCreate(xw.dpy, xw.buf, xw.vis, xw.cmap); - - /* input methods */ - if ((xw.xim = XOpenIM(xw.dpy, NULL, NULL, NULL)) == NULL) { - XSetLocaleModifiers("@im=local"); - if ((xw.xim = XOpenIM(xw.dpy, NULL, NULL, NULL)) == NULL) { - XSetLocaleModifiers("@im="); - if ((xw.xim = XOpenIM(xw.dpy, - NULL, NULL, NULL)) == NULL) { - die("XOpenIM failed. Could not open input" - " device.\n"); - } - } - } - xw.xic = XCreateIC(xw.xim, XNInputStyle, XIMPreeditNothing - | XIMStatusNothing, XNClientWindow, xw.win, - XNFocusWindow, xw.win, NULL); - if (xw.xic == NULL) - die("XCreateIC failed. Could not obtain input method.\n"); - - /* white cursor, black outline */ - cursor = XCreateFontCursor(xw.dpy, mouseshape); - XDefineCursor(xw.dpy, xw.win, cursor); - - if (XParseColor(xw.dpy, xw.cmap, colorname[mousefg], &xmousefg) == 0) { - xmousefg.red = 0xffff; - xmousefg.green = 0xffff; - xmousefg.blue = 0xffff; - } - - if (XParseColor(xw.dpy, xw.cmap, colorname[mousebg], &xmousebg) == 0) { - xmousebg.red = 0x0000; - xmousebg.green = 0x0000; - xmousebg.blue = 0x0000; - } - - XRecolorCursor(xw.dpy, cursor, &xmousefg, &xmousebg); - - 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); - XSetWMProtocols(xw.dpy, xw.win, &xw.wmdeletewin, 1); - - xw.netwmpid = XInternAtom(xw.dpy, "_NET_WM_PID", False); - XChangeProperty(xw.dpy, xw.win, xw.netwmpid, XA_CARDINAL, 32, - PropModeReplace, (uchar *)&thispid, 1); - - xresettitle(); - XMapWindow(xw.dpy, xw.win); - xhints(); - XSync(xw.dpy, False); -} - -int -xmakeglyphfontspecs(XftGlyphFontSpec *specs, const Glyph *glyphs, int len, int x, int y) -{ - float winx = borderpx + x * xw.cw, winy = borderpx + y * xw.ch, xp, yp; - ushort mode, prevmode = USHRT_MAX; - Font *font = &dc.font; - int frcflags = FRC_NORMAL; - float runewidth = xw.cw; - Rune rune; - FT_UInt glyphidx; - FcResult fcres; - FcPattern *fcpattern, *fontpattern; - FcFontSet *fcsets[] = { NULL }; - FcCharSet *fccharset; - int i, f, numspecs = 0; - - for (i = 0, xp = winx, yp = winy + font->ascent; i < len; ++i) { - /* Fetch rune and mode for current glyph. */ - rune = glyphs[i].u; - mode = glyphs[i].mode; - - /* Skip dummy wide-character spacing. */ - if (mode == ATTR_WDUMMY) - continue; - - /* Determine font for glyph if different from previous glyph. */ - if (prevmode != mode) { - prevmode = mode; - font = &dc.font; - frcflags = FRC_NORMAL; - runewidth = xw.cw * ((mode & ATTR_WIDE) ? 2.0f : 1.0f); - if ((mode & ATTR_ITALIC) && (mode & ATTR_BOLD)) { - font = &dc.ibfont; - frcflags = FRC_ITALICBOLD; - } else if (mode & ATTR_ITALIC) { - font = &dc.ifont; - frcflags = FRC_ITALIC; - } else if (mode & ATTR_BOLD) { - font = &dc.bfont; - frcflags = FRC_BOLD; - } - yp = winy + font->ascent; - } - - /* Lookup character index with default font. */ - glyphidx = XftCharIndex(xw.dpy, font->match, rune); - if (glyphidx) { - specs[numspecs].font = font->match; - specs[numspecs].glyph = glyphidx; - specs[numspecs].x = (short)xp; - specs[numspecs].y = (short)yp; - xp += runewidth; - numspecs++; - continue; - } - - /* Fallback on font cache, search the font cache for match. */ - for (f = 0; f < frclen; f++) { - glyphidx = XftCharIndex(xw.dpy, frc[f].font, rune); - /* Everything correct. */ - if (glyphidx && frc[f].flags == frcflags) - break; - /* We got a default font for a not found glyph. */ - if (!glyphidx && frc[f].flags == frcflags - && frc[f].unicodep == rune) { - break; - } - } - - /* Nothing was found. Use fontconfig to find matching font. */ - if (f >= frclen) { - if (!font->set) - font->set = FcFontSort(0, font->pattern, - 1, 0, &fcres); - fcsets[0] = font->set; - - /* - * Nothing was found in the cache. Now use - * some dozen of Fontconfig calls to get the - * font for one single character. - * - * Xft and fontconfig are design failures. - */ - fcpattern = FcPatternDuplicate(font->pattern); - fccharset = FcCharSetCreate(); - - FcCharSetAddChar(fccharset, rune); - FcPatternAddCharSet(fcpattern, FC_CHARSET, - fccharset); - FcPatternAddBool(fcpattern, FC_SCALABLE, 1); - - FcConfigSubstitute(0, fcpattern, - FcMatchPattern); - FcDefaultSubstitute(fcpattern); - - fontpattern = FcFontSetMatch(0, fcsets, 1, - fcpattern, &fcres); - - /* - * Overwrite or create the new cache entry. - */ - if (frclen >= LEN(frc)) { - frclen = LEN(frc) - 1; - XftFontClose(xw.dpy, frc[frclen].font); - frc[frclen].unicodep = 0; - } - - frc[frclen].font = XftFontOpenPattern(xw.dpy, - fontpattern); - frc[frclen].flags = frcflags; - frc[frclen].unicodep = rune; - - glyphidx = XftCharIndex(xw.dpy, frc[frclen].font, rune); - - f = frclen; - frclen++; - - FcPatternDestroy(fcpattern); - FcCharSetDestroy(fccharset); - } - - specs[numspecs].font = frc[f].font; - specs[numspecs].glyph = glyphidx; - specs[numspecs].x = (short)xp; - specs[numspecs].y = (short)yp; - xp += runewidth; - numspecs++; - } - - return numspecs; -} - -void -xdrawglyphfontspecs(const XftGlyphFontSpec *specs, Glyph base, int len, int x, int y) -{ - int charlen = len * ((base.mode & ATTR_WIDE) ? 2 : 1); - int winx = borderpx + x * xw.cw, winy = borderpx + y * xw.ch, - width = charlen * xw.cw; - Color *fg, *bg, *temp, revfg, revbg, truefg, truebg; - XRenderColor colfg, colbg; - XRectangle r; - - /* Fallback on color display for attributes not supported by the font */ - if (base.mode & ATTR_ITALIC && base.mode & ATTR_BOLD) { - if (dc.ibfont.badslant || dc.ibfont.badweight) - base.fg = defaultattr; - } else if ((base.mode & ATTR_ITALIC && dc.ifont.badslant) || - (base.mode & ATTR_BOLD && dc.bfont.badweight)) { - base.fg = defaultattr; - } - - if (IS_TRUECOL(base.fg)) { - colfg.alpha = 0xffff; - colfg.red = TRUERED(base.fg); - colfg.green = TRUEGREEN(base.fg); - colfg.blue = TRUEBLUE(base.fg); - XftColorAllocValue(xw.dpy, xw.vis, xw.cmap, &colfg, &truefg); - fg = &truefg; - } else { - fg = &dc.col[base.fg]; - } - - if (IS_TRUECOL(base.bg)) { - colbg.alpha = 0xffff; - colbg.green = TRUEGREEN(base.bg); - colbg.red = TRUERED(base.bg); - colbg.blue = TRUEBLUE(base.bg); - XftColorAllocValue(xw.dpy, xw.vis, xw.cmap, &colbg, &truebg); - bg = &truebg; - } else { - bg = &dc.col[base.bg]; - } - - /* Change basic system colors [0-7] to bright system colors [8-15] */ - if ((base.mode & ATTR_BOLD_FAINT) == ATTR_BOLD && BETWEEN(base.fg, 0, 7)) - fg = &dc.col[base.fg + 8]; - - if (IS_SET(MODE_REVERSE)) { - if (fg == &dc.col[defaultfg]) { - fg = &dc.col[defaultbg]; - } else { - colfg.red = ~fg->color.red; - colfg.green = ~fg->color.green; - colfg.blue = ~fg->color.blue; - colfg.alpha = fg->color.alpha; - XftColorAllocValue(xw.dpy, xw.vis, xw.cmap, &colfg, - &revfg); - fg = &revfg; - } - - if (bg == &dc.col[defaultbg]) { - bg = &dc.col[defaultfg]; - } else { - colbg.red = ~bg->color.red; - colbg.green = ~bg->color.green; - colbg.blue = ~bg->color.blue; - colbg.alpha = bg->color.alpha; - XftColorAllocValue(xw.dpy, xw.vis, xw.cmap, &colbg, - &revbg); - bg = &revbg; - } - } - - if (base.mode & ATTR_REVERSE) { - temp = fg; - fg = bg; - bg = temp; - } - - if ((base.mode & ATTR_BOLD_FAINT) == ATTR_FAINT) { - colfg.red = fg->color.red / 2; - colfg.green = fg->color.green / 2; - colfg.blue = fg->color.blue / 2; - XftColorAllocValue(xw.dpy, xw.vis, xw.cmap, &colfg, &revfg); - fg = &revfg; - } - - if (base.mode & ATTR_BLINK && term.mode & MODE_BLINK) - fg = bg; - - if (base.mode & ATTR_INVISIBLE) - fg = bg; - - /* Intelligent cleaning up of the borders. */ - if (x == 0) { - xclear(0, (y == 0)? 0 : winy, borderpx, - winy + xw.ch + ((y >= term.row-1)? xw.h : 0)); - } - if (x + charlen >= term.col) { - xclear(winx + width, (y == 0)? 0 : winy, xw.w, - ((y >= term.row-1)? xw.h : (winy + xw.ch))); - } - if (y == 0) - xclear(winx, 0, winx + width, borderpx); - if (y == term.row-1) - xclear(winx, winy + xw.ch, winx + width, xw.h); - - /* Clean up the region we want to draw to. */ - XftDrawRect(xw.draw, bg, winx, winy, width, xw.ch); - - /* Set the clip region because Xft is sometimes dirty. */ - r.x = 0; - r.y = 0; - r.height = xw.ch; - r.width = width; - XftDrawSetClipRectangles(xw.draw, winx, winy, &r, 1); - - /* Render the glyphs. */ - XftDrawGlyphFontSpec(xw.draw, fg, specs, len); - - /* Render underline and strikethrough. */ - if (base.mode & ATTR_UNDERLINE) { - XftDrawRect(xw.draw, fg, winx, winy + dc.font.ascent + 1, - width, 1); - } - - if (base.mode & ATTR_STRUCK) { - XftDrawRect(xw.draw, fg, winx, winy + 2 * dc.font.ascent / 3, - width, 1); - } - - /* Reset clip to none. */ - XftDrawSetClip(xw.draw, 0); -} - -void -xdrawglyph(Glyph g, int x, int y) -{ - int numspecs; - XftGlyphFontSpec spec; - - numspecs = xmakeglyphfontspecs(&spec, &g, 1, x, y); - xdrawglyphfontspecs(&spec, g, numspecs, x, y); -} - -void -xdrawcursor(void) -{ - static int oldx = 0, oldy = 0; - int curx; - Glyph g = {' ', ATTR_NULL, defaultbg, defaultcs}, og; - int ena_sel = sel.ob.x != -1 && sel.alt == IS_SET(MODE_ALTSCREEN); - Color drawcol; - - LIMIT(oldx, 0, term.col-1); - LIMIT(oldy, 0, term.row-1); - - curx = term.c.x; - - /* adjust position if in dummy */ - if (term.line[oldy][oldx].mode & ATTR_WDUMMY) - oldx--; - if (term.line[term.c.y][curx].mode & ATTR_WDUMMY) - curx--; - - /* remove the old cursor */ - og = term.line[oldy][oldx]; - if (ena_sel && selected(oldx, oldy)) - og.mode ^= ATTR_REVERSE; - xdrawglyph(og, oldx, oldy); - - g.u = term.line[term.c.y][term.c.x].u; - - /* - * Select the right color for the right mode. - */ - if (IS_SET(MODE_REVERSE)) { - g.mode |= ATTR_REVERSE; - g.bg = defaultfg; - if (ena_sel && selected(term.c.x, term.c.y)) { - drawcol = dc.col[defaultcs]; - g.fg = defaultrcs; - } else { - drawcol = dc.col[defaultrcs]; - g.fg = defaultcs; - } - } else { - if (ena_sel && selected(term.c.x, term.c.y)) { - drawcol = dc.col[defaultrcs]; - g.fg = defaultfg; - g.bg = defaultrcs; - } else { - drawcol = dc.col[defaultcs]; - } - } - - if (IS_SET(MODE_HIDE)) - return; - - /* draw the new one */ - if (xw.state & WIN_FOCUSED) { - switch (xw.cursor) { - case 7: /* st extension: snowman */ - utf8decode("☃", &g.u, UTF_SIZ); - case 0: /* Blinking Block */ - case 1: /* Blinking Block (Default) */ - case 2: /* Steady Block */ - g.mode |= term.line[term.c.y][curx].mode & ATTR_WIDE; - xdrawglyph(g, term.c.x, term.c.y); - break; - case 3: /* Blinking Underline */ - case 4: /* Steady Underline */ - XftDrawRect(xw.draw, &drawcol, - borderpx + curx * xw.cw, - borderpx + (term.c.y + 1) * xw.ch - \ - cursorthickness, - xw.cw, cursorthickness); - break; - case 5: /* Blinking bar */ - case 6: /* Steady bar */ - XftDrawRect(xw.draw, &drawcol, - borderpx + curx * xw.cw, - borderpx + term.c.y * xw.ch, - cursorthickness, xw.ch); - break; - } - } else { - XftDrawRect(xw.draw, &drawcol, - borderpx + curx * xw.cw, - borderpx + term.c.y * xw.ch, - xw.cw - 1, 1); - XftDrawRect(xw.draw, &drawcol, - borderpx + curx * xw.cw, - borderpx + term.c.y * xw.ch, - 1, xw.ch - 1); - XftDrawRect(xw.draw, &drawcol, - borderpx + (curx + 1) * xw.cw - 1, - borderpx + term.c.y * xw.ch, - 1, xw.ch - 1); - XftDrawRect(xw.draw, &drawcol, - borderpx + curx * xw.cw, - borderpx + (term.c.y + 1) * xw.ch - 1, - xw.cw, 1); - } - oldx = curx, oldy = term.c.y; -} - - -void -xsettitle(char *p) -{ - XTextProperty prop; - - Xutf8TextListToTextProperty(xw.dpy, &p, 1, XUTF8StringStyle, - &prop); - XSetWMName(xw.dpy, xw.win, &prop); - XSetTextProperty(xw.dpy, xw.win, &prop, xw.netwmname); - XFree(prop.value); -} - -void -xresettitle(void) +resettitle(void) { xsettitle(opt_title ? opt_title : "st"); } @@ -4083,121 +2559,6 @@ redraw(void) draw(); } -void -draw(void) -{ - drawregion(0, 0, term.col, term.row); - XCopyArea(xw.dpy, xw.buf, xw.win, dc.gc, 0, 0, xw.w, - xw.h, 0, 0); - XSetForeground(xw.dpy, dc.gc, - dc.col[IS_SET(MODE_REVERSE)? - defaultfg : defaultbg].pixel); -} - -void -drawregion(int x1, int y1, int x2, int y2) -{ - int i, x, y, ox, numspecs; - Glyph base, new; - XftGlyphFontSpec *specs; - int ena_sel = sel.ob.x != -1 && sel.alt == IS_SET(MODE_ALTSCREEN); - - if (!(xw.state & WIN_VISIBLE)) - return; - - for (y = y1; y < y2; y++) { - if (!term.dirty[y]) - continue; - - term.dirty[y] = 0; - - specs = term.specbuf; - numspecs = xmakeglyphfontspecs(specs, &term.line[y][x1], x2 - x1, x1, y); - - i = ox = 0; - for (x = x1; x < x2 && i < numspecs; x++) { - new = term.line[y][x]; - if (new.mode == ATTR_WDUMMY) - continue; - if (ena_sel && selected(x, y)) - new.mode ^= ATTR_REVERSE; - if (i > 0 && ATTRCMP(base, new)) { - xdrawglyphfontspecs(specs, base, i, ox, y); - specs += i; - numspecs -= i; - i = 0; - } - if (i == 0) { - ox = x; - base = new; - } - i++; - } - if (i > 0) - xdrawglyphfontspecs(specs, base, i, ox, y); - } - xdrawcursor(); -} - -void -expose(XEvent *ev) -{ - redraw(); -} - -void -visibility(XEvent *ev) -{ - XVisibilityEvent *e = &ev->xvisibility; - - MODBIT(xw.state, e->state != VisibilityFullyObscured, WIN_VISIBLE); -} - -void -unmap(XEvent *ev) -{ - xw.state &= ~WIN_VISIBLE; -} - -void -xsetpointermotion(int set) -{ - MODBIT(xw.attrs.event_mask, set, PointerMotionMask); - XChangeWindowAttributes(xw.dpy, xw.win, CWEventMask, &xw.attrs); -} - -void -xseturgency(int add) -{ - XWMHints *h = XGetWMHints(xw.dpy, xw.win); - - MODBIT(h->flags, add, XUrgencyHint); - XSetWMHints(xw.dpy, xw.win, h); - XFree(h); -} - -void -focus(XEvent *ev) -{ - XFocusChangeEvent *e = &ev->xfocus; - - if (e->mode == NotifyGrab) - return; - - if (ev->type == FocusIn) { - XSetICFocus(xw.xic); - xw.state |= WIN_FOCUSED; - xseturgency(0); - if (IS_SET(MODE_FOCUS)) - ttywrite("\033[I", 3); - } else { - XUnsetICFocus(xw.xic); - xw.state &= ~WIN_FOCUSED; - if (IS_SET(MODE_FOCUS)) - ttywrite("\033[O", 3); - } -} - int match(uint mask, uint state) { @@ -4250,211 +2611,23 @@ kmap(KeySym k, uint state) return NULL; } -void -kpress(XEvent *ev) -{ - XKeyEvent *e = &ev->xkey; - KeySym ksym; - char buf[32], *customkey; - int len; - Rune c; - Status status; - Shortcut *bp; - - if (IS_SET(MODE_KBDLOCK)) - return; - - len = XmbLookupString(xw.xic, e, buf, sizeof buf, &ksym, &status); - /* 1. shortcuts */ - for (bp = shortcuts; bp < shortcuts + LEN(shortcuts); bp++) { - if (ksym == bp->keysym && match(bp->mod, e->state)) { - bp->func(&(bp->arg)); - return; - } - } - - /* 2. custom keys from config.h */ - if ((customkey = kmap(ksym, e->state))) { - ttysend(customkey, strlen(customkey)); - return; - } - - /* 3. composed string from input method */ - if (len == 0) - return; - if (len == 1 && e->state & Mod1Mask) { - if (IS_SET(MODE_8BIT)) { - if (*buf < 0177) { - c = *buf | 0x80; - len = utf8encode(c, buf); - } - } else { - buf[1] = buf[0]; - buf[0] = '\033'; - len = 2; - } - } - ttysend(buf, len); -} - - -void -cmessage(XEvent *e) -{ - /* - * See xembed specs - * http://standards.freedesktop.org/xembed-spec/xembed-spec-latest.html - */ - if (e->xclient.message_type == xw.xembed && e->xclient.format == 32) { - if (e->xclient.data.l[1] == XEMBED_FOCUS_IN) { - xw.state |= WIN_FOCUSED; - xseturgency(0); - } else if (e->xclient.data.l[1] == XEMBED_FOCUS_OUT) { - xw.state &= ~WIN_FOCUSED; - } - } else if (e->xclient.data.l[0] == xw.wmdeletewin) { - /* Send SIGHUP to shell */ - kill(pid, SIGHUP); - exit(0); - } -} - void cresize(int width, int height) { int col, row; if (width != 0) - xw.w = width; + win.w = width; if (height != 0) - xw.h = height; + win.h = height; - col = (xw.w - 2 * borderpx) / xw.cw; - row = (xw.h - 2 * borderpx) / xw.ch; + col = (win.w - 2 * borderpx) / win.cw; + row = (win.h - 2 * borderpx) / win.ch; tresize(col, row); xresize(col, row); } -void -resize(XEvent *e) -{ - if (e->xconfigure.width == xw.w && e->xconfigure.height == xw.h) - return; - - cresize(e->xconfigure.width, e->xconfigure.height); - ttyresize(); -} - -void -run(void) -{ - XEvent ev; - int w = xw.w, h = xw.h; - fd_set rfd; - int xfd = XConnectionNumber(xw.dpy), xev, blinkset = 0, dodraw = 0; - struct timespec drawtimeout, *tv = NULL, now, last, lastblink; - long deltatime; - - /* Waiting for window mapping */ - do { - XNextEvent(xw.dpy, &ev); - /* - * This XFilterEvent call is required because of XOpenIM. It - * does filter out the key event and some client message for - * the input method too. - */ - if (XFilterEvent(&ev, None)) - continue; - if (ev.type == ConfigureNotify) { - w = ev.xconfigure.width; - h = ev.xconfigure.height; - } - } while (ev.type != MapNotify); - - cresize(w, h); - ttynew(); - ttyresize(); - - clock_gettime(CLOCK_MONOTONIC, &last); - lastblink = last; - - for (xev = actionfps;;) { - FD_ZERO(&rfd); - FD_SET(cmdfd, &rfd); - FD_SET(xfd, &rfd); - - if (pselect(MAX(xfd, cmdfd)+1, &rfd, NULL, NULL, tv, NULL) < 0) { - if (errno == EINTR) - continue; - die("select failed: %s\n", strerror(errno)); - } - if (FD_ISSET(cmdfd, &rfd)) { - ttyread(); - if (blinktimeout) { - blinkset = tattrset(ATTR_BLINK); - if (!blinkset) - MODBIT(term.mode, 0, MODE_BLINK); - } - } - - if (FD_ISSET(xfd, &rfd)) - xev = actionfps; - - clock_gettime(CLOCK_MONOTONIC, &now); - drawtimeout.tv_sec = 0; - drawtimeout.tv_nsec = (1000 * 1E6)/ xfps; - tv = &drawtimeout; - - dodraw = 0; - if (blinktimeout && TIMEDIFF(now, lastblink) > blinktimeout) { - tsetdirtattr(ATTR_BLINK); - term.mode ^= MODE_BLINK; - lastblink = now; - dodraw = 1; - } - deltatime = TIMEDIFF(now, last); - if (deltatime > 1000 / (xev ? xfps : actionfps)) { - dodraw = 1; - last = now; - } - - if (dodraw) { - while (XPending(xw.dpy)) { - XNextEvent(xw.dpy, &ev); - if (XFilterEvent(&ev, None)) - continue; - if (handler[ev.type]) - (handler[ev.type])(&ev); - } - - draw(); - XFlush(xw.dpy); - - if (xev && !FD_ISSET(xfd, &rfd)) - xev--; - if (!FD_ISSET(cmdfd, &rfd) && !FD_ISSET(xfd, &rfd)) { - if (blinkset) { - if (TIMEDIFF(now, lastblink) \ - > blinktimeout) { - drawtimeout.tv_nsec = 1000; - } else { - drawtimeout.tv_nsec = (1E6 * \ - (blinktimeout - \ - TIMEDIFF(now, - lastblink))); - } - drawtimeout.tv_sec = \ - drawtimeout.tv_nsec / 1E9; - drawtimeout.tv_nsec %= (long)1E9; - } else { - tv = NULL; - } - } - } - } -} - void usage(void) { @@ -4467,72 +2640,3 @@ usage(void) " [-T title] [-t title] [-w windowid] -l line" " [stty_args ...]\n", argv0, argv0); } - -int -main(int argc, char *argv[]) -{ - xw.l = xw.t = 0; - xw.isfixed = False; - xw.cursor = cursorshape; - - ARGBEGIN { - case 'a': - allowaltscreen = 0; - break; - case 'c': - opt_class = EARGF(usage()); - break; - case 'e': - if (argc > 0) - --argc, ++argv; - goto run; - case 'f': - opt_font = EARGF(usage()); - break; - case 'g': - xw.gm = XParseGeometry(EARGF(usage()), - &xw.l, &xw.t, &cols, &rows); - break; - case 'i': - xw.isfixed = 1; - break; - case 'o': - opt_io = EARGF(usage()); - break; - case 'l': - opt_line = EARGF(usage()); - break; - case 'n': - opt_name = EARGF(usage()); - break; - case 't': - case 'T': - opt_title = EARGF(usage()); - break; - case 'w': - opt_embed = EARGF(usage()); - break; - case 'v': - die("%s " VERSION " (c) 2010-2016 st engineers\n", argv0); - break; - default: - usage(); - } ARGEND; - -run: - if (argc > 0) { - /* eat all remaining arguments */ - opt_cmd = argv; - if (!opt_title && !opt_line) - opt_title = basename(xstrdup(argv[0])); - } - setlocale(LC_CTYPE, ""); - XSetLocaleModifiers(""); - tnew(MAX(cols, 1), MAX(rows, 1)); - xinit(); - selinit(); - run(); - - return 0; -} - diff --git a/st.h b/st.h new file mode 100644 index 0000000..44d4938 --- /dev/null +++ b/st.h @@ -0,0 +1,272 @@ +/* See LICENSE for license details. */ + +/* Arbitrary sizes */ +#define UTF_SIZ 4 + +/* macros */ +#define MIN(a, b) ((a) < (b) ? (a) : (b)) +#define MAX(a, b) ((a) < (b) ? (b) : (a)) +#define LEN(a) (sizeof(a) / sizeof(a)[0]) +#define BETWEEN(x, a, b) ((a) <= (x) && (x) <= (b)) +#define DIVCEIL(n, d) (((n) + ((d) - 1)) / (d)) +#define LIMIT(x, a, b) (x) = (x) < (a) ? (a) : (x) > (b) ? (b) : (x) +#define ATTRCMP(a, b) ((a).mode != (b).mode || (a).fg != (b).fg || \ + (a).bg != (b).bg) +#define IS_SET(flag) ((term.mode & (flag)) != 0) +#define TIMEDIFF(t1, t2) ((t1.tv_sec-t2.tv_sec)*1000 + \ + (t1.tv_nsec-t2.tv_nsec)/1E6) +#define MODBIT(x, set, bit) ((set) ? ((x) |= (bit)) : ((x) &= ~(bit))) + +#define TRUECOLOR(r,g,b) (1 << 24 | (r) << 16 | (g) << 8 | (b)) +#define IS_TRUECOL(x) (1 << 24 & (x)) + +enum glyph_attribute { + ATTR_NULL = 0, + ATTR_BOLD = 1 << 0, + ATTR_FAINT = 1 << 1, + ATTR_ITALIC = 1 << 2, + ATTR_UNDERLINE = 1 << 3, + ATTR_BLINK = 1 << 4, + ATTR_REVERSE = 1 << 5, + ATTR_INVISIBLE = 1 << 6, + ATTR_STRUCK = 1 << 7, + ATTR_WRAP = 1 << 8, + ATTR_WIDE = 1 << 9, + ATTR_WDUMMY = 1 << 10, + ATTR_BOLD_FAINT = ATTR_BOLD | ATTR_FAINT, +}; + +enum term_mode { + MODE_WRAP = 1 << 0, + MODE_INSERT = 1 << 1, + MODE_APPKEYPAD = 1 << 2, + MODE_ALTSCREEN = 1 << 3, + MODE_CRLF = 1 << 4, + MODE_MOUSEBTN = 1 << 5, + MODE_MOUSEMOTION = 1 << 6, + MODE_REVERSE = 1 << 7, + MODE_KBDLOCK = 1 << 8, + MODE_HIDE = 1 << 9, + MODE_ECHO = 1 << 10, + MODE_APPCURSOR = 1 << 11, + MODE_MOUSESGR = 1 << 12, + MODE_8BIT = 1 << 13, + MODE_BLINK = 1 << 14, + MODE_FBLINK = 1 << 15, + MODE_FOCUS = 1 << 16, + MODE_MOUSEX10 = 1 << 17, + MODE_MOUSEMANY = 1 << 18, + MODE_BRCKTPASTE = 1 << 19, + MODE_PRINT = 1 << 20, + MODE_UTF8 = 1 << 21, + MODE_SIXEL = 1 << 22, + MODE_MOUSE = MODE_MOUSEBTN|MODE_MOUSEMOTION|MODE_MOUSEX10\ + |MODE_MOUSEMANY, +}; + +enum selection_mode { + SEL_IDLE = 0, + SEL_EMPTY = 1, + SEL_READY = 2 +}; + +enum selection_type { + SEL_REGULAR = 1, + SEL_RECTANGULAR = 2 +}; + +enum selection_snap { + SNAP_WORD = 1, + SNAP_LINE = 2 +}; + +enum window_state { + WIN_VISIBLE = 1, + WIN_FOCUSED = 2 +}; + +typedef unsigned char uchar; +typedef unsigned int uint; +typedef unsigned long ulong; +typedef unsigned short ushort; + +typedef uint_least32_t Rune; + +typedef struct { + Rune u; /* character code */ + ushort mode; /* attribute flags */ + uint32_t fg; /* foreground */ + uint32_t bg; /* background */ +} Glyph; + +typedef Glyph *Line; + +typedef struct { + Glyph attr; /* current char attributes */ + int x; + int y; + char state; +} TCursor; + +/* Internal representation of the screen */ +typedef struct { + int row; /* nb row */ + int col; /* nb col */ + Line *line; /* screen */ + Line *alt; /* alternate screen */ + int *dirty; /* dirtyness of lines */ + GlyphFontSpec *specbuf; /* font spec buffer used for rendering */ + TCursor c; /* cursor */ + int top; /* top scroll limit */ + int bot; /* bottom scroll limit */ + int mode; /* terminal mode flags */ + int esc; /* escape state flags */ + char trantbl[4]; /* charset table translation */ + int charset; /* current charset */ + int icharset; /* selected charset for sequence */ + int numlock; /* lock numbers in keyboard */ + int *tabs; +} Term; + +/* Purely graphic info */ +typedef struct { + int tw, th; /* tty width and height */ + int w, h; /* window width and height */ + int ch; /* char height */ + int cw; /* char width */ + char state; /* focus, redraw, visible */ + int cursor; /* cursor style */ +} TermWindow; + +typedef struct { + uint b; + uint mask; + char *s; +} MouseShortcut; + +typedef struct { + int mode; + int type; + int snap; + /* + * Selection variables: + * nb – normalized coordinates of the beginning of the selection + * ne – normalized coordinates of the end of the selection + * ob – original coordinates of the beginning of the selection + * oe – original coordinates of the end of the selection + */ + struct { + int x, y; + } nb, ne, ob, oe; + + char *primary, *clipboard; + int alt; + struct timespec tclick1; + struct timespec tclick2; + + //Atom xtarget; +} Selection; + +typedef union { + int i; + uint ui; + float f; + const void *v; +} Arg; + +typedef struct { + uint mod; + KeySym keysym; + void (*func)(const Arg *); + const Arg arg; +} Shortcut; + +void die(const char *, ...); +void redraw(void); + +int tattrset(int); +void tnew(int, int); +void tsetdirt(int, int); +void tsetdirtattr(int); +int match(uint, uint); +void ttynew(void); +size_t ttyread(void); +void ttyresize(void); +void ttysend(char *, size_t); +void ttywrite(const char *, size_t); + +void resettitle(void); + +char *kmap(KeySym, uint); +void cresize(int, int); +void selclear(void); + +void selinit(void); +void selnormalize(void); +int selected(int, int); +char *getsel(void); +int x2col(int); +int y2row(int); + +size_t utf8decode(char *, Rune *, size_t); +size_t utf8encode(Rune, char *); + +void *xmalloc(size_t); +char *xstrdup(char *); + +void usage(void); + +/* Globals */ +extern TermWindow win; +extern Term term; +extern Selection sel; +extern int cmdfd; +extern pid_t pid; +extern char **opt_cmd; +extern char *opt_class; +extern char *opt_embed; +extern char *opt_font; +extern char *opt_io; +extern char *opt_line; +extern char *opt_name; +extern char *opt_title; +extern int oldbutton; + +extern char *usedfont; +extern double usedfontsize; +extern double defaultfontsize; + +/* config.h globals */ +extern char font[]; +extern int borderpx; +extern float cwscale; +extern float chscale; +extern unsigned int doubleclicktimeout; +extern unsigned int tripleclicktimeout; +extern int allowaltscreen; +extern unsigned int xfps; +extern unsigned int actionfps; +extern unsigned int cursorthickness; +extern unsigned int blinktimeout; +extern char termname[]; +extern const char *colorname[]; +extern size_t colornamelen; +extern unsigned int defaultfg; +extern unsigned int defaultbg; +extern unsigned int defaultcs; +extern unsigned int defaultrcs; +extern unsigned int cursorshape; +extern unsigned int cols; +extern unsigned int rows; +extern unsigned int mouseshape; +extern unsigned int mousefg; +extern unsigned int mousebg; +extern unsigned int defaultattr; +extern MouseShortcut mshortcuts[]; +extern size_t mshortcutslen; +extern Shortcut shortcuts[]; +extern size_t shortcutslen; +extern uint forceselmod; +extern uint selmasks[]; +extern size_t selmaskslen; +extern char ascii_printable[]; diff --git a/win.h b/win.h new file mode 100644 index 0000000..d684797 --- /dev/null +++ b/win.h @@ -0,0 +1,29 @@ +/* See LICENSE for license details. */ + +/* X modifiers */ +#define XK_ANY_MOD UINT_MAX +#define XK_NO_MOD 0 +#define XK_SWITCH_MOD (1<<13) + +typedef XftGlyphFontSpec GlyphFontSpec; + +void draw(void); +void drawregion(int, int, int, int); +void run(void); + +void xbell(int); +void xclipcopy(void); +void xclippaste(void); +void xhints(void); +void xinit(void); +void xloadcols(void); +int xsetcolorname(int, const char *); +void xloadfonts(char *, double); +void xsetenv(void); +void xsettitle(char *); +void xsetpointermotion(int); +void xseturgency(int); +void xunloadfonts(void); +void xresize(int, int); +void xselpaste(void); +unsigned long xwinid(void); diff --git a/x.c b/x.c new file mode 100644 index 0000000..6474a01 --- /dev/null +++ b/x.c @@ -0,0 +1,1766 @@ +/* See LICENSE for license details. */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "arg.h" + +#define Glyph Glyph_ +#define Font Font_ + +#include "win.h" +#include "st.h" + +/* XEMBED messages */ +#define XEMBED_FOCUS_IN 4 +#define XEMBED_FOCUS_OUT 5 + +/* macros */ +#define TRUERED(x) (((x) & 0xff0000) >> 8) +#define TRUEGREEN(x) (((x) & 0xff00)) +#define TRUEBLUE(x) (((x) & 0xff) << 8) + +typedef XftDraw *Draw; +typedef XftColor Color; + +/* Purely graphic info */ +typedef struct { + Display *dpy; + Colormap cmap; + Window win; + Drawable buf; + Atom xembed, wmdeletewin, netwmname, netwmpid; + XIM xim; + XIC xic; + Draw draw; + Visual *vis; + XSetWindowAttributes attrs; + int scr; + int isfixed; /* is fixed geometry? */ + int l, t; /* left and top offset */ + int gm; /* geometry mask */ +} XWindow; + +typedef struct { + Atom xtarget; +} XSelection; + +/* Font structure */ +typedef struct { + int height; + int width; + int ascent; + int descent; + int badslant; + int badweight; + short lbearing; + short rbearing; + XftFont *match; + FcFontSet *set; + FcPattern *pattern; +} Font; + +/* Drawing Context */ +typedef struct { + Color *col; + size_t collen; + Font font, bfont, ifont, ibfont; + GC gc; +} DC; + +static inline ushort sixd_to_16bit(int); +static int xmakeglyphfontspecs(XftGlyphFontSpec *, const Glyph *, int, int, int); +static void xdrawglyphfontspecs(const XftGlyphFontSpec *, Glyph, int, int, int); +static void xdrawglyph(Glyph, int, int); +static void xclear(int, int, int, int); +static void xdrawcursor(void); +static int xgeommasktogravity(int); +static int xloadfont(Font *, FcPattern *); +static void xsetsel(char *, Time); +static void xunloadfont(Font *); + +static void expose(XEvent *); +static void visibility(XEvent *); +static void unmap(XEvent *); +static void kpress(XEvent *); +static void cmessage(XEvent *); +static void resize(XEvent *); +static void focus(XEvent *); +static void brelease(XEvent *); +static void bpress(XEvent *); +static void bmotion(XEvent *); +static void propnotify(XEvent *); +static void selnotify(XEvent *); +static void selclear_(XEvent *); +static void selrequest(XEvent *); + +static void selcopy(Time); +static void getbuttoninfo(XEvent *); +static void mousereport(XEvent *); + +static void (*handler[LASTEvent])(XEvent *) = { + [KeyPress] = kpress, + [ClientMessage] = cmessage, + [ConfigureNotify] = resize, + [VisibilityNotify] = visibility, + [UnmapNotify] = unmap, + [Expose] = expose, + [FocusIn] = focus, + [FocusOut] = focus, + [MotionNotify] = bmotion, + [ButtonPress] = bpress, + [ButtonRelease] = brelease, +/* + * Uncomment if you want the selection to disappear when you select something + * different in another window. + */ +/* [SelectionClear] = selclear_, */ + [SelectionNotify] = selnotify, +/* + * PropertyNotify is only turned on when there is some INCR transfer happening + * for the selection retrieval. + */ + [PropertyNotify] = propnotify, + [SelectionRequest] = selrequest, +}; + +/* Globals */ +static DC dc; +static XWindow xw; +static XSelection xsel; + +/* Font Ring Cache */ +enum { + FRC_NORMAL, + FRC_ITALIC, + FRC_BOLD, + FRC_ITALICBOLD +}; + +typedef struct { + XftFont *font; + int flags; + Rune unicodep; +} Fontcache; + +/* Fontcache is an array now. A new font will be appended to the array. */ +static Fontcache frc[16]; +static int frclen = 0; + +void +getbuttoninfo(XEvent *e) +{ + int type; + uint state = e->xbutton.state & ~(Button1Mask | forceselmod); + + sel.alt = IS_SET(MODE_ALTSCREEN); + + sel.oe.x = x2col(e->xbutton.x); + sel.oe.y = y2row(e->xbutton.y); + selnormalize(); + + sel.type = SEL_REGULAR; + for (type = 1; type < selmaskslen; ++type) { + if (match(selmasks[type], state)) { + sel.type = type; + break; + } + } +} + +void +mousereport(XEvent *e) +{ + int x = x2col(e->xbutton.x), y = y2row(e->xbutton.y), + button = e->xbutton.button, state = e->xbutton.state, + len; + char buf[40]; + static int ox, oy; + + /* from urxvt */ + if (e->xbutton.type == MotionNotify) { + if (x == ox && y == oy) + return; + if (!IS_SET(MODE_MOUSEMOTION) && !IS_SET(MODE_MOUSEMANY)) + return; + /* MOUSE_MOTION: no reporting if no button is pressed */ + if (IS_SET(MODE_MOUSEMOTION) && oldbutton == 3) + return; + + button = oldbutton + 32; + ox = x; + oy = y; + } else { + if (!IS_SET(MODE_MOUSESGR) && e->xbutton.type == ButtonRelease) { + button = 3; + } else { + button -= Button1; + if (button >= 3) + button += 64 - 3; + } + if (e->xbutton.type == ButtonPress) { + oldbutton = button; + ox = x; + oy = y; + } else if (e->xbutton.type == ButtonRelease) { + oldbutton = 3; + /* MODE_MOUSEX10: no button release reporting */ + if (IS_SET(MODE_MOUSEX10)) + return; + if (button == 64 || button == 65) + return; + } + } + + if (!IS_SET(MODE_MOUSEX10)) { + button += ((state & ShiftMask ) ? 4 : 0) + + ((state & Mod4Mask ) ? 8 : 0) + + ((state & ControlMask) ? 16 : 0); + } + + if (IS_SET(MODE_MOUSESGR)) { + len = snprintf(buf, sizeof(buf), "\033[<%d;%d;%d%c", + button, x+1, y+1, + e->xbutton.type == ButtonRelease ? 'm' : 'M'); + } else if (x < 223 && y < 223) { + len = snprintf(buf, sizeof(buf), "\033[M%c%c%c", + 32+button, 32+x+1, 32+y+1); + } else { + return; + } + + ttywrite(buf, len); +} + +void +bpress(XEvent *e) +{ + struct timespec now; + MouseShortcut *ms; + + if (IS_SET(MODE_MOUSE) && !(e->xbutton.state & forceselmod)) { + mousereport(e); + return; + } + + for (ms = mshortcuts; ms < mshortcuts + mshortcutslen; ms++) { + if (e->xbutton.button == ms->b + && match(ms->mask, e->xbutton.state)) { + ttysend(ms->s, strlen(ms->s)); + return; + } + } + + if (e->xbutton.button == Button1) { + clock_gettime(CLOCK_MONOTONIC, &now); + + /* Clear previous selection, logically and visually. */ + selclear_(NULL); + sel.mode = SEL_EMPTY; + sel.type = SEL_REGULAR; + sel.oe.x = sel.ob.x = x2col(e->xbutton.x); + sel.oe.y = sel.ob.y = y2row(e->xbutton.y); + + /* + * If the user clicks below predefined timeouts specific + * snapping behaviour is exposed. + */ + if (TIMEDIFF(now, sel.tclick2) <= tripleclicktimeout) { + sel.snap = SNAP_LINE; + } else if (TIMEDIFF(now, sel.tclick1) <= doubleclicktimeout) { + sel.snap = SNAP_WORD; + } else { + sel.snap = 0; + } + selnormalize(); + + if (sel.snap != 0) + sel.mode = SEL_READY; + tsetdirt(sel.nb.y, sel.ne.y); + sel.tclick2 = sel.tclick1; + sel.tclick1 = now; + } +} + +void +selcopy(Time t) +{ + xsetsel(getsel(), t); +} + +void +propnotify(XEvent *e) +{ + XPropertyEvent *xpev; + Atom clipboard = XInternAtom(xw.dpy, "CLIPBOARD", 0); + + xpev = &e->xproperty; + if (xpev->state == PropertyNewValue && + (xpev->atom == XA_PRIMARY || + xpev->atom == clipboard)) { + selnotify(e); + } +} + +void +selnotify(XEvent *e) +{ + ulong nitems, ofs, rem; + int format; + uchar *data, *last, *repl; + Atom type, incratom, property; + + incratom = XInternAtom(xw.dpy, "INCR", 0); + + ofs = 0; + if (e->type == SelectionNotify) { + property = e->xselection.property; + } else if(e->type == PropertyNotify) { + property = e->xproperty.atom; + } else { + return; + } + if (property == None) + return; + + do { + if (XGetWindowProperty(xw.dpy, xw.win, property, ofs, + BUFSIZ/4, False, AnyPropertyType, + &type, &format, &nitems, &rem, + &data)) { + fprintf(stderr, "Clipboard allocation failed\n"); + return; + } + + if (e->type == PropertyNotify && nitems == 0 && rem == 0) { + /* + * If there is some PropertyNotify with no data, then + * this is the signal of the selection owner that all + * data has been transferred. We won't need to receive + * PropertyNotify events anymore. + */ + MODBIT(xw.attrs.event_mask, 0, PropertyChangeMask); + XChangeWindowAttributes(xw.dpy, xw.win, CWEventMask, + &xw.attrs); + } + + if (type == incratom) { + /* + * Activate the PropertyNotify events so we receive + * when the selection owner does send us the next + * chunk of data. + */ + MODBIT(xw.attrs.event_mask, 1, PropertyChangeMask); + XChangeWindowAttributes(xw.dpy, xw.win, CWEventMask, + &xw.attrs); + + /* + * Deleting the property is the transfer start signal. + */ + XDeleteProperty(xw.dpy, xw.win, (int)property); + continue; + } + + /* + * As seen in getsel: + * Line endings are inconsistent in the terminal and GUI world + * copy and pasting. When receiving some selection data, + * replace all '\n' with '\r'. + * FIXME: Fix the computer world. + */ + repl = data; + last = data + nitems * format / 8; + while ((repl = memchr(repl, '\n', last - repl))) { + *repl++ = '\r'; + } + + if (IS_SET(MODE_BRCKTPASTE) && ofs == 0) + ttywrite("\033[200~", 6); + ttysend((char *)data, nitems * format / 8); + if (IS_SET(MODE_BRCKTPASTE) && rem == 0) + ttywrite("\033[201~", 6); + XFree(data); + /* number of 32-bit chunks returned */ + ofs += nitems * format / 32; + } while (rem > 0); + + /* + * Deleting the property again tells the selection owner to send the + * next data chunk in the property. + */ + XDeleteProperty(xw.dpy, xw.win, (int)property); +} + +void +xselpaste(void) +{ + XConvertSelection(xw.dpy, XA_PRIMARY, xsel.xtarget, XA_PRIMARY, + xw.win, CurrentTime); +} + +void +xclipcopy(void) +{ + Atom clipboard; + + if (sel.clipboard != NULL) + free(sel.clipboard); + + if (sel.primary != NULL) { + sel.clipboard = xstrdup(sel.primary); + clipboard = XInternAtom(xw.dpy, "CLIPBOARD", 0); + XSetSelectionOwner(xw.dpy, clipboard, xw.win, CurrentTime); + } +} + +void +xclippaste(void) +{ + Atom clipboard; + + clipboard = XInternAtom(xw.dpy, "CLIPBOARD", 0); + XConvertSelection(xw.dpy, clipboard, xsel.xtarget, clipboard, + xw.win, CurrentTime); +} + +void +selclear_(XEvent *e) +{ + selclear(); +} + +void +selrequest(XEvent *e) +{ + XSelectionRequestEvent *xsre; + XSelectionEvent xev; + Atom xa_targets, string, clipboard; + char *seltext; + + xsre = (XSelectionRequestEvent *) e; + xev.type = SelectionNotify; + xev.requestor = xsre->requestor; + xev.selection = xsre->selection; + xev.target = xsre->target; + xev.time = xsre->time; + if (xsre->property == None) + xsre->property = xsre->target; + + /* reject */ + xev.property = None; + + xa_targets = XInternAtom(xw.dpy, "TARGETS", 0); + if (xsre->target == xa_targets) { + /* respond with the supported type */ + string = xsel.xtarget; + XChangeProperty(xsre->display, xsre->requestor, xsre->property, + XA_ATOM, 32, PropModeReplace, + (uchar *) &string, 1); + xev.property = xsre->property; + } else if (xsre->target == xsel.xtarget || xsre->target == XA_STRING) { + /* + * xith XA_STRING non ascii characters may be incorrect in the + * requestor. It is not our problem, use utf8. + */ + clipboard = XInternAtom(xw.dpy, "CLIPBOARD", 0); + if (xsre->selection == XA_PRIMARY) { + seltext = sel.primary; + } else if (xsre->selection == clipboard) { + seltext = sel.clipboard; + } else { + fprintf(stderr, + "Unhandled clipboard selection 0x%lx\n", + xsre->selection); + return; + } + if (seltext != NULL) { + XChangeProperty(xsre->display, xsre->requestor, + xsre->property, xsre->target, + 8, PropModeReplace, + (uchar *)seltext, strlen(seltext)); + xev.property = xsre->property; + } + } + + /* all done, send a notification to the listener */ + if (!XSendEvent(xsre->display, xsre->requestor, 1, 0, (XEvent *) &xev)) + fprintf(stderr, "Error sending SelectionNotify event\n"); +} + +void +xsetsel(char *str, Time t) +{ + free(sel.primary); + sel.primary = str; + + XSetSelectionOwner(xw.dpy, XA_PRIMARY, xw.win, t); + if (XGetSelectionOwner(xw.dpy, XA_PRIMARY) != xw.win) + selclear_(NULL); +} + +void +brelease(XEvent *e) +{ + if (IS_SET(MODE_MOUSE) && !(e->xbutton.state & forceselmod)) { + mousereport(e); + return; + } + + if (e->xbutton.button == Button2) { + xselpaste(); + } else if (e->xbutton.button == Button1) { + if (sel.mode == SEL_READY) { + getbuttoninfo(e); + selcopy(e->xbutton.time); + } else + selclear_(NULL); + sel.mode = SEL_IDLE; + tsetdirt(sel.nb.y, sel.ne.y); + } +} + +void +bmotion(XEvent *e) +{ + int oldey, oldex, oldsby, oldsey; + + if (IS_SET(MODE_MOUSE) && !(e->xbutton.state & forceselmod)) { + mousereport(e); + return; + } + + if (!sel.mode) + return; + + sel.mode = SEL_READY; + oldey = sel.oe.y; + oldex = sel.oe.x; + oldsby = sel.nb.y; + oldsey = sel.ne.y; + getbuttoninfo(e); + + if (oldey != sel.oe.y || oldex != sel.oe.x) + tsetdirt(MIN(sel.nb.y, oldsby), MAX(sel.ne.y, oldsey)); +} + +void +xresize(int col, int row) +{ + win.tw = MAX(1, col * win.cw); + win.th = MAX(1, row * win.ch); + + XFreePixmap(xw.dpy, xw.buf); + xw.buf = XCreatePixmap(xw.dpy, xw.win, win.w, win.h, + DefaultDepth(xw.dpy, xw.scr)); + XftDrawChange(xw.draw, xw.buf); + xclear(0, 0, win.w, win.h); +} + +ushort +sixd_to_16bit(int x) +{ + return x == 0 ? 0 : 0x3737 + 0x2828 * x; +} + +int +xloadcolor(int i, const char *name, Color *ncolor) +{ + XRenderColor color = { .alpha = 0xffff }; + + if (!name) { + if (BETWEEN(i, 16, 255)) { /* 256 color */ + if (i < 6*6*6+16) { /* same colors as xterm */ + color.red = sixd_to_16bit( ((i-16)/36)%6 ); + color.green = sixd_to_16bit( ((i-16)/6) %6 ); + color.blue = sixd_to_16bit( ((i-16)/1) %6 ); + } else { /* greyscale */ + color.red = 0x0808 + 0x0a0a * (i - (6*6*6+16)); + color.green = color.blue = color.red; + } + return XftColorAllocValue(xw.dpy, xw.vis, + xw.cmap, &color, ncolor); + } else + name = colorname[i]; + } + + return XftColorAllocName(xw.dpy, xw.vis, xw.cmap, name, ncolor); +} + +void +xloadcols(void) +{ + int i; + static int loaded; + Color *cp; + + dc.collen = MAX(colornamelen, 256); + dc.col = xmalloc(dc.collen * sizeof(Color)); + + if (loaded) { + for (cp = dc.col; cp < &dc.col[dc.collen]; ++cp) + XftColorFree(xw.dpy, xw.vis, xw.cmap, cp); + } + + for (i = 0; i < dc.collen; i++) + if (!xloadcolor(i, NULL, &dc.col[i])) { + if (colorname[i]) + die("Could not allocate color '%s'\n", colorname[i]); + else + die("Could not allocate color %d\n", i); + } + loaded = 1; +} + +int +xsetcolorname(int x, const char *name) +{ + Color ncolor; + + if (!BETWEEN(x, 0, dc.collen)) + return 1; + + + if (!xloadcolor(x, name, &ncolor)) + return 1; + + XftColorFree(xw.dpy, xw.vis, xw.cmap, &dc.col[x]); + dc.col[x] = ncolor; + + return 0; +} + +/* + * Absolute coordinates. + */ +void +xclear(int x1, int y1, int x2, int y2) +{ + XftDrawRect(xw.draw, + &dc.col[IS_SET(MODE_REVERSE)? defaultfg : defaultbg], + x1, y1, x2-x1, y2-y1); +} + +void +xhints(void) +{ + XClassHint class = {opt_name ? opt_name : termname, + opt_class ? opt_class : termname}; + XWMHints wm = {.flags = InputHint, .input = 1}; + XSizeHints *sizeh = NULL; + + sizeh = XAllocSizeHints(); + + sizeh->flags = PSize | PResizeInc | PBaseSize; + sizeh->height = win.h; + sizeh->width = win.w; + sizeh->height_inc = win.ch; + sizeh->width_inc = win.cw; + sizeh->base_height = 2 * borderpx; + sizeh->base_width = 2 * borderpx; + if (xw.isfixed) { + sizeh->flags |= PMaxSize | PMinSize; + sizeh->min_width = sizeh->max_width = win.w; + sizeh->min_height = sizeh->max_height = win.h; + } + if (xw.gm & (XValue|YValue)) { + sizeh->flags |= USPosition | PWinGravity; + sizeh->x = xw.l; + sizeh->y = xw.t; + sizeh->win_gravity = xgeommasktogravity(xw.gm); + } + + XSetWMProperties(xw.dpy, xw.win, NULL, NULL, NULL, 0, sizeh, &wm, + &class); + XFree(sizeh); +} + +int +xgeommasktogravity(int mask) +{ + switch (mask & (XNegative|YNegative)) { + case 0: + return NorthWestGravity; + case XNegative: + return NorthEastGravity; + case YNegative: + return SouthWestGravity; + } + + return SouthEastGravity; +} + +int +xloadfont(Font *f, FcPattern *pattern) +{ + FcPattern *configured; + FcPattern *match; + FcResult result; + XGlyphInfo extents; + int wantattr, haveattr; + + /* + * Manually configure instead of calling XftMatchFont + * so that we can use the configured pattern for + * "missing glyph" lookups. + */ + configured = FcPatternDuplicate(pattern); + if (!configured) + return 1; + + FcConfigSubstitute(NULL, configured, FcMatchPattern); + XftDefaultSubstitute(xw.dpy, xw.scr, configured); + + match = FcFontMatch(NULL, configured, &result); + if (!match) { + FcPatternDestroy(configured); + return 1; + } + + if (!(f->match = XftFontOpenPattern(xw.dpy, match))) { + FcPatternDestroy(configured); + FcPatternDestroy(match); + return 1; + } + + if ((XftPatternGetInteger(pattern, "slant", 0, &wantattr) == + XftResultMatch)) { + /* + * Check if xft was unable to find a font with the appropriate + * slant but gave us one anyway. Try to mitigate. + */ + if ((XftPatternGetInteger(f->match->pattern, "slant", 0, + &haveattr) != XftResultMatch) || haveattr < wantattr) { + f->badslant = 1; + fputs("st: font slant does not match\n", stderr); + } + } + + if ((XftPatternGetInteger(pattern, "weight", 0, &wantattr) == + XftResultMatch)) { + if ((XftPatternGetInteger(f->match->pattern, "weight", 0, + &haveattr) != XftResultMatch) || haveattr != wantattr) { + f->badweight = 1; + fputs("st: font weight does not match\n", stderr); + } + } + + XftTextExtentsUtf8(xw.dpy, f->match, + (const FcChar8 *) ascii_printable, + strlen(ascii_printable), &extents); + + f->set = NULL; + f->pattern = configured; + + f->ascent = f->match->ascent; + f->descent = f->match->descent; + f->lbearing = 0; + f->rbearing = f->match->max_advance_width; + + f->height = f->ascent + f->descent; + f->width = DIVCEIL(extents.xOff, strlen(ascii_printable)); + + return 0; +} + +void +xloadfonts(char *fontstr, double fontsize) +{ + FcPattern *pattern; + double fontval; + float ceilf(float); + + if (fontstr[0] == '-') { + pattern = XftXlfdParse(fontstr, False, False); + } else { + pattern = FcNameParse((FcChar8 *)fontstr); + } + + if (!pattern) + die("st: can't open font %s\n", fontstr); + + if (fontsize > 1) { + FcPatternDel(pattern, FC_PIXEL_SIZE); + FcPatternDel(pattern, FC_SIZE); + FcPatternAddDouble(pattern, FC_PIXEL_SIZE, (double)fontsize); + usedfontsize = fontsize; + } else { + if (FcPatternGetDouble(pattern, FC_PIXEL_SIZE, 0, &fontval) == + FcResultMatch) { + usedfontsize = fontval; + } else if (FcPatternGetDouble(pattern, FC_SIZE, 0, &fontval) == + FcResultMatch) { + usedfontsize = -1; + } else { + /* + * Default font size is 12, if none given. This is to + * have a known usedfontsize value. + */ + FcPatternAddDouble(pattern, FC_PIXEL_SIZE, 12); + usedfontsize = 12; + } + defaultfontsize = usedfontsize; + } + + if (xloadfont(&dc.font, pattern)) + die("st: can't open font %s\n", fontstr); + + if (usedfontsize < 0) { + FcPatternGetDouble(dc.font.match->pattern, + FC_PIXEL_SIZE, 0, &fontval); + usedfontsize = fontval; + if (fontsize == 0) + defaultfontsize = fontval; + } + + /* Setting character width and height. */ + win.cw = ceilf(dc.font.width * cwscale); + win.ch = ceilf(dc.font.height * chscale); + + FcPatternDel(pattern, FC_SLANT); + FcPatternAddInteger(pattern, FC_SLANT, FC_SLANT_ITALIC); + if (xloadfont(&dc.ifont, pattern)) + die("st: can't open font %s\n", fontstr); + + FcPatternDel(pattern, FC_WEIGHT); + FcPatternAddInteger(pattern, FC_WEIGHT, FC_WEIGHT_BOLD); + if (xloadfont(&dc.ibfont, pattern)) + die("st: can't open font %s\n", fontstr); + + FcPatternDel(pattern, FC_SLANT); + FcPatternAddInteger(pattern, FC_SLANT, FC_SLANT_ROMAN); + if (xloadfont(&dc.bfont, pattern)) + die("st: can't open font %s\n", fontstr); + + FcPatternDestroy(pattern); +} + +void +xunloadfont(Font *f) +{ + XftFontClose(xw.dpy, f->match); + FcPatternDestroy(f->pattern); + if (f->set) + FcFontSetDestroy(f->set); +} + +void +xunloadfonts(void) +{ + /* Free the loaded fonts in the font cache. */ + while (frclen > 0) + XftFontClose(xw.dpy, frc[--frclen].font); + + xunloadfont(&dc.font); + xunloadfont(&dc.bfont); + xunloadfont(&dc.ifont); + xunloadfont(&dc.ibfont); +} + +void +xinit(void) +{ + XGCValues gcvalues; + Cursor cursor; + Window parent; + pid_t thispid = getpid(); + XColor xmousefg, xmousebg; + + if (!(xw.dpy = XOpenDisplay(NULL))) + die("Can't open display\n"); + xw.scr = XDefaultScreen(xw.dpy); + xw.vis = XDefaultVisual(xw.dpy, xw.scr); + + /* font */ + if (!FcInit()) + die("Could not init fontconfig.\n"); + + usedfont = (opt_font == NULL)? font : opt_font; + xloadfonts(usedfont, 0); + + /* colors */ + xw.cmap = XDefaultColormap(xw.dpy, xw.scr); + xloadcols(); + + /* adjust fixed window geometry */ + win.w = 2 * borderpx + term.col * win.cw; + win.h = 2 * borderpx + term.row * win.ch; + if (xw.gm & XNegative) + xw.l += DisplayWidth(xw.dpy, xw.scr) - win.w - 2; + if (xw.gm & YNegative) + xw.t += DisplayHeight(xw.dpy, xw.scr) - win.h - 2; + + /* Events */ + xw.attrs.background_pixel = dc.col[defaultbg].pixel; + xw.attrs.border_pixel = dc.col[defaultbg].pixel; + xw.attrs.bit_gravity = NorthWestGravity; + xw.attrs.event_mask = FocusChangeMask | KeyPressMask + | ExposureMask | VisibilityChangeMask | StructureNotifyMask + | ButtonMotionMask | ButtonPressMask | ButtonReleaseMask; + xw.attrs.colormap = xw.cmap; + + if (!(opt_embed && (parent = strtol(opt_embed, NULL, 0)))) + parent = XRootWindow(xw.dpy, xw.scr); + xw.win = XCreateWindow(xw.dpy, parent, xw.l, xw.t, + win.w, win.h, 0, XDefaultDepth(xw.dpy, xw.scr), InputOutput, + xw.vis, CWBackPixel | CWBorderPixel | CWBitGravity + | CWEventMask | CWColormap, &xw.attrs); + + memset(&gcvalues, 0, sizeof(gcvalues)); + gcvalues.graphics_exposures = False; + dc.gc = XCreateGC(xw.dpy, parent, GCGraphicsExposures, + &gcvalues); + xw.buf = XCreatePixmap(xw.dpy, xw.win, win.w, win.h, + DefaultDepth(xw.dpy, xw.scr)); + XSetForeground(xw.dpy, dc.gc, dc.col[defaultbg].pixel); + XFillRectangle(xw.dpy, xw.buf, dc.gc, 0, 0, win.w, win.h); + + /* Xft rendering context */ + xw.draw = XftDrawCreate(xw.dpy, xw.buf, xw.vis, xw.cmap); + + /* input methods */ + if ((xw.xim = XOpenIM(xw.dpy, NULL, NULL, NULL)) == NULL) { + XSetLocaleModifiers("@im=local"); + if ((xw.xim = XOpenIM(xw.dpy, NULL, NULL, NULL)) == NULL) { + XSetLocaleModifiers("@im="); + if ((xw.xim = XOpenIM(xw.dpy, + NULL, NULL, NULL)) == NULL) { + die("XOpenIM failed. Could not open input" + " device.\n"); + } + } + } + xw.xic = XCreateIC(xw.xim, XNInputStyle, XIMPreeditNothing + | XIMStatusNothing, XNClientWindow, xw.win, + XNFocusWindow, xw.win, NULL); + if (xw.xic == NULL) + die("XCreateIC failed. Could not obtain input method.\n"); + + /* white cursor, black outline */ + cursor = XCreateFontCursor(xw.dpy, mouseshape); + XDefineCursor(xw.dpy, xw.win, cursor); + + if (XParseColor(xw.dpy, xw.cmap, colorname[mousefg], &xmousefg) == 0) { + xmousefg.red = 0xffff; + xmousefg.green = 0xffff; + xmousefg.blue = 0xffff; + } + + if (XParseColor(xw.dpy, xw.cmap, colorname[mousebg], &xmousebg) == 0) { + xmousebg.red = 0x0000; + xmousebg.green = 0x0000; + xmousebg.blue = 0x0000; + } + + XRecolorCursor(xw.dpy, cursor, &xmousefg, &xmousebg); + + 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); + XSetWMProtocols(xw.dpy, xw.win, &xw.wmdeletewin, 1); + + xw.netwmpid = XInternAtom(xw.dpy, "_NET_WM_PID", False); + XChangeProperty(xw.dpy, xw.win, xw.netwmpid, XA_CARDINAL, 32, + PropModeReplace, (uchar *)&thispid, 1); + + resettitle(); + XMapWindow(xw.dpy, xw.win); + xhints(); + XSync(xw.dpy, False); + + xsel.xtarget = XInternAtom(xw.dpy, "UTF8_STRING", 0); + if (xsel.xtarget == None) + xsel.xtarget = XA_STRING; +} + +int +xmakeglyphfontspecs(XftGlyphFontSpec *specs, const Glyph *glyphs, int len, int x, int y) +{ + float winx = borderpx + x * win.cw, winy = borderpx + y * win.ch, xp, yp; + ushort mode, prevmode = USHRT_MAX; + Font *font = &dc.font; + int frcflags = FRC_NORMAL; + float runewidth = win.cw; + Rune rune; + FT_UInt glyphidx; + FcResult fcres; + FcPattern *fcpattern, *fontpattern; + FcFontSet *fcsets[] = { NULL }; + FcCharSet *fccharset; + int i, f, numspecs = 0; + + for (i = 0, xp = winx, yp = winy + font->ascent; i < len; ++i) { + /* Fetch rune and mode for current glyph. */ + rune = glyphs[i].u; + mode = glyphs[i].mode; + + /* Skip dummy wide-character spacing. */ + if (mode == ATTR_WDUMMY) + continue; + + /* Determine font for glyph if different from previous glyph. */ + if (prevmode != mode) { + prevmode = mode; + font = &dc.font; + frcflags = FRC_NORMAL; + runewidth = win.cw * ((mode & ATTR_WIDE) ? 2.0f : 1.0f); + if ((mode & ATTR_ITALIC) && (mode & ATTR_BOLD)) { + font = &dc.ibfont; + frcflags = FRC_ITALICBOLD; + } else if (mode & ATTR_ITALIC) { + font = &dc.ifont; + frcflags = FRC_ITALIC; + } else if (mode & ATTR_BOLD) { + font = &dc.bfont; + frcflags = FRC_BOLD; + } + yp = winy + font->ascent; + } + + /* Lookup character index with default font. */ + glyphidx = XftCharIndex(xw.dpy, font->match, rune); + if (glyphidx) { + specs[numspecs].font = font->match; + specs[numspecs].glyph = glyphidx; + specs[numspecs].x = (short)xp; + specs[numspecs].y = (short)yp; + xp += runewidth; + numspecs++; + continue; + } + + /* Fallback on font cache, search the font cache for match. */ + for (f = 0; f < frclen; f++) { + glyphidx = XftCharIndex(xw.dpy, frc[f].font, rune); + /* Everything correct. */ + if (glyphidx && frc[f].flags == frcflags) + break; + /* We got a default font for a not found glyph. */ + if (!glyphidx && frc[f].flags == frcflags + && frc[f].unicodep == rune) { + break; + } + } + + /* Nothing was found. Use fontconfig to find matching font. */ + if (f >= frclen) { + if (!font->set) + font->set = FcFontSort(0, font->pattern, + 1, 0, &fcres); + fcsets[0] = font->set; + + /* + * Nothing was found in the cache. Now use + * some dozen of Fontconfig calls to get the + * font for one single character. + * + * Xft and fontconfig are design failures. + */ + fcpattern = FcPatternDuplicate(font->pattern); + fccharset = FcCharSetCreate(); + + FcCharSetAddChar(fccharset, rune); + FcPatternAddCharSet(fcpattern, FC_CHARSET, + fccharset); + FcPatternAddBool(fcpattern, FC_SCALABLE, 1); + + FcConfigSubstitute(0, fcpattern, + FcMatchPattern); + FcDefaultSubstitute(fcpattern); + + fontpattern = FcFontSetMatch(0, fcsets, 1, + fcpattern, &fcres); + + /* + * Overwrite or create the new cache entry. + */ + if (frclen >= LEN(frc)) { + frclen = LEN(frc) - 1; + XftFontClose(xw.dpy, frc[frclen].font); + frc[frclen].unicodep = 0; + } + + frc[frclen].font = XftFontOpenPattern(xw.dpy, + fontpattern); + frc[frclen].flags = frcflags; + frc[frclen].unicodep = rune; + + glyphidx = XftCharIndex(xw.dpy, frc[frclen].font, rune); + + f = frclen; + frclen++; + + FcPatternDestroy(fcpattern); + FcCharSetDestroy(fccharset); + } + + specs[numspecs].font = frc[f].font; + specs[numspecs].glyph = glyphidx; + specs[numspecs].x = (short)xp; + specs[numspecs].y = (short)yp; + xp += runewidth; + numspecs++; + } + + return numspecs; +} + +void +xdrawglyphfontspecs(const XftGlyphFontSpec *specs, Glyph base, int len, int x, int y) +{ + int charlen = len * ((base.mode & ATTR_WIDE) ? 2 : 1); + int winx = borderpx + x * win.cw, winy = borderpx + y * win.ch, + width = charlen * win.cw; + Color *fg, *bg, *temp, revfg, revbg, truefg, truebg; + XRenderColor colfg, colbg; + XRectangle r; + + /* Fallback on color display for attributes not supported by the font */ + if (base.mode & ATTR_ITALIC && base.mode & ATTR_BOLD) { + if (dc.ibfont.badslant || dc.ibfont.badweight) + base.fg = defaultattr; + } else if ((base.mode & ATTR_ITALIC && dc.ifont.badslant) || + (base.mode & ATTR_BOLD && dc.bfont.badweight)) { + base.fg = defaultattr; + } + + if (IS_TRUECOL(base.fg)) { + colfg.alpha = 0xffff; + colfg.red = TRUERED(base.fg); + colfg.green = TRUEGREEN(base.fg); + colfg.blue = TRUEBLUE(base.fg); + XftColorAllocValue(xw.dpy, xw.vis, xw.cmap, &colfg, &truefg); + fg = &truefg; + } else { + fg = &dc.col[base.fg]; + } + + if (IS_TRUECOL(base.bg)) { + colbg.alpha = 0xffff; + colbg.green = TRUEGREEN(base.bg); + colbg.red = TRUERED(base.bg); + colbg.blue = TRUEBLUE(base.bg); + XftColorAllocValue(xw.dpy, xw.vis, xw.cmap, &colbg, &truebg); + bg = &truebg; + } else { + bg = &dc.col[base.bg]; + } + + /* Change basic system colors [0-7] to bright system colors [8-15] */ + if ((base.mode & ATTR_BOLD_FAINT) == ATTR_BOLD && BETWEEN(base.fg, 0, 7)) + fg = &dc.col[base.fg + 8]; + + if (IS_SET(MODE_REVERSE)) { + if (fg == &dc.col[defaultfg]) { + fg = &dc.col[defaultbg]; + } else { + colfg.red = ~fg->color.red; + colfg.green = ~fg->color.green; + colfg.blue = ~fg->color.blue; + colfg.alpha = fg->color.alpha; + XftColorAllocValue(xw.dpy, xw.vis, xw.cmap, &colfg, + &revfg); + fg = &revfg; + } + + if (bg == &dc.col[defaultbg]) { + bg = &dc.col[defaultfg]; + } else { + colbg.red = ~bg->color.red; + colbg.green = ~bg->color.green; + colbg.blue = ~bg->color.blue; + colbg.alpha = bg->color.alpha; + XftColorAllocValue(xw.dpy, xw.vis, xw.cmap, &colbg, + &revbg); + bg = &revbg; + } + } + + if (base.mode & ATTR_REVERSE) { + temp = fg; + fg = bg; + bg = temp; + } + + if ((base.mode & ATTR_BOLD_FAINT) == ATTR_FAINT) { + colfg.red = fg->color.red / 2; + colfg.green = fg->color.green / 2; + colfg.blue = fg->color.blue / 2; + XftColorAllocValue(xw.dpy, xw.vis, xw.cmap, &colfg, &revfg); + fg = &revfg; + } + + if (base.mode & ATTR_BLINK && term.mode & MODE_BLINK) + fg = bg; + + if (base.mode & ATTR_INVISIBLE) + fg = bg; + + /* Intelligent cleaning up of the borders. */ + if (x == 0) { + xclear(0, (y == 0)? 0 : winy, borderpx, + winy + win.ch + ((y >= term.row-1)? win.h : 0)); + } + if (x + charlen >= term.col) { + xclear(winx + width, (y == 0)? 0 : winy, win.w, + ((y >= term.row-1)? win.h : (winy + win.ch))); + } + if (y == 0) + xclear(winx, 0, winx + width, borderpx); + if (y == term.row-1) + xclear(winx, winy + win.ch, winx + width, win.h); + + /* Clean up the region we want to draw to. */ + XftDrawRect(xw.draw, bg, winx, winy, width, win.ch); + + /* 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); + + /* Render the glyphs. */ + XftDrawGlyphFontSpec(xw.draw, fg, specs, len); + + /* Render underline and strikethrough. */ + if (base.mode & ATTR_UNDERLINE) { + XftDrawRect(xw.draw, fg, winx, winy + dc.font.ascent + 1, + width, 1); + } + + if (base.mode & ATTR_STRUCK) { + XftDrawRect(xw.draw, fg, winx, winy + 2 * dc.font.ascent / 3, + width, 1); + } + + /* Reset clip to none. */ + XftDrawSetClip(xw.draw, 0); +} + +void +xdrawglyph(Glyph g, int x, int y) +{ + int numspecs; + XftGlyphFontSpec spec; + + numspecs = xmakeglyphfontspecs(&spec, &g, 1, x, y); + xdrawglyphfontspecs(&spec, g, numspecs, x, y); +} + +void +xdrawcursor(void) +{ + static int oldx = 0, oldy = 0; + int curx; + Glyph g = {' ', ATTR_NULL, defaultbg, defaultcs}, og; + int ena_sel = sel.ob.x != -1 && sel.alt == IS_SET(MODE_ALTSCREEN); + Color drawcol; + + LIMIT(oldx, 0, term.col-1); + LIMIT(oldy, 0, term.row-1); + + curx = term.c.x; + + /* adjust position if in dummy */ + if (term.line[oldy][oldx].mode & ATTR_WDUMMY) + oldx--; + if (term.line[term.c.y][curx].mode & ATTR_WDUMMY) + curx--; + + /* remove the old cursor */ + og = term.line[oldy][oldx]; + if (ena_sel && selected(oldx, oldy)) + og.mode ^= ATTR_REVERSE; + xdrawglyph(og, oldx, oldy); + + g.u = term.line[term.c.y][term.c.x].u; + + /* + * Select the right color for the right mode. + */ + if (IS_SET(MODE_REVERSE)) { + g.mode |= ATTR_REVERSE; + g.bg = defaultfg; + if (ena_sel && selected(term.c.x, term.c.y)) { + drawcol = dc.col[defaultcs]; + g.fg = defaultrcs; + } else { + drawcol = dc.col[defaultrcs]; + g.fg = defaultcs; + } + } else { + if (ena_sel && selected(term.c.x, term.c.y)) { + drawcol = dc.col[defaultrcs]; + g.fg = defaultfg; + g.bg = defaultrcs; + } else { + drawcol = dc.col[defaultcs]; + } + } + + if (IS_SET(MODE_HIDE)) + return; + + /* draw the new one */ + if (win.state & WIN_FOCUSED) { + switch (win.cursor) { + case 7: /* st extension: snowman */ + utf8decode("☃", &g.u, UTF_SIZ); + case 0: /* Blinking Block */ + case 1: /* Blinking Block (Default) */ + case 2: /* Steady Block */ + g.mode |= term.line[term.c.y][curx].mode & ATTR_WIDE; + xdrawglyph(g, term.c.x, term.c.y); + break; + case 3: /* Blinking Underline */ + case 4: /* Steady Underline */ + XftDrawRect(xw.draw, &drawcol, + borderpx + curx * win.cw, + borderpx + (term.c.y + 1) * win.ch - \ + cursorthickness, + win.cw, cursorthickness); + break; + case 5: /* Blinking bar */ + case 6: /* Steady bar */ + XftDrawRect(xw.draw, &drawcol, + borderpx + curx * win.cw, + borderpx + term.c.y * win.ch, + cursorthickness, win.ch); + break; + } + } else { + XftDrawRect(xw.draw, &drawcol, + borderpx + curx * win.cw, + borderpx + term.c.y * win.ch, + win.cw - 1, 1); + XftDrawRect(xw.draw, &drawcol, + borderpx + curx * win.cw, + borderpx + term.c.y * win.ch, + 1, win.ch - 1); + XftDrawRect(xw.draw, &drawcol, + borderpx + (curx + 1) * win.cw - 1, + borderpx + term.c.y * win.ch, + 1, win.ch - 1); + XftDrawRect(xw.draw, &drawcol, + borderpx + curx * win.cw, + borderpx + (term.c.y + 1) * win.ch - 1, + win.cw, 1); + } + oldx = curx, oldy = term.c.y; +} + +void +xsetenv(void) +{ + char buf[sizeof(long) * 8 + 1]; + + snprintf(buf, sizeof(buf), "%lu", xw.win); + setenv("WINDOWID", buf, 1); +} + +void +xsettitle(char *p) +{ + XTextProperty prop; + + Xutf8TextListToTextProperty(xw.dpy, &p, 1, XUTF8StringStyle, + &prop); + XSetWMName(xw.dpy, xw.win, &prop); + XSetTextProperty(xw.dpy, xw.win, &prop, xw.netwmname); + XFree(prop.value); +} + +void +draw(void) +{ + drawregion(0, 0, term.col, term.row); + XCopyArea(xw.dpy, xw.buf, xw.win, dc.gc, 0, 0, win.w, + win.h, 0, 0); + XSetForeground(xw.dpy, dc.gc, + dc.col[IS_SET(MODE_REVERSE)? + defaultfg : defaultbg].pixel); +} + +void +drawregion(int x1, int y1, int x2, int y2) +{ + int i, x, y, ox, numspecs; + Glyph base, new; + XftGlyphFontSpec *specs; + int ena_sel = sel.ob.x != -1 && sel.alt == IS_SET(MODE_ALTSCREEN); + + if (!(win.state & WIN_VISIBLE)) + return; + + for (y = y1; y < y2; y++) { + if (!term.dirty[y]) + continue; + + term.dirty[y] = 0; + + specs = term.specbuf; + numspecs = xmakeglyphfontspecs(specs, &term.line[y][x1], x2 - x1, x1, y); + + i = ox = 0; + for (x = x1; x < x2 && i < numspecs; x++) { + new = term.line[y][x]; + if (new.mode == ATTR_WDUMMY) + continue; + if (ena_sel && selected(x, y)) + new.mode ^= ATTR_REVERSE; + if (i > 0 && ATTRCMP(base, new)) { + xdrawglyphfontspecs(specs, base, i, ox, y); + specs += i; + numspecs -= i; + i = 0; + } + if (i == 0) { + ox = x; + base = new; + } + i++; + } + if (i > 0) + xdrawglyphfontspecs(specs, base, i, ox, y); + } + xdrawcursor(); +} + +void +expose(XEvent *ev) +{ + redraw(); +} + +void +visibility(XEvent *ev) +{ + XVisibilityEvent *e = &ev->xvisibility; + + MODBIT(win.state, e->state != VisibilityFullyObscured, WIN_VISIBLE); +} + +void +unmap(XEvent *ev) +{ + win.state &= ~WIN_VISIBLE; +} + +void +xsetpointermotion(int set) +{ + MODBIT(xw.attrs.event_mask, set, PointerMotionMask); + XChangeWindowAttributes(xw.dpy, xw.win, CWEventMask, &xw.attrs); +} + +void +xseturgency(int add) +{ + XWMHints *h = XGetWMHints(xw.dpy, xw.win); + + MODBIT(h->flags, add, XUrgencyHint); + XSetWMHints(xw.dpy, xw.win, h); + XFree(h); +} + +void +xbell(int vol) +{ + XkbBell(xw.dpy, xw.win, vol, (Atom)NULL); +} + +unsigned long +xwinid(void) +{ + return xw.win; +} + +void +focus(XEvent *ev) +{ + XFocusChangeEvent *e = &ev->xfocus; + + if (e->mode == NotifyGrab) + return; + + if (ev->type == FocusIn) { + XSetICFocus(xw.xic); + win.state |= WIN_FOCUSED; + xseturgency(0); + if (IS_SET(MODE_FOCUS)) + ttywrite("\033[I", 3); + } else { + XUnsetICFocus(xw.xic); + win.state &= ~WIN_FOCUSED; + if (IS_SET(MODE_FOCUS)) + ttywrite("\033[O", 3); + } +} + +void +kpress(XEvent *ev) +{ + XKeyEvent *e = &ev->xkey; + KeySym ksym; + char buf[32], *customkey; + int len; + Rune c; + Status status; + Shortcut *bp; + + if (IS_SET(MODE_KBDLOCK)) + return; + + len = XmbLookupString(xw.xic, e, buf, sizeof buf, &ksym, &status); + /* 1. shortcuts */ + for (bp = shortcuts; bp < shortcuts + shortcutslen; bp++) { + if (ksym == bp->keysym && match(bp->mod, e->state)) { + bp->func(&(bp->arg)); + return; + } + } + + /* 2. custom keys from config.h */ + if ((customkey = kmap(ksym, e->state))) { + ttysend(customkey, strlen(customkey)); + return; + } + + /* 3. composed string from input method */ + if (len == 0) + return; + if (len == 1 && e->state & Mod1Mask) { + if (IS_SET(MODE_8BIT)) { + if (*buf < 0177) { + c = *buf | 0x80; + len = utf8encode(c, buf); + } + } else { + buf[1] = buf[0]; + buf[0] = '\033'; + len = 2; + } + } + ttysend(buf, len); +} + + +void +cmessage(XEvent *e) +{ + /* + * See xembed specs + * http://standards.freedesktop.org/xembed-spec/xembed-spec-latest.html + */ + if (e->xclient.message_type == xw.xembed && e->xclient.format == 32) { + if (e->xclient.data.l[1] == XEMBED_FOCUS_IN) { + win.state |= WIN_FOCUSED; + xseturgency(0); + } else if (e->xclient.data.l[1] == XEMBED_FOCUS_OUT) { + win.state &= ~WIN_FOCUSED; + } + } else if (e->xclient.data.l[0] == xw.wmdeletewin) { + /* Send SIGHUP to shell */ + kill(pid, SIGHUP); + exit(0); + } +} + +void +resize(XEvent *e) +{ + if (e->xconfigure.width == win.w && e->xconfigure.height == win.h) + return; + + cresize(e->xconfigure.width, e->xconfigure.height); + ttyresize(); +} + +void +run(void) +{ + XEvent ev; + int w = win.w, h = win.h; + fd_set rfd; + int xfd = XConnectionNumber(xw.dpy), xev, blinkset = 0, dodraw = 0; + struct timespec drawtimeout, *tv = NULL, now, last, lastblink; + long deltatime; + + /* Waiting for window mapping */ + do { + XNextEvent(xw.dpy, &ev); + /* + * This XFilterEvent call is required because of XOpenIM. It + * does filter out the key event and some client message for + * the input method too. + */ + if (XFilterEvent(&ev, None)) + continue; + if (ev.type == ConfigureNotify) { + w = ev.xconfigure.width; + h = ev.xconfigure.height; + } + } while (ev.type != MapNotify); + + cresize(w, h); + ttynew(); + ttyresize(); + + clock_gettime(CLOCK_MONOTONIC, &last); + lastblink = last; + + for (xev = actionfps;;) { + FD_ZERO(&rfd); + FD_SET(cmdfd, &rfd); + FD_SET(xfd, &rfd); + + if (pselect(MAX(xfd, cmdfd)+1, &rfd, NULL, NULL, tv, NULL) < 0) { + if (errno == EINTR) + continue; + die("select failed: %s\n", strerror(errno)); + } + if (FD_ISSET(cmdfd, &rfd)) { + ttyread(); + if (blinktimeout) { + blinkset = tattrset(ATTR_BLINK); + if (!blinkset) + MODBIT(term.mode, 0, MODE_BLINK); + } + } + + if (FD_ISSET(xfd, &rfd)) + xev = actionfps; + + clock_gettime(CLOCK_MONOTONIC, &now); + drawtimeout.tv_sec = 0; + drawtimeout.tv_nsec = (1000 * 1E6)/ xfps; + tv = &drawtimeout; + + dodraw = 0; + if (blinktimeout && TIMEDIFF(now, lastblink) > blinktimeout) { + tsetdirtattr(ATTR_BLINK); + term.mode ^= MODE_BLINK; + lastblink = now; + dodraw = 1; + } + deltatime = TIMEDIFF(now, last); + if (deltatime > 1000 / (xev ? xfps : actionfps)) { + dodraw = 1; + last = now; + } + + if (dodraw) { + while (XPending(xw.dpy)) { + XNextEvent(xw.dpy, &ev); + if (XFilterEvent(&ev, None)) + continue; + if (handler[ev.type]) + (handler[ev.type])(&ev); + } + + draw(); + XFlush(xw.dpy); + + if (xev && !FD_ISSET(xfd, &rfd)) + xev--; + if (!FD_ISSET(cmdfd, &rfd) && !FD_ISSET(xfd, &rfd)) { + if (blinkset) { + if (TIMEDIFF(now, lastblink) \ + > blinktimeout) { + drawtimeout.tv_nsec = 1000; + } else { + drawtimeout.tv_nsec = (1E6 * \ + (blinktimeout - \ + TIMEDIFF(now, + lastblink))); + } + drawtimeout.tv_sec = \ + drawtimeout.tv_nsec / 1E9; + drawtimeout.tv_nsec %= (long)1E9; + } else { + tv = NULL; + } + } + } + } +} + +int +main(int argc, char *argv[]) +{ + xw.l = xw.t = 0; + xw.isfixed = False; + win.cursor = cursorshape; + + ARGBEGIN { + case 'a': + allowaltscreen = 0; + break; + case 'c': + opt_class = EARGF(usage()); + break; + case 'e': + if (argc > 0) + --argc, ++argv; + goto run; + case 'f': + opt_font = EARGF(usage()); + break; + case 'g': + xw.gm = XParseGeometry(EARGF(usage()), + &xw.l, &xw.t, &cols, &rows); + break; + case 'i': + xw.isfixed = 1; + break; + case 'o': + opt_io = EARGF(usage()); + break; + case 'l': + opt_line = EARGF(usage()); + break; + case 'n': + opt_name = EARGF(usage()); + break; + case 't': + case 'T': + opt_title = EARGF(usage()); + break; + case 'w': + opt_embed = EARGF(usage()); + break; + case 'v': + die("%s " VERSION " (c) 2010-2016 st engineers\n", argv0); + break; + default: + usage(); + } ARGEND; + +run: + if (argc > 0) { + /* eat all remaining arguments */ + opt_cmd = argv; + if (!opt_title && !opt_line) + opt_title = basename(xstrdup(argv[0])); + } + setlocale(LC_CTYPE, ""); + XSetLocaleModifiers(""); + tnew(MAX(cols, 1), MAX(rows, 1)); + xinit(); + selinit(); + run(); + + return 0; +} From 20f713548de451b67db3306cf8cf7b2f38fee05c Mon Sep 17 00:00:00 2001 From: "Roberto E. Vargas Caballero" Date: Wed, 25 Jan 2017 19:17:38 +0100 Subject: [PATCH 002/185] Change default keybindings CTRL+SHIFT is an impossible combination in the terminal world (0x20 | x & 0x1F), so it is perfect to be used for internals shortcuts of terminals, and being a double combination reduces the prossibility of having comflicts. --- config.def.h | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/config.def.h b/config.def.h index fd80923..877afab 100644 --- a/config.def.h +++ b/config.def.h @@ -162,6 +162,7 @@ MouseShortcut mshortcuts[] = { /* Internal keyboard shortcuts. */ #define MODKEY Mod1Mask +#define TERMMOD (ControlMask|ShiftMask) Shortcut shortcuts[] = { /* mask keysym function argument */ @@ -169,15 +170,14 @@ Shortcut shortcuts[] = { { ControlMask, XK_Print, toggleprinter, {.i = 0} }, { ShiftMask, XK_Print, printscreen, {.i = 0} }, { XK_ANY_MOD, XK_Print, printsel, {.i = 0} }, - { MODKEY|ShiftMask, XK_Prior, zoom, {.f = +1} }, - { MODKEY|ShiftMask, XK_Next, zoom, {.f = -1} }, - { MODKEY|ShiftMask, XK_Home, zoomreset, {.f = 0} }, - { ShiftMask, XK_Insert, selpaste, {.i = 0} }, - { MODKEY|ShiftMask, XK_Insert, clippaste, {.i = 0} }, - { MODKEY|ShiftMask, XK_C, clipcopy, {.i = 0} }, - { MODKEY|ShiftMask, XK_V, clippaste, {.i = 0} }, - { MODKEY, XK_Num_Lock, numlock, {.i = 0} }, - { MODKEY, XK_Control_L, iso14755, {.i = 0} }, + { TERMMOD, XK_Prior, zoom, {.f = +1} }, + { TERMMOD, XK_Next, zoom, {.f = -1} }, + { TERMMOD, XK_Home, zoomreset, {.f = 0} }, + { TERMMOD, XK_C, clipcopy, {.i = 0} }, + { TERMMOD, XK_V, clippaste, {.i = 0} }, + { TERMMOD, XK_Y, selpaste, {.i = 0} }, + { TERMMOD, XK_Num_Lock, numlock, {.i = 0} }, + { TERMMOD, XK_I, iso14755, {.i = 0} }, }; /* From e7ed326d2e914a57017c9f34459824614075519b Mon Sep 17 00:00:00 2001 From: "osandov@osandov.com" Date: Sat, 18 Mar 2017 11:55:04 +0100 Subject: [PATCH 003/185] Support xterm Ms feature to set clipboard This is used by, e.g., tmux. --- st.c | 57 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++ st.info | 1 + win.h | 1 + x.c | 1 - 4 files changed, 59 insertions(+), 1 deletion(-) diff --git a/st.c b/st.c index 1e4196e..d7bd32a 100644 --- a/st.c +++ b/st.c @@ -198,6 +198,8 @@ static char utf8encodebyte(Rune, size_t); static char *utf8strchr(char *s, Rune u); static size_t utf8validate(Rune *, size_t); +static char *base64dec(const char *); + static ssize_t xwrite(int, const char *, size_t); static void *xrealloc(void *, size_t); @@ -369,6 +371,48 @@ utf8validate(Rune *u, size_t i) return i; } +static const char base64_digits[] = { + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 62, 0, 0, 0, + 63, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 0, 0, 0, -1, 0, 0, 0, 0, 1, + 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, + 22, 23, 24, 25, 0, 0, 0, 0, 0, 0, 26, 27, 28, 29, 30, 31, 32, 33, 34, + 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 +}; + +char * +base64dec(const char *src) +{ + size_t in_len = strlen(src); + char *result, *dst; + + if (in_len % 4) + return NULL; + result = dst = xmalloc(in_len / 4 * 3 + 1); + while (*src) { + int a = base64_digits[(unsigned char) *src++]; + int b = base64_digits[(unsigned char) *src++]; + int c = base64_digits[(unsigned char) *src++]; + int d = base64_digits[(unsigned char) *src++]; + + *dst++ = (a << 2) | ((b & 0x30) >> 4); + if (c == -1) + break; + *dst++ = ((b & 0x0f) << 4) | ((c & 0x3c) >> 2); + if (d == -1) + break; + *dst++ = ((c & 0x03) << 6) | d; + } + *dst = '\0'; + return result; +} + void selinit(void) { @@ -1820,6 +1864,19 @@ strhandle(void) if (narg > 1) xsettitle(strescseq.args[1]); return; + case 52: + if (narg > 2) { + char *dec; + + dec = base64dec(strescseq.args[2]); + if (dec) { + xsetsel(dec, CurrentTime); + clipcopy(NULL); + } else { + fprintf(stderr, "erresc: invalid base64\n"); + } + } + return; case 4: /* color set */ if (narg < 3) break; diff --git a/st.info b/st.info index 13cc8eb..0b928af 100644 --- a/st.info +++ b/st.info @@ -189,6 +189,7 @@ st| simpleterm, Se, Ss, Tc, + Ms=\E]52;%p1%s;%p2%s\007, st-256color| simpleterm with 256 colors, use=st, diff --git a/win.h b/win.h index d684797..428111c 100644 --- a/win.h +++ b/win.h @@ -27,3 +27,4 @@ void xunloadfonts(void); void xresize(int, int); void xselpaste(void); unsigned long xwinid(void); +void xsetsel(char *, Time); diff --git a/x.c b/x.c index 6474a01..743b084 100644 --- a/x.c +++ b/x.c @@ -88,7 +88,6 @@ static void xclear(int, int, int, int); static void xdrawcursor(void); static int xgeommasktogravity(int); static int xloadfont(Font *, FcPattern *); -static void xsetsel(char *, Time); static void xunloadfont(Font *); static void expose(XEvent *); From f2bfd513b14a2aa27796670235557a550b4189db Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nils=20Reu=C3=9Fe?= Date: Wed, 29 Mar 2017 18:34:12 +0200 Subject: [PATCH 004/185] keep some glyph modes for the cursor st currently does not keep any mode for the cursor that was active in the underlying glyph (e.g. italic text), the mode is always ATTR_NULL [1]. At [2] you can find a screenshot that shows the implications. Other terminals (at least vte-based, such as XFCE-terminal) keep some modes for the cursor. I find the current behaviour very disruptive, so here is a patch that keeps a few (arbitrarily chosen) modes for the cursor. [1] http://git.suckless.org/st/tree/st.c#n3963 [2] http://i.imgur.com/R2yCEaC.png --- x.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/x.c b/x.c index 743b084..b7339e9 100644 --- a/x.c +++ b/x.c @@ -1266,6 +1266,7 @@ xdrawcursor(void) Glyph g = {' ', ATTR_NULL, defaultbg, defaultcs}, og; int ena_sel = sel.ob.x != -1 && sel.alt == IS_SET(MODE_ALTSCREEN); Color drawcol; + unsigned attr; LIMIT(oldx, 0, term.col-1); LIMIT(oldy, 0, term.row-1); @@ -1285,6 +1286,8 @@ xdrawcursor(void) xdrawglyph(og, oldx, oldy); g.u = term.line[term.c.y][term.c.x].u; + attr = ATTR_BOLD | ATTR_ITALIC | ATTR_UNDERLINE | ATTR_STRUCK; + g.mode |= term.line[term.c.y][term.c.x].mode & attr; /* * Select the right color for the right mode. From 149c0d3aedffe69b625ef95868daae200941d5f5 Mon Sep 17 00:00:00 2001 From: Alexander Krotov Date: Sat, 25 Mar 2017 23:02:42 +0300 Subject: [PATCH 005/185] Fix commented out code --- st.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/st.c b/st.c index d7bd32a..ae93ade 100644 --- a/st.c +++ b/st.c @@ -2537,7 +2537,7 @@ tresize(int col, int row) } /* allocate any new rows */ - for (/* i == minrow */; i < row; i++) { + for (/* i = minrow */; i < row; i++) { term.line[i] = xmalloc(col * sizeof(Glyph)); term.alt[i] = xmalloc(col * sizeof(Glyph)); } From 745c40f8b07ab898d1f9d4f564881b40599bc80d Mon Sep 17 00:00:00 2001 From: Quentin Rameau Date: Tue, 4 Apr 2017 17:20:55 +0200 Subject: [PATCH 006/185] Simplify how we keep ATTRs under cursor Thanks to tarug0 for the suggestion/patch. --- x.c | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/x.c b/x.c index b7339e9..fbfd350 100644 --- a/x.c +++ b/x.c @@ -1266,7 +1266,6 @@ xdrawcursor(void) Glyph g = {' ', ATTR_NULL, defaultbg, defaultcs}, og; int ena_sel = sel.ob.x != -1 && sel.alt == IS_SET(MODE_ALTSCREEN); Color drawcol; - unsigned attr; LIMIT(oldx, 0, term.col-1); LIMIT(oldy, 0, term.row-1); @@ -1286,8 +1285,8 @@ xdrawcursor(void) xdrawglyph(og, oldx, oldy); g.u = term.line[term.c.y][term.c.x].u; - attr = ATTR_BOLD | ATTR_ITALIC | ATTR_UNDERLINE | ATTR_STRUCK; - g.mode |= term.line[term.c.y][term.c.x].mode & attr; + g.mode |= term.line[term.c.y][term.c.x].mode & + (ATTR_BOLD | ATTR_ITALIC | ATTR_UNDERLINE | ATTR_STRUCK); /* * Select the right color for the right mode. From 5a10aca702bf7bc9094cb4abaea6c59558740d29 Mon Sep 17 00:00:00 2001 From: "greg.reagle@umbc.edu" Date: Mon, 10 Apr 2017 18:24:03 +0200 Subject: [PATCH 007/185] st.1: modify man page to accurately reflect default keybindings Attached. ===> 2/ (text/x-patch) [file] cp /mail/fs/mbox/298/2/body /usr/k0ga/0001-st.1-modify-man-page-to-accurately-reflect-default-k.patch From 265db94b1eca5850d484f86b7db4af8e57822cfe Mon Sep 17 00:00:00 2001 From: Greg Reagle Date: Sun, 9 Apr 2017 23:05:47 -0400 Subject: [PATCH] st.1: modify man page to accurately reflect default keybindings --- st.1 | 17 +++++++---------- 1 file changed, 7 insertions(+), 10 deletions(-) diff --git a/st.1 b/st.1 index c429dcf..bbca7e5 100644 --- a/st.1 +++ b/st.1 @@ -142,28 +142,25 @@ Print the full screen to the Print the selection to the .I iofile. .TP -.B Alt-Shift-Page Up +.B Ctrl-Shift-Page Up Increase font size. .TP -.B Alt-Shift-Page Down +.B Ctrl-Shift-Page Down Decrease font size. .TP -.B Alt-Shift-Home +.B Ctrl-Shift-Home Reset to default font size. .TP -.B Shift-Insert +.B Ctrl-Shift-y Paste from primary selection (middle mouse button). .TP -.B Alt-Shift-Insert -Paste from clipboard selection. -.TP -.B Alt-Shift-c +.B Ctrl-Shift-c Copy the selected text to the clipboard selection. .TP -.B Alt-Shift-v +.B Ctrl-Shift-v Paste from the clipboard selection. .TP -.B Alt-Ctrl +.B Ctrl-Shift-i Launch dmenu to enter a unicode codepoint and send the corresponding glyph to st. .SH CUSTOMIZATION From 6cb6d61525931c88971cce323f63e40451a6d365 Mon Sep 17 00:00:00 2001 From: Quentin Rameau Date: Sun, 28 May 2017 09:56:57 +0200 Subject: [PATCH 008/185] Add bold off SGR --- st.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/st.c b/st.c index ae93ade..8d4a9f2 100644 --- a/st.c +++ b/st.c @@ -1402,6 +1402,9 @@ tsetattr(int *attr, int l) case 9: term.c.attr.mode |= ATTR_STRUCK; break; + case 21: + term.c.attr.mode &= ~ATTR_BOLD; + break; case 22: term.c.attr.mode &= ~(ATTR_BOLD | ATTR_FAINT); break; From b331da550b290d54592b7ba11242c92f5a303a48 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marc=20Andr=C3=A9=20Tanner?= Date: Mon, 15 May 2017 12:45:41 +0200 Subject: [PATCH 009/185] Add color change terminfo capabilities --- st.info | 3 +++ 1 file changed, 3 insertions(+) diff --git a/st.info b/st.info index 0b928af..4d77eae 100644 --- a/st.info +++ b/st.info @@ -193,9 +193,12 @@ st| simpleterm, st-256color| simpleterm with 256 colors, use=st, + ccc, colors#256, + oc=\E]104\007, pairs#32767, # Nicked from xterm-256color + initc=\E]4;%p1%d;rgb\:%p2%{255}%*%{1000}%/%2.2X/%p3%{255}%*%{1000}%/%2.2X/%p4%{255}%*%{1000}%/%2.2X\E\\, setab=\E[%?%p1%{8}%<%t4%p1%d%e%p1%{16}%<%t10%p1%{8}%-%d%e48;5;%p1%d%;m, setaf=\E[%?%p1%{8}%<%t3%p1%d%e%p1%{16}%<%t9%p1%{8}%-%d%e38;5;%p1%d%;m, From fabd4602b3223666165c76c397644a081b9a97e5 Mon Sep 17 00:00:00 2001 From: Quentin Rameau Date: Wed, 12 Jul 2017 00:24:51 +0200 Subject: [PATCH 010/185] Do not obfuscate what make is doing. Change some styling too while we're at it. --- Makefile | 56 ++++++++++++++++++++++++------------------------------- config.mk | 16 ++++++++-------- 2 files changed, 32 insertions(+), 40 deletions(-) diff --git a/Makefile b/Makefile index d8595fe..f5b84a0 100644 --- a/Makefile +++ b/Makefile @@ -1,63 +1,55 @@ # st - simple terminal # See LICENSE file for copyright and license details. +.POSIX: include config.mk SRC = st.c x.c -OBJ = ${SRC:.c=.o} +OBJ = $(SRC:.c=.o) all: options st options: @echo st build options: - @echo "CFLAGS = ${CFLAGS}" - @echo "LDFLAGS = ${LDFLAGS}" - @echo "CC = ${CC}" + @echo "CFLAGS = $(CFLAGS)" + @echo "LDFLAGS = $(LDFLAGS)" + @echo "CC = $(CC)" config.h: cp config.def.h config.h .c.o: - @echo CC $< - @${CC} -c ${CFLAGS} $< + $(CC) $(CFLAGS) -c $< st.o: config.h st.h win.h x.o: arg.h st.h win.h -${OBJ}: config.h config.mk +$(OBJ): config.h config.mk -st: ${OBJ} - @echo CC -o $@ - @${CC} -o $@ ${OBJ} ${LDFLAGS} +st: $(OBJ) + $(CC) $(LDFLAGS) -o $@ $(OBJ) clean: - @echo cleaning - @rm -f st ${OBJ} st-${VERSION}.tar.gz + rm -f st $(OBJ) st-$(VERSION).tar.gz dist: clean - @echo creating dist tarball - @mkdir -p st-${VERSION} - @cp -R LICENSE Makefile README config.mk config.def.h st.info st.1 arg.h ${SRC} st-${VERSION} - @tar -cf st-${VERSION}.tar st-${VERSION} - @gzip st-${VERSION}.tar - @rm -rf st-${VERSION} + mkdir -p st-$(VERSION) + cp -R LICENSE Makefile README config.mk config.def.h st.info st.1 arg.h $(SRC) st-$(VERSION) + tar -cf - st-$(VERSION) | gzip > st-$(VERSION).tar.gz + rm -rf st-$(VERSION) -install: all - @echo installing executable file to ${DESTDIR}${PREFIX}/bin - @mkdir -p ${DESTDIR}${PREFIX}/bin - @cp -f st ${DESTDIR}${PREFIX}/bin - @chmod 755 ${DESTDIR}${PREFIX}/bin/st - @echo installing manual page to ${DESTDIR}${MANPREFIX}/man1 - @mkdir -p ${DESTDIR}${MANPREFIX}/man1 - @sed "s/VERSION/${VERSION}/g" < st.1 > ${DESTDIR}${MANPREFIX}/man1/st.1 - @chmod 644 ${DESTDIR}${MANPREFIX}/man1/st.1 +install: st + mkdir -p $(DESTDIR)$(PREFIX)/bin + cp -f st $(DESTDIR)$(PREFIX)/bin + chmod 755 $(DESTDIR)$(PREFIX)/bin/st + mkdir -p $(DESTDIR)$(MANPREFIX)/man1 + sed "s/VERSION/$(VERSION)/g" < st.1 > $(DESTDIR)$(MANPREFIX)/man1/st.1 + chmod 644 $(DESTDIR)$(MANPREFIX)/man1/st.1 + tic -sx st.info @echo Please see the README file regarding the terminfo entry of st. - @tic -sx st.info uninstall: - @echo removing executable file from ${DESTDIR}${PREFIX}/bin - @rm -f ${DESTDIR}${PREFIX}/bin/st - @echo removing manual page from ${DESTDIR}${MANPREFIX}/man1 - @rm -f ${DESTDIR}${MANPREFIX}/man1/st.1 + rm -f $(DESTDIR)$(PREFIX)/bin/st + rm -f $(DESTDIR)$(MANPREFIX)/man1/st.1 .PHONY: all options clean dist install uninstall diff --git a/config.mk b/config.mk index c84c5ee..527a64d 100644 --- a/config.mk +++ b/config.mk @@ -5,24 +5,24 @@ VERSION = 0.7 # paths PREFIX = /usr/local -MANPREFIX = ${PREFIX}/share/man +MANPREFIX = $(PREFIX)/share/man X11INC = /usr/X11R6/include X11LIB = /usr/X11R6/lib # includes and libs -INCS = -I. -I/usr/include -I${X11INC} \ +INCS = -I$(X11INC) \ `pkg-config --cflags fontconfig` \ `pkg-config --cflags freetype2` -LIBS = -L/usr/lib -lc -L${X11LIB} -lm -lrt -lX11 -lutil -lXft \ - `pkg-config --libs fontconfig` \ +LIBS = -L$(X11LIB) -lm -lrt -lX11 -lutil -lXft \ + `pkg-config --libs fontconfig` \ `pkg-config --libs freetype2` # flags -CPPFLAGS = -DVERSION=\"${VERSION}\" -D_XOPEN_SOURCE=600 -CFLAGS += -g -std=c99 -pedantic -Wall -Wvariadic-macros -Os ${INCS} ${CPPFLAGS} -LDFLAGS += -g ${LIBS} +CPPFLAGS = -DVERSION=\"$(VERSION)\" -D_XOPEN_SOURCE=600 +CFLAGS = -g -std=c99 -pedantic -Wall -Wvariadic-macros -Os $(INCS) $(CPPFLAGS) +LDFLAGS = -g $(LIBS) # compiler and linker -# CC = cc +# CC = c99 From d4928edba0fe2cc63b3bc13fd6dad0bcb875174e Mon Sep 17 00:00:00 2001 From: Quentin Rameau Date: Wed, 12 Jul 2017 00:26:35 +0200 Subject: [PATCH 011/185] Let the user specify C and LD FLAGS --- Makefile | 8 ++++---- config.mk | 4 ++-- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/Makefile b/Makefile index f5b84a0..128ee9d 100644 --- a/Makefile +++ b/Makefile @@ -11,15 +11,15 @@ all: options st options: @echo st build options: - @echo "CFLAGS = $(CFLAGS)" - @echo "LDFLAGS = $(LDFLAGS)" + @echo "CFLAGS = $(STCFLAGS)" + @echo "LDFLAGS = $(STLDFLAGS)" @echo "CC = $(CC)" config.h: cp config.def.h config.h .c.o: - $(CC) $(CFLAGS) -c $< + $(CC) $(STCFLAGS) -c $< st.o: config.h st.h win.h x.o: arg.h st.h win.h @@ -27,7 +27,7 @@ x.o: arg.h st.h win.h $(OBJ): config.h config.mk st: $(OBJ) - $(CC) $(LDFLAGS) -o $@ $(OBJ) + $(CC) $(STLDFLAGS) -o $@ $(OBJ) clean: rm -f st $(OBJ) st-$(VERSION).tar.gz diff --git a/config.mk b/config.mk index 527a64d..0aceec4 100644 --- a/config.mk +++ b/config.mk @@ -20,8 +20,8 @@ LIBS = -L$(X11LIB) -lm -lrt -lX11 -lutil -lXft \ # flags CPPFLAGS = -DVERSION=\"$(VERSION)\" -D_XOPEN_SOURCE=600 -CFLAGS = -g -std=c99 -pedantic -Wall -Wvariadic-macros -Os $(INCS) $(CPPFLAGS) -LDFLAGS = -g $(LIBS) +STCFLAGS = $(INCS) $(CPPFLAGS) $(CFLAGS) +STLDFLAGS = $(LIBS) $(LDFLAGS) # compiler and linker # CC = c99 From c0882f2ed1d7a2dd0fa2efa52157e6fc6fde3652 Mon Sep 17 00:00:00 2001 From: Quentin Rameau Date: Tue, 11 Jul 2017 23:03:27 +0200 Subject: [PATCH 012/185] Add dim/smxx/rmxx to terminfo, remove duplicate kich1 --- st.info | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/st.info b/st.info index 4d77eae..52fc617 100644 --- a/st.info +++ b/st.info @@ -25,6 +25,7 @@ st| simpleterm, cuu=\E[%p1%dA, dch=\E[%p1%dP, dch1=\E[P, + dim=\E[2m, dl=\E[%p1%dM, dl1=\E[M, ech=\E[%p1%dX, @@ -142,7 +143,6 @@ st| simpleterm, khome=\E[1~, kil1=\E[2;5~, krmir=\E[2;2~, - kich1=\E[2~, knp=\E[6~, kmous=\E[M, kpp=\E[5~, @@ -185,7 +185,10 @@ st| simpleterm, tsl=\E]0;, xenl, vpa=\E[%i%p1%dd, -# Tmux unofficial extensions, see TERMINFO EXTENSIONS in tmux(1) +# XTerm extensions + rmxx=\E[29m, + smxx=\E[9m, +# tmux extensions, see TERMINFO EXTENSIONS in tmux(1) Se, Ss, Tc, From 8dacdfbab1c476063a31bc3b8d7222ac2b70ff66 Mon Sep 17 00:00:00 2001 From: Quentin Rameau Date: Tue, 11 Jul 2017 23:16:25 +0200 Subject: [PATCH 013/185] Revert "Add bold off SGR" This reverts commit 6cb6d61525931c88971cce323f63e40451a6d365. This wasn't a useful thing after all. --- st.c | 3 --- 1 file changed, 3 deletions(-) diff --git a/st.c b/st.c index 8d4a9f2..ae93ade 100644 --- a/st.c +++ b/st.c @@ -1402,9 +1402,6 @@ tsetattr(int *attr, int l) case 9: term.c.attr.mode |= ATTR_STRUCK; break; - case 21: - term.c.attr.mode &= ~ATTR_BOLD; - break; case 22: term.c.attr.mode &= ~(ATTR_BOLD | ATTR_FAINT); break; From 7f990328e4fec8dfaaad311cb8af2304b58c872e Mon Sep 17 00:00:00 2001 From: Anselm R Garbe Date: Sun, 23 Jul 2017 11:17:26 +0200 Subject: [PATCH 014/185] fixed STLDFLAG order in broken st Makefile --- Makefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Makefile b/Makefile index 128ee9d..c678ead 100644 --- a/Makefile +++ b/Makefile @@ -27,7 +27,7 @@ x.o: arg.h st.h win.h $(OBJ): config.h config.mk st: $(OBJ) - $(CC) $(STLDFLAGS) -o $@ $(OBJ) + $(CC) -o $@ $(OBJ) $(STLDFLAGS) clean: rm -f st $(OBJ) st-$(VERSION).tar.gz From 77c51c5a6b16387f1792e23acbcf2080f790aa25 Mon Sep 17 00:00:00 2001 From: Anselm R Garbe Date: Fri, 1 Sep 2017 09:48:24 +0200 Subject: [PATCH 015/185] make clipboard patch obsolete --- x.c | 1 + 1 file changed, 1 insertion(+) diff --git a/x.c b/x.c index fbfd350..ab9593e 100644 --- a/x.c +++ b/x.c @@ -507,6 +507,7 @@ xsetsel(char *str, Time t) XSetSelectionOwner(xw.dpy, XA_PRIMARY, xw.win, t); if (XGetSelectionOwner(xw.dpy, XA_PRIMARY) != xw.win) selclear_(NULL); + xclipcopy(); } void From 9c61f29bb7da237907e42875ebe7d0084e8ab1ac Mon Sep 17 00:00:00 2001 From: Hiltjo Posthuma Date: Sat, 2 Sep 2017 13:52:33 +0200 Subject: [PATCH 016/185] Revert "make clipboard patch obsolete" This reverts commit 77c51c5a6b16387f1792e23acbcf2080f790aa25. Having multiple clipboards are useful, for example for plumber scripts. I've discussed this on IRC and it is useful to have. --- x.c | 1 - 1 file changed, 1 deletion(-) diff --git a/x.c b/x.c index ab9593e..fbfd350 100644 --- a/x.c +++ b/x.c @@ -507,7 +507,6 @@ xsetsel(char *str, Time t) XSetSelectionOwner(xw.dpy, XA_PRIMARY, xw.win, t); if (XGetSelectionOwner(xw.dpy, XA_PRIMARY) != xw.win) selclear_(NULL); - xclipcopy(); } void From 274d46ace00003d1df718b974d17642cbce167d5 Mon Sep 17 00:00:00 2001 From: Hiltjo Posthuma Date: Wed, 13 Sep 2017 22:40:36 +0200 Subject: [PATCH 017/185] Revert "fixed STLDFLAG order in broken st Makefile" This reverts commit 7f990328e4fec8dfaaad311cb8af2304b58c872e. this was wrong as pointed out by k0ga: "STLDFLAGS is about flags to the linker, for example -L not about -l for that reason it must go before the object list". --- Makefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Makefile b/Makefile index c678ead..128ee9d 100644 --- a/Makefile +++ b/Makefile @@ -27,7 +27,7 @@ x.o: arg.h st.h win.h $(OBJ): config.h config.mk st: $(OBJ) - $(CC) -o $@ $(OBJ) $(STLDFLAGS) + $(CC) $(STLDFLAGS) -o $@ $(OBJ) clean: rm -f st $(OBJ) st-$(VERSION).tar.gz From ee5cc8e903574bf629e5159334ae6b0fad6af402 Mon Sep 17 00:00:00 2001 From: "Suraj N. Kurapati" Date: Thu, 17 Aug 2017 23:00:10 -0700 Subject: [PATCH 018/185] base64dec: skip non-printable characters like \r\n Non-printable characters, such as line breaks, in a base64 encoded string violate the "string length must be a multiple of four" rule. This patch pads the result buffer by one extra unit of four bytes, and skips over non-printable characters found in the input string. --- st.c | 17 ++++++++++++----- 1 file changed, 12 insertions(+), 5 deletions(-) diff --git a/st.c b/st.c index ae93ade..7c7ddff 100644 --- a/st.c +++ b/st.c @@ -386,6 +386,13 @@ static const char base64_digits[] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }; +char +base64dec_getc(const char **src) +{ + while (**src && !isprint(**src)) (*src)++; + return *((*src)++); +} + char * base64dec(const char *src) { @@ -393,13 +400,13 @@ base64dec(const char *src) char *result, *dst; if (in_len % 4) - return NULL; + in_len += 4 - (in_len % 4); result = dst = xmalloc(in_len / 4 * 3 + 1); while (*src) { - int a = base64_digits[(unsigned char) *src++]; - int b = base64_digits[(unsigned char) *src++]; - int c = base64_digits[(unsigned char) *src++]; - int d = base64_digits[(unsigned char) *src++]; + int a = base64_digits[(unsigned char) base64dec_getc(&src)]; + int b = base64_digits[(unsigned char) base64dec_getc(&src)]; + int c = base64_digits[(unsigned char) base64dec_getc(&src)]; + int d = base64_digits[(unsigned char) base64dec_getc(&src)]; *dst++ = (a << 2) | ((b & 0x30) >> 4); if (c == -1) From b2ac91775302fa4b8ce462a6e7bcfffa93923471 Mon Sep 17 00:00:00 2001 From: Hiltjo Posthuma Date: Fri, 15 Sep 2017 11:16:37 +0200 Subject: [PATCH 019/185] Revert "Revert "fixed STLDFLAG order in broken st Makefile"" This reverts commit 274d46ace00003d1df718b974d17642cbce167d5. Sorry, the original commit was correct after all. It allows has the correct link order and supports static-linking also. Just a reminder: it is important to give a (brief) rationale of the patch intentions. --- Makefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Makefile b/Makefile index 128ee9d..c678ead 100644 --- a/Makefile +++ b/Makefile @@ -27,7 +27,7 @@ x.o: arg.h st.h win.h $(OBJ): config.h config.mk st: $(OBJ) - $(CC) $(STLDFLAGS) -o $@ $(OBJ) + $(CC) -o $@ $(OBJ) $(STLDFLAGS) clean: rm -f st $(OBJ) st-$(VERSION).tar.gz From b1338e91ed632adbcd08388de37e46cf25326e01 Mon Sep 17 00:00:00 2001 From: Gary Allen Vollink Date: Thu, 14 Sep 2017 15:30:02 -0400 Subject: [PATCH 020/185] Add an error for XftFontOpenPattern failure. --- x.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/x.c b/x.c index fbfd350..191e5dc 100644 --- a/x.c +++ b/x.c @@ -1092,6 +1092,9 @@ xmakeglyphfontspecs(XftGlyphFontSpec *specs, const Glyph *glyphs, int len, int x frc[frclen].font = XftFontOpenPattern(xw.dpy, fontpattern); + if (!frc[frclen].font) + die("XftFontOpenPattern failed seeking fallback font: %s\n", + strerror(errno)); frc[frclen].flags = frcflags; frc[frclen].unicodep = rune; From 0ac685fc015362e749bf82cfaa3cfe2dd9b305f0 Mon Sep 17 00:00:00 2001 From: Paride Legovini Date: Tue, 10 Oct 2017 17:49:38 +0200 Subject: [PATCH 021/185] Fix manpage typo Signed-off-by: Paride Legovini --- st.1 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/st.1 b/st.1 index bbca7e5..81bceff 100644 --- a/st.1 +++ b/st.1 @@ -120,7 +120,7 @@ st executes instead of the shell. If this is used it .B must be the last option on the command line, as in xterm / rxvt. -This option is only intended for compability, +This option is only intended for compatibility, and all the remaining arguments are used as a command even without it. .SH SHORTCUTS From c1d23afa9c44cc29818c538126790ae90a64a3c5 Mon Sep 17 00:00:00 2001 From: George Ornbo Date: Wed, 20 Dec 2017 09:05:32 +0000 Subject: [PATCH 022/185] Fix FAQ typo --- FAQ | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/FAQ b/FAQ index 4defff9..921c493 100644 --- a/FAQ +++ b/FAQ @@ -6,7 +6,7 @@ Use the excellent tool of [utmp](http://git.suckless.org/utmp/) for this task. It means that st doesn’t have any terminfo entry on your system. Chances are you did not `make install`. If you just want to test it without installing it, -you can manualy run `tic -sx st.info`. +you can manually run `tic -sx st.info`. ## Nothing works, and nothing is said about an unknown terminal! From e829e13bb1a830e0cdce749ea0865cd93af1846c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Benno=20F=C3=BCnfst=C3=BCck?= Date: Tue, 26 Dec 2017 16:38:27 +0100 Subject: [PATCH 023/185] Apply ATTR_REVERSE after ATTR_FAINT An example where the new behaviour makes more sense: Suppose some text is formatted with ATTR_FAINT for red for the foreground, so it is rendered in a dark red. In that case, when selected with the mouse, the intended behaviour is that foreground and background color are swapped: so the selection should be rendered in dark red and the text in the default background color. Before this patch, what happened was that the selection would be in normal red and the text in the darkened background color, making it almost unreadable. For an example application that uses the FAINT attribute, try dmesg from util-linux with color support, it uses FAINT for segfault messages. --- x.c | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/x.c b/x.c index 191e5dc..474d73b 100644 --- a/x.c +++ b/x.c @@ -1189,12 +1189,6 @@ xdrawglyphfontspecs(const XftGlyphFontSpec *specs, Glyph base, int len, int x, i } } - if (base.mode & ATTR_REVERSE) { - temp = fg; - fg = bg; - bg = temp; - } - if ((base.mode & ATTR_BOLD_FAINT) == ATTR_FAINT) { colfg.red = fg->color.red / 2; colfg.green = fg->color.green / 2; @@ -1203,6 +1197,13 @@ xdrawglyphfontspecs(const XftGlyphFontSpec *specs, Glyph base, int len, int x, i fg = &revfg; } + + if (base.mode & ATTR_REVERSE) { + temp = fg; + fg = bg; + bg = temp; + } + if (base.mode & ATTR_BLINK && term.mode & MODE_BLINK) fg = bg; From 1f24bde82b19912c080fbb4a0b153a248cd6c6ea Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Benno=20F=C3=BCnfst=C3=BCck?= Date: Tue, 26 Dec 2017 16:23:24 +0100 Subject: [PATCH 024/185] Fix color with FAINT attribute The alpha value needs to be initialized as well. --- x.c | 1 + 1 file changed, 1 insertion(+) diff --git a/x.c b/x.c index 474d73b..c484dfc 100644 --- a/x.c +++ b/x.c @@ -1193,6 +1193,7 @@ xdrawglyphfontspecs(const XftGlyphFontSpec *specs, Glyph base, int len, int x, i colfg.red = fg->color.red / 2; colfg.green = fg->color.green / 2; colfg.blue = fg->color.blue / 2; + colfg.alpha = fg->color.alpha; XftColorAllocValue(xw.dpy, xw.vis, xw.cmap, &colfg, &revfg); fg = &revfg; } From 3e44ee5569a81ba6f06e1ecd19bf0ceb1e97f18d Mon Sep 17 00:00:00 2001 From: "Devin J. Pohly" Date: Tue, 10 Oct 2017 10:30:23 -0500 Subject: [PATCH 025/185] Call xsetenv() in main process instead of child This makes xsetenv internal to x.c, and allows iso14755's external command to use $WINDOWID instead of having to snprintf it again. (The same benefit will apply to the externalpipe patch.) The xwinid function is no longer needed. Signed-off-by: Devin J. Pohly --- st.c | 8 ++------ win.h | 2 -- x.c | 8 ++------ 3 files changed, 4 insertions(+), 14 deletions(-) diff --git a/st.c b/st.c index 7c7ddff..668b312 100644 --- a/st.c +++ b/st.c @@ -60,7 +60,7 @@ char *argv0; #define ISDELIM(u) (utf8strchr(worddelimiters, u) != NULL) /* constants */ -#define ISO14755CMD "dmenu -w %lu -p codepoint: pw_dir, 1); setenv("TERM", termname, 1); - xsetenv(); signal(SIGCHLD, SIG_DFL); signal(SIGHUP, SIG_DFL); @@ -1993,14 +1992,11 @@ tprinter(char *s, size_t len) void iso14755(const Arg *arg) { - unsigned long id = xwinid(); - char cmd[sizeof(ISO14755CMD) + NUMMAXLEN(id)]; FILE *p; char *us, *e, codepoint[9], uc[UTF_SIZ]; unsigned long utf32; - snprintf(cmd, sizeof(cmd), ISO14755CMD, id); - if (!(p = popen(cmd, "r"))) + if (!(p = popen(ISO14755CMD, "r"))) return; us = fgets(codepoint, sizeof(codepoint), p); diff --git a/win.h b/win.h index 428111c..423c114 100644 --- a/win.h +++ b/win.h @@ -19,12 +19,10 @@ void xinit(void); void xloadcols(void); int xsetcolorname(int, const char *); void xloadfonts(char *, double); -void xsetenv(void); void xsettitle(char *); void xsetpointermotion(int); void xseturgency(int); void xunloadfonts(void); void xresize(int, int); void xselpaste(void); -unsigned long xwinid(void); void xsetsel(char *, Time); diff --git a/x.c b/x.c index c484dfc..df2a88c 100644 --- a/x.c +++ b/x.c @@ -89,6 +89,7 @@ static void xdrawcursor(void); static int xgeommasktogravity(int); static int xloadfont(Font *, FcPattern *); static void xunloadfont(Font *); +static void xsetenv(void); static void expose(XEvent *); static void visibility(XEvent *); @@ -1487,12 +1488,6 @@ xbell(int vol) XkbBell(xw.dpy, xw.win, vol, (Atom)NULL); } -unsigned long -xwinid(void) -{ - return xw.win; -} - void focus(XEvent *ev) { @@ -1765,6 +1760,7 @@ run: XSetLocaleModifiers(""); tnew(MAX(cols, 1), MAX(rows, 1)); xinit(); + xsetenv(); selinit(); run(); From 3518dba2a5fb57f601b74528ddeb67f173e4024b Mon Sep 17 00:00:00 2001 From: "Devin J. Pohly" Date: Tue, 10 Oct 2017 11:11:27 -0500 Subject: [PATCH 026/185] Move usage() to be with run() in x.c run/usage/xinit are now all internal to x.c Signed-off-by: Devin J. Pohly --- st.c | 15 --------------- st.h | 2 -- win.h | 2 -- x.c | 18 ++++++++++++++++++ 4 files changed, 18 insertions(+), 19 deletions(-) diff --git a/st.c b/st.c index 668b312..6338510 100644 --- a/st.c +++ b/st.c @@ -28,8 +28,6 @@ #include #include -char *argv0; - #define Glyph Glyph_ #define Font Font_ @@ -2687,16 +2685,3 @@ cresize(int width, int height) tresize(col, row); xresize(col, row); } - -void -usage(void) -{ - die("usage: %s [-aiv] [-c class] [-f font] [-g geometry]" - " [-n name] [-o file]\n" - " [-T title] [-t title] [-w windowid]" - " [[-e] command [args ...]]\n" - " %s [-aiv] [-c class] [-f font] [-g geometry]" - " [-n name] [-o file]\n" - " [-T title] [-t title] [-w windowid] -l line" - " [stty_args ...]\n", argv0, argv0); -} diff --git a/st.h b/st.h index 44d4938..28a751d 100644 --- a/st.h +++ b/st.h @@ -214,8 +214,6 @@ size_t utf8encode(Rune, char *); void *xmalloc(size_t); char *xstrdup(char *); -void usage(void); - /* Globals */ extern TermWindow win; extern Term term; diff --git a/win.h b/win.h index 423c114..60cecb1 100644 --- a/win.h +++ b/win.h @@ -9,13 +9,11 @@ typedef XftGlyphFontSpec GlyphFontSpec; void draw(void); void drawregion(int, int, int, int); -void run(void); void xbell(int); void xclipcopy(void); void xclippaste(void); void xhints(void); -void xinit(void); void xloadcols(void); int xsetcolorname(int, const char *); void xloadfonts(char *, double); diff --git a/x.c b/x.c index df2a88c..f660ca3 100644 --- a/x.c +++ b/x.c @@ -15,6 +15,7 @@ #include #include +static char *argv0; #include "arg.h" #define Glyph Glyph_ @@ -87,6 +88,7 @@ static void xdrawglyph(Glyph, int, int); static void xclear(int, int, int, int); static void xdrawcursor(void); static int xgeommasktogravity(int); +static void xinit(void); static int xloadfont(Font *, FcPattern *); static void xunloadfont(Font *); static void xsetenv(void); @@ -110,6 +112,9 @@ static void selcopy(Time); static void getbuttoninfo(XEvent *); static void mousereport(XEvent *); +static void run(void); +static void usage(void); + static void (*handler[LASTEvent])(XEvent *) = { [KeyPress] = kpress, [ClientMessage] = cmessage, @@ -1698,6 +1703,19 @@ run(void) } } +void +usage(void) +{ + die("usage: %s [-aiv] [-c class] [-f font] [-g geometry]" + " [-n name] [-o file]\n" + " [-T title] [-t title] [-w windowid]" + " [[-e] command [args ...]]\n" + " %s [-aiv] [-c class] [-f font] [-g geometry]" + " [-n name] [-o file]\n" + " [-T title] [-t title] [-w windowid] -l line" + " [stty_args ...]\n", argv0, argv0); +} + int main(int argc, char *argv[]) { From d5275012b45149a2a6e94679609aacca478221ad Mon Sep 17 00:00:00 2001 From: "Devin J. Pohly" Date: Tue, 10 Oct 2017 11:30:36 -0500 Subject: [PATCH 027/185] Move zoom functions into x.c This makes x(un)loadfonts internal to x.c. Needed to reorder includes and move a typedef to keep the compiler happy. Signed-off-by: Devin J. Pohly --- st.c | 36 +----------------------------------- st.h | 1 + win.h | 7 +++---- x.c | 35 ++++++++++++++++++++++++++++++++++- 4 files changed, 39 insertions(+), 40 deletions(-) diff --git a/st.c b/st.c index 6338510..f1f7bc1 100644 --- a/st.c +++ b/st.c @@ -31,8 +31,8 @@ #define Glyph Glyph_ #define Font Font_ -#include "win.h" #include "st.h" +#include "win.h" #if defined(__linux) #include @@ -128,9 +128,6 @@ static void clipcopy(const Arg *); static void clippaste(const Arg *); static void numlock(const Arg *); static void selpaste(const Arg *); -static void zoom(const Arg *); -static void zoomabs(const Arg *); -static void zoomreset(const Arg *); static void printsel(const Arg *); static void printscreen(const Arg *) ; static void iso14755(const Arg *); @@ -2573,37 +2570,6 @@ tresize(int col, int row) term.c = c; } -void -zoom(const Arg *arg) -{ - Arg larg; - - larg.f = usedfontsize + arg->f; - zoomabs(&larg); -} - -void -zoomabs(const Arg *arg) -{ - xunloadfonts(); - xloadfonts(usedfont, arg->f); - cresize(0, 0); - ttyresize(); - redraw(); - xhints(); -} - -void -zoomreset(const Arg *arg) -{ - Arg larg; - - if (defaultfontsize > 0) { - larg.f = defaultfontsize; - zoomabs(&larg); - } -} - void resettitle(void) { diff --git a/st.h b/st.h index 28a751d..9ece72f 100644 --- a/st.h +++ b/st.h @@ -100,6 +100,7 @@ typedef struct { } Glyph; typedef Glyph *Line; +typedef XftGlyphFontSpec GlyphFontSpec; typedef struct { Glyph attr; /* current char attributes */ diff --git a/win.h b/win.h index 60cecb1..ea45e60 100644 --- a/win.h +++ b/win.h @@ -5,8 +5,6 @@ #define XK_NO_MOD 0 #define XK_SWITCH_MOD (1<<13) -typedef XftGlyphFontSpec GlyphFontSpec; - void draw(void); void drawregion(int, int, int, int); @@ -16,11 +14,12 @@ void xclippaste(void); void xhints(void); void xloadcols(void); int xsetcolorname(int, const char *); -void xloadfonts(char *, double); void xsettitle(char *); void xsetpointermotion(int); void xseturgency(int); -void xunloadfonts(void); void xresize(int, int); void xselpaste(void); void xsetsel(char *, Time); +void zoom(const Arg *); +void zoomabs(const Arg *); +void zoomreset(const Arg *); diff --git a/x.c b/x.c index f660ca3..426ca28 100644 --- a/x.c +++ b/x.c @@ -21,8 +21,8 @@ static char *argv0; #define Glyph Glyph_ #define Font Font_ -#include "win.h" #include "st.h" +#include "win.h" /* XEMBED messages */ #define XEMBED_FOCUS_IN 4 @@ -90,7 +90,9 @@ static void xdrawcursor(void); static int xgeommasktogravity(int); static void xinit(void); static int xloadfont(Font *, FcPattern *); +static void xloadfonts(char *, double); static void xunloadfont(Font *); +static void xunloadfonts(void); static void xsetenv(void); static void expose(XEvent *); @@ -164,6 +166,37 @@ typedef struct { static Fontcache frc[16]; static int frclen = 0; +void +zoom(const Arg *arg) +{ + Arg larg; + + larg.f = usedfontsize + arg->f; + zoomabs(&larg); +} + +void +zoomabs(const Arg *arg) +{ + xunloadfonts(); + xloadfonts(usedfont, arg->f); + cresize(0, 0); + ttyresize(); + redraw(); + xhints(); +} + +void +zoomreset(const Arg *arg) +{ + Arg larg; + + if (defaultfontsize > 0) { + larg.f = defaultfontsize; + zoomabs(&larg); + } +} + void getbuttoninfo(XEvent *e) { From 626b0ae40c71b6c1e02ece79bf033432647381a6 Mon Sep 17 00:00:00 2001 From: "Devin J. Pohly" Date: Tue, 10 Oct 2017 12:01:18 -0500 Subject: [PATCH 028/185] Move window urgency handling entirely into x.c This allows us to make xseturgency internal. Signed-off-by: Devin J. Pohly --- config.def.h | 2 +- st.c | 5 +---- st.h | 1 + win.h | 3 +-- x.c | 8 ++++++-- 5 files changed, 10 insertions(+), 9 deletions(-) diff --git a/config.def.h b/config.def.h index 877afab..dd94be2 100644 --- a/config.def.h +++ b/config.def.h @@ -60,7 +60,7 @@ unsigned int cursorthickness = 2; * bell volume. It must be a value between -100 and 100. Use 0 for disabling * it */ -static int bellvolume = 0; +int bellvolume = 0; /* default TERM value */ char termname[] = "st-256color"; diff --git a/st.c b/st.c index f1f7bc1..df43fac 100644 --- a/st.c +++ b/st.c @@ -2176,10 +2176,7 @@ tcontrolcode(uchar ascii) /* backwards compatibility to xterm */ strhandle(); } else { - if (!(win.state & WIN_FOCUSED)) - xseturgency(1); - if (bellvolume) - xbell(bellvolume); + xbell(); } break; case '\033': /* ESC */ diff --git a/st.h b/st.h index 9ece72f..ad94351 100644 --- a/st.h +++ b/st.h @@ -246,6 +246,7 @@ extern int allowaltscreen; extern unsigned int xfps; extern unsigned int actionfps; extern unsigned int cursorthickness; +extern int bellvolume; extern unsigned int blinktimeout; extern char termname[]; extern const char *colorname[]; diff --git a/win.h b/win.h index ea45e60..dee0b7f 100644 --- a/win.h +++ b/win.h @@ -8,7 +8,7 @@ void draw(void); void drawregion(int, int, int, int); -void xbell(int); +void xbell(void); void xclipcopy(void); void xclippaste(void); void xhints(void); @@ -16,7 +16,6 @@ void xloadcols(void); int xsetcolorname(int, const char *); void xsettitle(char *); void xsetpointermotion(int); -void xseturgency(int); void xresize(int, int); void xselpaste(void); void xsetsel(char *, Time); diff --git a/x.c b/x.c index 426ca28..5b3c97b 100644 --- a/x.c +++ b/x.c @@ -94,6 +94,7 @@ static void xloadfonts(char *, double); static void xunloadfont(Font *); static void xunloadfonts(void); static void xsetenv(void); +static void xseturgency(int); static void expose(XEvent *); static void visibility(XEvent *); @@ -1521,9 +1522,12 @@ xseturgency(int add) } void -xbell(int vol) +xbell(void) { - XkbBell(xw.dpy, xw.win, vol, (Atom)NULL); + if (!(win.state & WIN_FOCUSED)) + xseturgency(1); + if (bellvolume) + XkbBell(xw.dpy, xw.win, bellvolume, (Atom)NULL); } void From a09138afa57adb4b76dba8ca72dc7ee2642a5c8d Mon Sep 17 00:00:00 2001 From: "Devin J. Pohly" Date: Tue, 10 Oct 2017 12:17:25 -0500 Subject: [PATCH 029/185] Move font/fontspec variables into x.c and XWindow Signed-off-by: Devin J. Pohly --- st.c | 11 ----------- st.h | 8 ++------ x.c | 18 +++++++++++++----- 3 files changed, 15 insertions(+), 22 deletions(-) diff --git a/st.c b/st.c index df43fac..540b487 100644 --- a/st.c +++ b/st.c @@ -28,9 +28,6 @@ #include #include -#define Glyph Glyph_ -#define Font Font_ - #include "st.h" #include "win.h" @@ -196,7 +193,6 @@ static size_t utf8validate(Rune *, size_t); static char *base64dec(const char *); static ssize_t xwrite(int, const char *, size_t); -static void *xrealloc(void *, size_t); /* Globals */ TermWindow win; @@ -218,10 +214,6 @@ static CSIEscape csiescseq; static STREscape strescseq; static int iofd = 1; -char *usedfont = NULL; -double usedfontsize = 0; -double defaultfontsize = 0; - static uchar utfbyte[UTF_SIZ + 1] = {0x80, 0, 0xC0, 0xE0, 0xF0}; static uchar utfmask[UTF_SIZ + 1] = {0xC0, 0x80, 0xE0, 0xF0, 0xF8}; static Rune utfmin[UTF_SIZ + 1] = { 0, 0, 0x80, 0x800, 0x10000}; @@ -2516,9 +2508,6 @@ tresize(int col, int row) free(term.alt[i]); } - /* resize to new width */ - term.specbuf = xrealloc(term.specbuf, col * sizeof(GlyphFontSpec)); - /* resize to new height */ term.line = xrealloc(term.line, row * sizeof(Line)); term.alt = xrealloc(term.alt, row * sizeof(Line)); diff --git a/st.h b/st.h index ad94351..2199c13 100644 --- a/st.h +++ b/st.h @@ -92,6 +92,7 @@ typedef unsigned short ushort; typedef uint_least32_t Rune; +#define Glyph Glyph_ typedef struct { Rune u; /* character code */ ushort mode; /* attribute flags */ @@ -100,7 +101,6 @@ typedef struct { } Glyph; typedef Glyph *Line; -typedef XftGlyphFontSpec GlyphFontSpec; typedef struct { Glyph attr; /* current char attributes */ @@ -116,7 +116,6 @@ typedef struct { Line *line; /* screen */ Line *alt; /* alternate screen */ int *dirty; /* dirtyness of lines */ - GlyphFontSpec *specbuf; /* font spec buffer used for rendering */ TCursor c; /* cursor */ int top; /* top scroll limit */ int bot; /* bottom scroll limit */ @@ -213,6 +212,7 @@ size_t utf8decode(char *, Rune *, size_t); size_t utf8encode(Rune, char *); void *xmalloc(size_t); +void *xrealloc(void *, size_t); char *xstrdup(char *); /* Globals */ @@ -231,10 +231,6 @@ extern char *opt_name; extern char *opt_title; extern int oldbutton; -extern char *usedfont; -extern double usedfontsize; -extern double defaultfontsize; - /* config.h globals */ extern char font[]; extern int borderpx; diff --git a/x.c b/x.c index 5b3c97b..186e408 100644 --- a/x.c +++ b/x.c @@ -17,10 +17,6 @@ static char *argv0; #include "arg.h" - -#define Glyph Glyph_ -#define Font Font_ - #include "st.h" #include "win.h" @@ -35,6 +31,7 @@ static char *argv0; typedef XftDraw *Draw; typedef XftColor Color; +typedef XftGlyphFontSpec GlyphFontSpec; /* Purely graphic info */ typedef struct { @@ -42,6 +39,7 @@ typedef struct { Colormap cmap; Window win; Drawable buf; + GlyphFontSpec *specbuf; /* font spec buffer used for rendering */ Atom xembed, wmdeletewin, netwmname, netwmpid; XIM xim; XIC xic; @@ -59,6 +57,7 @@ typedef struct { } XSelection; /* Font structure */ +#define Font Font_ typedef struct { int height; int width; @@ -166,6 +165,9 @@ typedef struct { /* Fontcache is an array now. A new font will be appended to the array. */ static Fontcache frc[16]; static int frclen = 0; +static char *usedfont = NULL; +static double usedfontsize = 0; +static double defaultfontsize = 0; void zoom(const Arg *arg) @@ -605,6 +607,9 @@ xresize(int col, int row) DefaultDepth(xw.dpy, xw.scr)); XftDrawChange(xw.draw, xw.buf); xclear(0, 0, win.w, win.h); + + /* resize to new width */ + xw.specbuf = xrealloc(xw.specbuf, col * sizeof(GlyphFontSpec)); } ushort @@ -965,6 +970,9 @@ xinit(void) XSetForeground(xw.dpy, dc.gc, dc.col[defaultbg].pixel); XFillRectangle(xw.dpy, xw.buf, dc.gc, 0, 0, win.w, win.h); + /* font spec buffer */ + xw.specbuf = xmalloc(term.col * sizeof(GlyphFontSpec)); + /* Xft rendering context */ xw.draw = XftDrawCreate(xw.dpy, xw.buf, xw.vis, xw.cmap); @@ -1456,7 +1464,7 @@ drawregion(int x1, int y1, int x2, int y2) term.dirty[y] = 0; - specs = term.specbuf; + specs = xw.specbuf; numspecs = xmakeglyphfontspecs(specs, &term.line[y][x1], x2 - x1, x1, y); i = ox = 0; From a8314643b1aeaa2187dad71dc5748aaac1760c1b Mon Sep 17 00:00:00 2001 From: "Devin J. Pohly" Date: Tue, 10 Oct 2017 12:46:53 -0500 Subject: [PATCH 030/185] Move window-manipulating functions into x.c xresize is now internal to x.c Signed-off-by: Devin J. Pohly --- st.c | 36 ------------------------------------ st.h | 9 +-------- win.h | 1 - x.c | 45 ++++++++++++++++++++++++++++++++++++++++++++- 4 files changed, 45 insertions(+), 46 deletions(-) diff --git a/st.c b/st.c index 540b487..75c191d 100644 --- a/st.c +++ b/st.c @@ -165,7 +165,6 @@ static void tnewline(int); static void tputtab(int); static void tputc(Rune); static void treset(void); -static void tresize(int, int); static void tscrollup(int, int); static void tscrolldown(int, int); static void tsetattr(int *, int); @@ -419,24 +418,6 @@ selinit(void) sel.clipboard = NULL; } -int -x2col(int x) -{ - x -= borderpx; - x /= win.cw; - - return LIMIT(x, 0, term.col-1); -} - -int -y2row(int y) -{ - y -= borderpx; - y /= win.ch; - - return LIMIT(y, 0, term.row-1); -} - int tlinelen(int y) { @@ -2620,20 +2601,3 @@ kmap(KeySym k, uint state) return NULL; } - -void -cresize(int width, int height) -{ - int col, row; - - if (width != 0) - win.w = width; - if (height != 0) - win.h = height; - - col = (win.w - 2 * borderpx) / win.cw; - row = (win.h - 2 * borderpx) / win.ch; - - tresize(col, row); - xresize(col, row); -} diff --git a/st.h b/st.h index 2199c13..5d44411 100644 --- a/st.h +++ b/st.h @@ -80,11 +80,6 @@ enum selection_snap { SNAP_LINE = 2 }; -enum window_state { - WIN_VISIBLE = 1, - WIN_FOCUSED = 2 -}; - typedef unsigned char uchar; typedef unsigned int uint; typedef unsigned long ulong; @@ -186,6 +181,7 @@ void redraw(void); int tattrset(int); void tnew(int, int); +void tresize(int, int); void tsetdirt(int, int); void tsetdirtattr(int); int match(uint, uint); @@ -198,15 +194,12 @@ void ttywrite(const char *, size_t); void resettitle(void); char *kmap(KeySym, uint); -void cresize(int, int); void selclear(void); void selinit(void); void selnormalize(void); int selected(int, int); char *getsel(void); -int x2col(int); -int y2row(int); size_t utf8decode(char *, Rune *, size_t); size_t utf8encode(Rune, char *); diff --git a/win.h b/win.h index dee0b7f..00113c5 100644 --- a/win.h +++ b/win.h @@ -16,7 +16,6 @@ void xloadcols(void); int xsetcolorname(int, const char *); void xsettitle(char *); void xsetpointermotion(int); -void xresize(int, int); void xselpaste(void); void xsetsel(char *, Time); void zoom(const Arg *); diff --git a/x.c b/x.c index 186e408..01ef1b0 100644 --- a/x.c +++ b/x.c @@ -88,12 +88,16 @@ static void xclear(int, int, int, int); static void xdrawcursor(void); static int xgeommasktogravity(int); static void xinit(void); +static void cresize(int, int); +static void xresize(int, int); static int xloadfont(Font *, FcPattern *); static void xloadfonts(char *, double); static void xunloadfont(Font *); static void xunloadfonts(void); static void xsetenv(void); static void xseturgency(int); +static int x2col(int); +static int y2row(int); static void expose(XEvent *); static void visibility(XEvent *); @@ -109,7 +113,6 @@ static void propnotify(XEvent *); static void selnotify(XEvent *); static void selclear_(XEvent *); static void selrequest(XEvent *); - static void selcopy(Time); static void getbuttoninfo(XEvent *); static void mousereport(XEvent *); @@ -148,6 +151,11 @@ static DC dc; static XWindow xw; static XSelection xsel; +enum window_state { + WIN_VISIBLE = 1, + WIN_FOCUSED = 2 +}; + /* Font Ring Cache */ enum { FRC_NORMAL, @@ -200,6 +208,24 @@ zoomreset(const Arg *arg) } } +int +x2col(int x) +{ + x -= borderpx; + x /= win.cw; + + return LIMIT(x, 0, term.col-1); +} + +int +y2row(int y) +{ + y -= borderpx; + y /= win.ch; + + return LIMIT(y, 0, term.row-1); +} + void getbuttoninfo(XEvent *e) { @@ -596,6 +622,23 @@ bmotion(XEvent *e) tsetdirt(MIN(sel.nb.y, oldsby), MAX(sel.ne.y, oldsey)); } +void +cresize(int width, int height) +{ + int col, row; + + if (width != 0) + win.w = width; + if (height != 0) + win.h = height; + + col = (win.w - 2 * borderpx) / win.cw; + row = (win.h - 2 * borderpx) / win.ch; + + tresize(col, row); + xresize(col, row); +} + void xresize(int col, int row) { From dbe8676d7d69651132bde2b6d9ec3787cbbc552a Mon Sep 17 00:00:00 2001 From: "Devin J. Pohly" Date: Tue, 10 Oct 2017 15:51:23 -0500 Subject: [PATCH 031/185] Pass new dimensions into ttyresize This removes another reference to TermWindow from st.c. Signed-off-by: Devin J. Pohly --- st.c | 6 +++--- st.h | 2 +- x.c | 6 +++--- 3 files changed, 7 insertions(+), 7 deletions(-) diff --git a/st.c b/st.c index 75c191d..839dc94 100644 --- a/st.c +++ b/st.c @@ -905,14 +905,14 @@ ttysend(char *s, size_t n) } void -ttyresize(void) +ttyresize(int tw, int th) { struct winsize w; w.ws_row = term.row; w.ws_col = term.col; - w.ws_xpixel = win.tw; - w.ws_ypixel = win.th; + w.ws_xpixel = tw; + w.ws_ypixel = th; if (ioctl(cmdfd, TIOCSWINSZ, &w) < 0) fprintf(stderr, "Couldn't set window size: %s\n", strerror(errno)); } diff --git a/st.h b/st.h index 5d44411..372462d 100644 --- a/st.h +++ b/st.h @@ -187,7 +187,7 @@ void tsetdirtattr(int); int match(uint, uint); void ttynew(void); size_t ttyread(void); -void ttyresize(void); +void ttyresize(int, int); void ttysend(char *, size_t); void ttywrite(const char *, size_t); diff --git a/x.c b/x.c index 01ef1b0..1b656ac 100644 --- a/x.c +++ b/x.c @@ -192,7 +192,7 @@ zoomabs(const Arg *arg) xunloadfonts(); xloadfonts(usedfont, arg->f); cresize(0, 0); - ttyresize(); + ttyresize(win.tw, win.th); redraw(); xhints(); } @@ -1679,7 +1679,7 @@ resize(XEvent *e) return; cresize(e->xconfigure.width, e->xconfigure.height); - ttyresize(); + ttyresize(win.tw, win.th); } void @@ -1710,7 +1710,7 @@ run(void) cresize(w, h); ttynew(); - ttyresize(); + ttyresize(win.tw, win.th); clock_gettime(CLOCK_MONOTONIC, &last); lastblink = last; From ed132e11271d18a5d8aa163096bc6192c694bc47 Mon Sep 17 00:00:00 2001 From: "Devin J. Pohly" Date: Wed, 11 Oct 2017 08:47:14 -0500 Subject: [PATCH 032/185] Move key-matching functions into x.c Modifiers and keysyms are specific to X, and the functions match and kmap are only used in x.c. Needed to global-ize the key arrays and lengths from config.h (for now). Signed-off-by: Devin J. Pohly --- config.def.h | 6 +++--- st.c | 58 ++-------------------------------------------------- st.h | 18 +++++++++++++--- x.c | 48 +++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 68 insertions(+), 62 deletions(-) diff --git a/config.def.h b/config.def.h index dd94be2..18cb31c 100644 --- a/config.def.h +++ b/config.def.h @@ -209,13 +209,13 @@ Shortcut shortcuts[] = { * If you want keys other than the X11 function keys (0xFD00 - 0xFFFF) * to be mapped below, add them to this array. */ -static KeySym mappedkeys[] = { -1 }; +KeySym mappedkeys[] = { -1 }; /* * State bits to ignore when matching key or button events. By default, * numlock (Mod2Mask) and keyboard layout (XK_SWITCH_MOD) are ignored. */ -static uint ignoremod = Mod2Mask|XK_SWITCH_MOD; +uint ignoremod = Mod2Mask|XK_SWITCH_MOD; /* * Override mouse-select while mask is active (when MODE_MOUSE is set). @@ -228,7 +228,7 @@ uint forceselmod = ShiftMask; * This is the huge key array which defines all compatibility to the Linux * world. Please decide about changes wisely. */ -static Key key[] = { +Key key[] = { /* keysym mask string appkey appcursor crlf */ { XK_KP_Home, ShiftMask, "\033[2J", 0, -1, 0}, { XK_KP_Home, ShiftMask, "\033[1;2H", 0, +1, 0}, diff --git a/st.c b/st.c index 839dc94..1a8fa1f 100644 --- a/st.c +++ b/st.c @@ -110,16 +110,6 @@ typedef struct { int narg; /* nb of args */ } STREscape; -typedef struct { - KeySym k; - uint mask; - char *s; - /* three valued logic variables: 0 indifferent, 1 on, -1 off */ - signed char appkey; /* application keypad */ - signed char appcursor; /* application cursor */ - signed char crlf; /* crlf mode */ -} Key; - /* function definitions used in config.h */ static void clipcopy(const Arg *); static void clippaste(const Arg *); @@ -223,6 +213,8 @@ size_t colornamelen = LEN(colorname); size_t mshortcutslen = LEN(mshortcuts); size_t shortcutslen = LEN(shortcuts); size_t selmaskslen = LEN(selmasks); +size_t keyslen = LEN(key); +size_t mappedkeyslen = LEN(mappedkeys); ssize_t xwrite(int fd, const char *s, size_t len) @@ -2550,54 +2542,8 @@ redraw(void) draw(); } -int -match(uint mask, uint state) -{ - return mask == XK_ANY_MOD || mask == (state & ~ignoremod); -} - void numlock(const Arg *dummy) { term.numlock ^= 1; } - -char* -kmap(KeySym k, uint state) -{ - Key *kp; - int i; - - /* Check for mapped keys out of X11 function keys. */ - for (i = 0; i < LEN(mappedkeys); i++) { - if (mappedkeys[i] == k) - break; - } - if (i == LEN(mappedkeys)) { - if ((k & 0xFFFF) < 0xFD00) - return NULL; - } - - for (kp = key; kp < key + LEN(key); kp++) { - if (kp->k != k) - continue; - - if (!match(kp->mask, state)) - continue; - - if (IS_SET(MODE_APPKEYPAD) ? kp->appkey < 0 : kp->appkey > 0) - continue; - if (term.numlock && kp->appkey == 2) - continue; - - if (IS_SET(MODE_APPCURSOR) ? kp->appcursor < 0 : kp->appcursor > 0) - continue; - - if (IS_SET(MODE_CRLF) ? kp->crlf < 0 : kp->crlf > 0) - continue; - - return kp->s; - } - - return NULL; -} diff --git a/st.h b/st.h index 372462d..c255b7c 100644 --- a/st.h +++ b/st.h @@ -176,6 +176,16 @@ typedef struct { const Arg arg; } Shortcut; +typedef struct { + KeySym k; + uint mask; + char *s; + /* three valued logic variables: 0 indifferent, 1 on, -1 off */ + signed char appkey; /* application keypad */ + signed char appcursor; /* application cursor */ + signed char crlf; /* crlf mode */ +} Key; + void die(const char *, ...); void redraw(void); @@ -184,7 +194,6 @@ void tnew(int, int); void tresize(int, int); void tsetdirt(int, int); void tsetdirtattr(int); -int match(uint, uint); void ttynew(void); size_t ttyread(void); void ttyresize(int, int); @@ -193,9 +202,7 @@ void ttywrite(const char *, size_t); void resettitle(void); -char *kmap(KeySym, uint); void selclear(void); - void selinit(void); void selnormalize(void); int selected(int, int); @@ -255,7 +262,12 @@ extern MouseShortcut mshortcuts[]; extern size_t mshortcutslen; extern Shortcut shortcuts[]; extern size_t shortcutslen; +extern KeySym mappedkeys[]; +extern size_t mappedkeyslen; +extern uint ignoremod; extern uint forceselmod; +extern Key key[]; +extern size_t keyslen; extern uint selmasks[]; extern size_t selmaskslen; extern char ascii_printable[]; diff --git a/x.c b/x.c index 1b656ac..371a467 100644 --- a/x.c +++ b/x.c @@ -116,6 +116,8 @@ static void selrequest(XEvent *); static void selcopy(Time); static void getbuttoninfo(XEvent *); static void mousereport(XEvent *); +static char *kmap(KeySym, uint); +static int match(uint, uint); static void run(void); static void usage(void); @@ -1603,6 +1605,52 @@ focus(XEvent *ev) } } +int +match(uint mask, uint state) +{ + return mask == XK_ANY_MOD || mask == (state & ~ignoremod); +} + +char* +kmap(KeySym k, uint state) +{ + Key *kp; + int i; + + /* Check for mapped keys out of X11 function keys. */ + for (i = 0; i < mappedkeyslen; i++) { + if (mappedkeys[i] == k) + break; + } + if (i == mappedkeyslen) { + if ((k & 0xFFFF) < 0xFD00) + return NULL; + } + + for (kp = key; kp < key + keyslen; kp++) { + if (kp->k != k) + continue; + + if (!match(kp->mask, state)) + continue; + + if (IS_SET(MODE_APPKEYPAD) ? kp->appkey < 0 : kp->appkey > 0) + continue; + if (term.numlock && kp->appkey == 2) + continue; + + if (IS_SET(MODE_APPCURSOR) ? kp->appcursor < 0 : kp->appcursor > 0) + continue; + + if (IS_SET(MODE_CRLF) ? kp->crlf < 0 : kp->crlf > 0) + continue; + + return kp->s; + } + + return NULL; +} + void kpress(XEvent *ev) { From 69e32a61df15787c410a48eaa10a89240c36257d Mon Sep 17 00:00:00 2001 From: "Devin J. Pohly" Date: Thu, 12 Oct 2017 22:25:49 -0500 Subject: [PATCH 033/185] Move opt_* into same file as main()/run() This commit is purely about reducing externs and LOC. If the main and run functions ever move elsewhere (which will probably make sense eventually), these should come along with them. Signed-off-by: Devin J. Pohly --- st.c | 50 ++++++++++++++++++++------------------------------ st.h | 11 ++--------- x.c | 12 +++++++++++- 3 files changed, 33 insertions(+), 40 deletions(-) diff --git a/st.c b/st.c index 1a8fa1f..58f7941 100644 --- a/st.c +++ b/st.c @@ -48,7 +48,6 @@ /* macros */ #define NUMMAXLEN(x) ((int)(sizeof(x) * 2.56 + 0.5) + 1) -#define DEFAULT(a, b) (a) = (a) ? (a) : (b) #define ISCONTROLC0(c) (BETWEEN(c, 0, 0x1f) || (c) == '\177') #define ISCONTROLC1(c) (BETWEEN(c, 0x80, 0x9f)) #define ISCONTROL(c) (ISCONTROLC0(c) || ISCONTROLC1(c)) @@ -124,8 +123,8 @@ static void sendbreak(const Arg *); /* config.h for applying patches and the configuration. */ #include "config.h" -static void execsh(void); -static void stty(void); +static void execsh(char **); +static void stty(char **); static void sigchld(int); static void csidump(void); @@ -189,14 +188,6 @@ Term term; Selection sel; int cmdfd; pid_t pid; -char **opt_cmd = NULL; -char *opt_class = NULL; -char *opt_embed = NULL; -char *opt_font = NULL; -char *opt_io = NULL; -char *opt_line = NULL; -char *opt_name = NULL; -char *opt_title = NULL; int oldbutton = 3; /* button event on startup: 3 = release */ static CSIEscape csiescseq; @@ -634,9 +625,9 @@ die(const char *errstr, ...) } void -execsh(void) +execsh(char **args) { - char **args, *sh, *prog; + char *sh, *prog; const struct passwd *pw; errno = 0; @@ -650,13 +641,13 @@ execsh(void) if ((sh = getenv("SHELL")) == NULL) sh = (pw->pw_shell[0]) ? pw->pw_shell : shell; - if (opt_cmd) - prog = opt_cmd[0]; + if (args) + prog = args[0]; else if (utmp) prog = utmp; else prog = sh; - args = (opt_cmd) ? opt_cmd : (char *[]) {prog, NULL}; + DEFAULT(args, ((char *[]) {prog, NULL})); unsetenv("COLUMNS"); unsetenv("LINES"); @@ -697,7 +688,7 @@ sigchld(int a) void -stty(void) +stty(char **args) { char cmd[_POSIX_ARG_MAX], **p, *q, *s; size_t n, siz; @@ -707,7 +698,7 @@ stty(void) memcpy(cmd, stty_args, n); q = cmd + n; siz = sizeof(cmd) - n; - for (p = opt_cmd; p && (s = *p); ++p) { + for (p = args; p && (s = *p); ++p) { if ((n = strlen(s)) > siz-1) die("stty parameter length too long\n"); *q++ = ' '; @@ -721,26 +712,26 @@ stty(void) } void -ttynew(void) +ttynew(char *line, char *out, char **args) { int m, s; struct winsize w = {term.row, term.col, 0, 0}; - if (opt_io) { + if (out) { term.mode |= MODE_PRINT; - iofd = (!strcmp(opt_io, "-")) ? - 1 : open(opt_io, O_WRONLY | O_CREAT, 0666); + iofd = (!strcmp(out, "-")) ? + 1 : open(out, O_WRONLY | O_CREAT, 0666); if (iofd < 0) { fprintf(stderr, "Error opening %s:%s\n", - opt_io, strerror(errno)); + out, strerror(errno)); } } - if (opt_line) { - if ((cmdfd = open(opt_line, O_RDWR)) < 0) + if (line) { + if ((cmdfd = open(line, O_RDWR)) < 0) die("open line failed: %s\n", strerror(errno)); dup2(cmdfd, 0); - stty(); + stty(args); return; } @@ -762,7 +753,7 @@ ttynew(void) die("ioctl TIOCSCTTY failed: %s\n", strerror(errno)); close(s); close(m); - execsh(); + execsh(args); break; default: close(s); @@ -1942,8 +1933,7 @@ void tprinter(char *s, size_t len) { if (iofd != -1 && xwrite(iofd, s, len) < 0) { - fprintf(stderr, "Error writing in %s:%s\n", - opt_io, strerror(errno)); + perror("Error writing to output file"); close(iofd); iofd = -1; } @@ -2532,7 +2522,7 @@ tresize(int col, int row) void resettitle(void) { - xsettitle(opt_title ? opt_title : "st"); + xsettitle(NULL); } void diff --git a/st.h b/st.h index c255b7c..09473c2 100644 --- a/st.h +++ b/st.h @@ -9,6 +9,7 @@ #define LEN(a) (sizeof(a) / sizeof(a)[0]) #define BETWEEN(x, a, b) ((a) <= (x) && (x) <= (b)) #define DIVCEIL(n, d) (((n) + ((d) - 1)) / (d)) +#define DEFAULT(a, b) (a) = (a) ? (a) : (b) #define LIMIT(x, a, b) (x) = (x) < (a) ? (a) : (x) > (b) ? (b) : (x) #define ATTRCMP(a, b) ((a).mode != (b).mode || (a).fg != (b).fg || \ (a).bg != (b).bg) @@ -194,7 +195,7 @@ void tnew(int, int); void tresize(int, int); void tsetdirt(int, int); void tsetdirtattr(int); -void ttynew(void); +void ttynew(char *, char *, char **); size_t ttyread(void); void ttyresize(int, int); void ttysend(char *, size_t); @@ -221,14 +222,6 @@ extern Term term; extern Selection sel; extern int cmdfd; extern pid_t pid; -extern char **opt_cmd; -extern char *opt_class; -extern char *opt_embed; -extern char *opt_font; -extern char *opt_io; -extern char *opt_line; -extern char *opt_name; -extern char *opt_title; extern int oldbutton; /* config.h globals */ diff --git a/x.c b/x.c index 371a467..e267961 100644 --- a/x.c +++ b/x.c @@ -179,6 +179,15 @@ static char *usedfont = NULL; static double usedfontsize = 0; static double defaultfontsize = 0; +static char *opt_class = NULL; +static char **opt_cmd = NULL; +static char *opt_embed = NULL; +static char *opt_font = NULL; +static char *opt_io = NULL; +static char *opt_line = NULL; +static char *opt_name = NULL; +static char *opt_title = NULL; + void zoom(const Arg *arg) { @@ -1473,6 +1482,7 @@ void xsettitle(char *p) { XTextProperty prop; + DEFAULT(p, "st"); Xutf8TextListToTextProperty(xw.dpy, &p, 1, XUTF8StringStyle, &prop); @@ -1757,7 +1767,7 @@ run(void) } while (ev.type != MapNotify); cresize(w, h); - ttynew(); + ttynew(opt_line, opt_io, opt_cmd); ttyresize(win.tw, win.th); clock_gettime(CLOCK_MONOTONIC, &last); From 32d3b1d00f66eda4f5446f3b32cabed2c9a77a40 Mon Sep 17 00:00:00 2001 From: "Devin J. Pohly" Date: Sun, 15 Oct 2017 20:35:48 -0500 Subject: [PATCH 034/185] Factor out equivalent code from ttyread/ttysend The echo-to-terminal portions of ttyread and ttysend were actually doing the same thing. New function twrite() now handles this. The parameter show_ctrl determines whether control characters are shown as "^A". This was the only difference between tputc and techo, and techo is now unused and removed. (This commit should not change st's behaviour.) Signed-off-by: Devin J. Pohly --- st.c | 101 +++++++++++++++++++++++------------------------------------ st.h | 2 +- 2 files changed, 41 insertions(+), 62 deletions(-) diff --git a/st.c b/st.c index 58f7941..641f896 100644 --- a/st.c +++ b/st.c @@ -161,8 +161,8 @@ static void tsetchar(Rune, Glyph *, int, int); static void tsetscroll(int, int); static void tswapscreen(void); static void tsetmode(int, int, int *, int); +static int twrite(const char *, int, int); static void tfulldirt(void); -static void techo(Rune); static void tcontrolcode(uchar ); static void tdectest(char ); static void tdefutf8(char); @@ -254,7 +254,7 @@ xstrdup(char *s) } size_t -utf8decode(char *c, Rune *u, size_t clen) +utf8decode(const char *c, Rune *u, size_t clen) { size_t i, j, len, type; Rune udecoded; @@ -768,38 +768,19 @@ ttyread(void) { static char buf[BUFSIZ]; static int buflen = 0; - char *ptr; - int charsize; /* size of utf8 char in bytes */ - Rune unicodep; + int written; int ret; /* append read bytes to unprocessed bytes */ if ((ret = read(cmdfd, buf+buflen, LEN(buf)-buflen)) < 0) die("Couldn't read from shell: %s\n", strerror(errno)); - buflen += ret; - ptr = buf; - for (;;) { - if (IS_SET(MODE_UTF8) && !IS_SET(MODE_SIXEL)) { - /* process a complete utf8 char */ - charsize = utf8decode(ptr, &unicodep, buflen); - if (charsize == 0) - break; - tputc(unicodep); - ptr += charsize; - buflen -= charsize; - - } else { - if (buflen <= 0) - break; - tputc(*ptr++ & 0xFF); - buflen--; - } - } + written = twrite(buf, buflen, 0); + buflen -= written; /* keep any uncomplete utf8 char for the next call */ if (buflen > 0) - memmove(buf, ptr, buflen); + memmove(buf, buf + written, buflen); return ret; } @@ -864,27 +845,9 @@ write_error: void ttysend(char *s, size_t n) { - int len; - char *t, *lim; - Rune u; - ttywrite(s, n); - if (!IS_SET(MODE_ECHO)) - return; - - lim = &s[n]; - for (t = s; t < lim; t += len) { - if (IS_SET(MODE_UTF8) && !IS_SET(MODE_SIXEL)) { - len = utf8decode(t, &u, n); - } else { - u = *t & 0xFF; - len = 1; - } - if (len <= 0) - break; - techo(u); - n -= len; - } + if (IS_SET(MODE_ECHO)) + twrite(s, n, 1); } void @@ -2031,22 +1994,6 @@ tputtab(int n) term.c.x = LIMIT(x, 0, term.col-1); } -void -techo(Rune u) -{ - if (ISCONTROL(u)) { /* control code */ - if (u & 0x80) { - u &= 0x7f; - tputc('^'); - tputc('['); - } else if (u != '\n' && u != '\r' && u != '\t') { - u ^= 0x40; - tputc('^'); - } - } - tputc(u); -} - void tdefutf8(char ascii) { @@ -2437,6 +2384,38 @@ check_control_code: } } +int +twrite(const char *buf, int buflen, int show_ctrl) +{ + int charsize; + Rune u; + int n; + + for (n = 0; n < buflen; n += charsize) { + if (IS_SET(MODE_UTF8) && !IS_SET(MODE_SIXEL)) { + /* process a complete utf8 char */ + charsize = utf8decode(buf + n, &u, buflen - n); + if (charsize == 0) + break; + } else { + u = buf[n] & 0xFF; + charsize = 1; + } + if (show_ctrl && ISCONTROL(u)) { + if (u & 0x80) { + u &= 0x7f; + tputc('^'); + tputc('['); + } else if (u != '\n' && u != '\r' && u != '\t') { + u ^= 0x40; + tputc('^'); + } + } + tputc(u); + } + return n; +} + void tresize(int col, int row) { diff --git a/st.h b/st.h index 09473c2..3d9b6e7 100644 --- a/st.h +++ b/st.h @@ -209,7 +209,7 @@ void selnormalize(void); int selected(int, int); char *getsel(void); -size_t utf8decode(char *, Rune *, size_t); +size_t utf8decode(const char *, Rune *, size_t); size_t utf8encode(Rune, char *); void *xmalloc(size_t); From 65976c1a29f2945c3cfb6af74cd6440cf193021d Mon Sep 17 00:00:00 2001 From: "Devin J. Pohly" Date: Tue, 17 Oct 2017 15:21:04 -0500 Subject: [PATCH 035/185] Move config.h include from st.c to x.c config.h includes references to KeySyms and other X stuff. Until we come up with a cleaner way to separate configuration, it is simpler (leads to more code removal) to have this here. Signed-off-by: Devin J. Pohly --- config.def.h | 73 ++++++++++++++++++++++++++-------------------------- st.c | 47 +++------------------------------ st.h | 50 ++++++++++------------------------- win.h | 3 --- x.c | 37 ++++++++++++++++++++++++++ 5 files changed, 91 insertions(+), 119 deletions(-) diff --git a/config.def.h b/config.def.h index 18cb31c..1c181ab 100644 --- a/config.def.h +++ b/config.def.h @@ -5,8 +5,8 @@ * * font: see http://freedesktop.org/software/fontconfig/fontconfig-user.html */ -char font[] = "Liberation Mono:pixelsize=12:antialias=true:autohint=true"; -int borderpx = 2; +static char *font = "Liberation Mono:pixelsize=12:antialias=true:autohint=true"; +static int borderpx = 2; /* * What program is execed by st depends of these precedence rules: @@ -16,54 +16,54 @@ int borderpx = 2; * 4: value of shell in /etc/passwd * 5: value of shell in config.h */ -static char shell[] = "/bin/sh"; -static char *utmp = NULL; -static char stty_args[] = "stty raw pass8 nl -echo -iexten -cstopb 38400"; +char *shell = "/bin/sh"; +char *utmp = NULL; +char *stty_args = "stty raw pass8 nl -echo -iexten -cstopb 38400"; /* identification sequence returned in DA and DECID */ -static char vtiden[] = "\033[?6c"; +char *vtiden = "\033[?6c"; /* Kerning / character bounding-box multipliers */ -float cwscale = 1.0; -float chscale = 1.0; +static float cwscale = 1.0; +static float chscale = 1.0; /* * word delimiter string * * More advanced example: " `'\"()[]{}" */ -static char worddelimiters[] = " "; +char *worddelimiters = " "; /* selection timeouts (in milliseconds) */ -unsigned int doubleclicktimeout = 300; -unsigned int tripleclicktimeout = 600; +static unsigned int doubleclicktimeout = 300; +static unsigned int tripleclicktimeout = 600; /* alt screens */ int allowaltscreen = 1; /* frames per second st should at maximum draw to the screen */ -unsigned int xfps = 120; -unsigned int actionfps = 30; +static unsigned int xfps = 120; +static unsigned int actionfps = 30; /* * blinking timeout (set to 0 to disable blinking) for the terminal blinking * attribute. */ -unsigned int blinktimeout = 800; +static unsigned int blinktimeout = 800; /* * thickness of underline and bar cursors */ -unsigned int cursorthickness = 2; +static unsigned int cursorthickness = 2; /* * bell volume. It must be a value between -100 and 100. Use 0 for disabling * it */ -int bellvolume = 0; +static int bellvolume = 0; /* default TERM value */ -char termname[] = "st-256color"; +char *termname = "st-256color"; /* * spaces per tab @@ -80,10 +80,10 @@ char termname[] = "st-256color"; * * stty tabs */ -static unsigned int tabspaces = 8; +unsigned int tabspaces = 8; /* Terminal colors (16 first used in escape sequence) */ -const char *colorname[] = { +static const char *colorname[] = { /* 8 normal colors */ "black", "red3", @@ -118,8 +118,8 @@ const char *colorname[] = { */ unsigned int defaultfg = 7; unsigned int defaultbg = 0; -unsigned int defaultcs = 256; -unsigned int defaultrcs = 257; +static unsigned int defaultcs = 256; +static unsigned int defaultrcs = 257; /* * Default shape of cursor @@ -128,33 +128,33 @@ unsigned int defaultrcs = 257; * 6: Bar ("|") * 7: Snowman ("☃") */ -unsigned int cursorshape = 2; +static unsigned int cursorshape = 2; /* * Default columns and rows numbers */ -unsigned int cols = 80; -unsigned int rows = 24; +static unsigned int cols = 80; +static unsigned int rows = 24; /* * Default colour and shape of the mouse cursor */ -unsigned int mouseshape = XC_xterm; -unsigned int mousefg = 7; -unsigned int mousebg = 0; +static unsigned int mouseshape = XC_xterm; +static unsigned int mousefg = 7; +static unsigned int mousebg = 0; /* * Color used to display font attributes when fontconfig selected a font which * doesn't match the ones requested. */ -unsigned int defaultattr = 11; +static unsigned int defaultattr = 11; /* * Internal mouse shortcuts. * Beware that overloading Button1 will disable the selection. */ -MouseShortcut mshortcuts[] = { +static MouseShortcut mshortcuts[] = { /* button mask string */ { Button4, XK_ANY_MOD, "\031" }, { Button5, XK_ANY_MOD, "\005" }, @@ -164,7 +164,7 @@ MouseShortcut mshortcuts[] = { #define MODKEY Mod1Mask #define TERMMOD (ControlMask|ShiftMask) -Shortcut shortcuts[] = { +static Shortcut shortcuts[] = { /* mask keysym function argument */ { XK_ANY_MOD, XK_Break, sendbreak, {.i = 0} }, { ControlMask, XK_Print, toggleprinter, {.i = 0} }, @@ -209,26 +209,26 @@ Shortcut shortcuts[] = { * If you want keys other than the X11 function keys (0xFD00 - 0xFFFF) * to be mapped below, add them to this array. */ -KeySym mappedkeys[] = { -1 }; +static KeySym mappedkeys[] = { -1 }; /* * State bits to ignore when matching key or button events. By default, * numlock (Mod2Mask) and keyboard layout (XK_SWITCH_MOD) are ignored. */ -uint ignoremod = Mod2Mask|XK_SWITCH_MOD; +static uint ignoremod = Mod2Mask|XK_SWITCH_MOD; /* * Override mouse-select while mask is active (when MODE_MOUSE is set). * Note that if you want to use ShiftMask with selmasks, set this to an other * modifier, set to 0 to not use it. */ -uint forceselmod = ShiftMask; +static uint forceselmod = ShiftMask; /* * This is the huge key array which defines all compatibility to the Linux * world. Please decide about changes wisely. */ -Key key[] = { +static Key key[] = { /* keysym mask string appkey appcursor crlf */ { XK_KP_Home, ShiftMask, "\033[2J", 0, -1, 0}, { XK_KP_Home, ShiftMask, "\033[1;2H", 0, +1, 0}, @@ -451,7 +451,7 @@ Key key[] = { * ButtonRelease and MotionNotify. * If no match is found, regular selection is used. */ -uint selmasks[] = { +static uint selmasks[] = { [SEL_RECTANGULAR] = Mod1Mask, }; @@ -459,8 +459,7 @@ uint selmasks[] = { * Printable characters in ASCII, used to estimate the advance width * of single wide characters. */ -char ascii_printable[] = +static char ascii_printable[] = " !\"#$%&'()*+,-./0123456789:;<=>?" "@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_" "`abcdefghijklmnopqrstuvwxyz{|}~"; - diff --git a/st.c b/st.c index 641f896..ec747cc 100644 --- a/st.c +++ b/st.c @@ -109,19 +109,6 @@ typedef struct { int narg; /* nb of args */ } STREscape; -/* function definitions used in config.h */ -static void clipcopy(const Arg *); -static void clippaste(const Arg *); -static void numlock(const Arg *); -static void selpaste(const Arg *); -static void printsel(const Arg *); -static void printscreen(const Arg *) ; -static void iso14755(const Arg *); -static void toggleprinter(const Arg *); -static void sendbreak(const Arg *); - -/* config.h for applying patches and the configuration. */ -#include "config.h" static void execsh(char **); static void stty(char **); @@ -199,14 +186,6 @@ 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}; -/* config.h array lengths */ -size_t colornamelen = LEN(colorname); -size_t mshortcutslen = LEN(mshortcuts); -size_t shortcutslen = LEN(shortcuts); -size_t selmaskslen = LEN(selmasks); -size_t keyslen = LEN(key); -size_t mappedkeyslen = LEN(mappedkeys); - ssize_t xwrite(int fd, const char *s, size_t len) { @@ -585,24 +564,6 @@ getsel(void) return str; } -void -selpaste(const Arg *dummy) -{ - xselpaste(); -} - -void -clipcopy(const Arg *dummy) -{ - xclipcopy(); -} - -void -clippaste(const Arg *dummy) -{ - xclippaste(); -} - void selclear(void) { @@ -1572,7 +1533,7 @@ csihandle(void) break; case 'c': /* DA -- Device Attributes */ if (csiescseq.arg[0] == 0) - ttywrite(vtiden, sizeof(vtiden) - 1); + ttywrite(vtiden, strlen(vtiden)); break; case 'C': /* CUF -- Cursor Forward */ case 'a': /* HPR -- Cursor Forward */ @@ -1791,7 +1752,7 @@ strhandle(void) dec = base64dec(strescseq.args[2]); if (dec) { xsetsel(dec, CurrentTime); - clipcopy(NULL); + xclipcopy(); } else { fprintf(stderr, "erresc: invalid base64\n"); } @@ -2134,7 +2095,7 @@ tcontrolcode(uchar ascii) case 0x99: /* TODO: SGCI */ break; case 0x9a: /* DECID -- Identify Terminal */ - ttywrite(vtiden, sizeof(vtiden) - 1); + ttywrite(vtiden, strlen(vtiden)); break; case 0x9b: /* TODO: CSI */ case 0x9c: /* TODO: ST */ @@ -2206,7 +2167,7 @@ eschandle(uchar ascii) } break; case 'Z': /* DECID -- Identify Terminal */ - ttywrite(vtiden, sizeof(vtiden) - 1); + ttywrite(vtiden, strlen(vtiden)); break; case 'c': /* RIS -- Reset to inital state */ treset(); diff --git a/st.h b/st.h index 3d9b6e7..9314607 100644 --- a/st.h +++ b/st.h @@ -190,6 +190,13 @@ typedef struct { void die(const char *, ...); void redraw(void); +void iso14755(const Arg *); +void numlock(const Arg *); +void printscreen(const Arg *); +void printsel(const Arg *); +void sendbreak(const Arg *); +void toggleprinter(const Arg *); + int tattrset(int); void tnew(int, int); void tresize(int, int); @@ -225,42 +232,13 @@ extern pid_t pid; extern int oldbutton; /* config.h globals */ -extern char font[]; -extern int borderpx; -extern float cwscale; -extern float chscale; -extern unsigned int doubleclicktimeout; -extern unsigned int tripleclicktimeout; +extern char *shell; +extern char *utmp; +extern char *stty_args; +extern char *vtiden; +extern char *worddelimiters; extern int allowaltscreen; -extern unsigned int xfps; -extern unsigned int actionfps; -extern unsigned int cursorthickness; -extern int bellvolume; -extern unsigned int blinktimeout; -extern char termname[]; -extern const char *colorname[]; -extern size_t colornamelen; +extern char *termname; +extern unsigned int tabspaces; extern unsigned int defaultfg; extern unsigned int defaultbg; -extern unsigned int defaultcs; -extern unsigned int defaultrcs; -extern unsigned int cursorshape; -extern unsigned int cols; -extern unsigned int rows; -extern unsigned int mouseshape; -extern unsigned int mousefg; -extern unsigned int mousebg; -extern unsigned int defaultattr; -extern MouseShortcut mshortcuts[]; -extern size_t mshortcutslen; -extern Shortcut shortcuts[]; -extern size_t shortcutslen; -extern KeySym mappedkeys[]; -extern size_t mappedkeyslen; -extern uint ignoremod; -extern uint forceselmod; -extern Key key[]; -extern size_t keyslen; -extern uint selmasks[]; -extern size_t selmaskslen; -extern char ascii_printable[]; diff --git a/win.h b/win.h index 00113c5..b7022ec 100644 --- a/win.h +++ b/win.h @@ -18,6 +18,3 @@ void xsettitle(char *); void xsetpointermotion(int); void xselpaste(void); void xsetsel(char *, Time); -void zoom(const Arg *); -void zoomabs(const Arg *); -void zoomreset(const Arg *); diff --git a/x.c b/x.c index e267961..cb8c351 100644 --- a/x.c +++ b/x.c @@ -20,6 +20,25 @@ static char *argv0; #include "st.h" #include "win.h" +/* function definitions used in config.h */ +static void clipcopy(const Arg *); +static void clippaste(const Arg *); +static void selpaste(const Arg *); +static void zoom(const Arg *); +static void zoomabs(const Arg *); +static void zoomreset(const Arg *); + +/* config.h for applying patches and the configuration. */ +#include "config.h" + +/* config.h array lengths */ +size_t colornamelen = LEN(colorname); +size_t mshortcutslen = LEN(mshortcuts); +size_t shortcutslen = LEN(shortcuts); +size_t selmaskslen = LEN(selmasks); +size_t keyslen = LEN(key); +size_t mappedkeyslen = LEN(mappedkeys); + /* XEMBED messages */ #define XEMBED_FOCUS_IN 4 #define XEMBED_FOCUS_OUT 5 @@ -188,6 +207,24 @@ static char *opt_line = NULL; static char *opt_name = NULL; static char *opt_title = NULL; +void +clipcopy(const Arg *dummy) +{ + xclipcopy(); +} + +void +clippaste(const Arg *dummy) +{ + xclippaste(); +} + +void +selpaste(const Arg *dummy) +{ + xselpaste(); +} + void zoom(const Arg *arg) { From 428f01969aaf48ffa2983746c0a397bcc8946799 Mon Sep 17 00:00:00 2001 From: "Devin J. Pohly" Date: Tue, 17 Oct 2017 15:43:32 -0500 Subject: [PATCH 036/185] Inline clipboard functions No need to keep a function that only calls another function in the same file. Signed-off-by: Devin J. Pohly --- win.h | 2 -- x.c | 50 +++++++++++++++++++------------------------------- 2 files changed, 19 insertions(+), 33 deletions(-) diff --git a/win.h b/win.h index b7022ec..beb458d 100644 --- a/win.h +++ b/win.h @@ -10,11 +10,9 @@ void drawregion(int, int, int, int); void xbell(void); void xclipcopy(void); -void xclippaste(void); void xhints(void); void xloadcols(void); int xsetcolorname(int, const char *); void xsettitle(char *); void xsetpointermotion(int); -void xselpaste(void); void xsetsel(char *, Time); diff --git a/x.c b/x.c index cb8c351..46356fe 100644 --- a/x.c +++ b/x.c @@ -210,19 +210,33 @@ static char *opt_title = NULL; void clipcopy(const Arg *dummy) { - xclipcopy(); + Atom clipboard; + + if (sel.clipboard != NULL) + free(sel.clipboard); + + if (sel.primary != NULL) { + sel.clipboard = xstrdup(sel.primary); + clipboard = XInternAtom(xw.dpy, "CLIPBOARD", 0); + XSetSelectionOwner(xw.dpy, clipboard, xw.win, CurrentTime); + } } void clippaste(const Arg *dummy) { - xclippaste(); + Atom clipboard; + + clipboard = XInternAtom(xw.dpy, "CLIPBOARD", 0); + XConvertSelection(xw.dpy, clipboard, xsel.xtarget, clipboard, + xw.win, CurrentTime); } void selpaste(const Arg *dummy) { - xselpaste(); + XConvertSelection(xw.dpy, XA_PRIMARY, xsel.xtarget, XA_PRIMARY, + xw.win, CurrentTime); } void @@ -518,36 +532,10 @@ selnotify(XEvent *e) XDeleteProperty(xw.dpy, xw.win, (int)property); } -void -xselpaste(void) -{ - XConvertSelection(xw.dpy, XA_PRIMARY, xsel.xtarget, XA_PRIMARY, - xw.win, CurrentTime); -} - void xclipcopy(void) { - Atom clipboard; - - if (sel.clipboard != NULL) - free(sel.clipboard); - - if (sel.primary != NULL) { - sel.clipboard = xstrdup(sel.primary); - clipboard = XInternAtom(xw.dpy, "CLIPBOARD", 0); - XSetSelectionOwner(xw.dpy, clipboard, xw.win, CurrentTime); - } -} - -void -xclippaste(void) -{ - Atom clipboard; - - clipboard = XInternAtom(xw.dpy, "CLIPBOARD", 0); - XConvertSelection(xw.dpy, clipboard, xsel.xtarget, clipboard, - xw.win, CurrentTime); + clipcopy(NULL); } void @@ -634,7 +622,7 @@ brelease(XEvent *e) } if (e->xbutton.button == Button2) { - xselpaste(); + selpaste(NULL); } else if (e->xbutton.button == Button1) { if (sel.mode == SEL_READY) { getbuttoninfo(e); From 75c9a0ee1d232a1a177746d97a13cf92b03da44a Mon Sep 17 00:00:00 2001 From: "Devin J. Pohly" Date: Tue, 17 Oct 2017 15:46:31 -0500 Subject: [PATCH 037/185] Remove unneeded array-length variables These were only used in x.c, which now has direct visibility of the config.h arrays. Signed-off-by: Devin J. Pohly --- x.c | 22 +++++++--------------- 1 file changed, 7 insertions(+), 15 deletions(-) diff --git a/x.c b/x.c index 46356fe..03555d1 100644 --- a/x.c +++ b/x.c @@ -31,14 +31,6 @@ static void zoomreset(const Arg *); /* config.h for applying patches and the configuration. */ #include "config.h" -/* config.h array lengths */ -size_t colornamelen = LEN(colorname); -size_t mshortcutslen = LEN(mshortcuts); -size_t shortcutslen = LEN(shortcuts); -size_t selmaskslen = LEN(selmasks); -size_t keyslen = LEN(key); -size_t mappedkeyslen = LEN(mappedkeys); - /* XEMBED messages */ #define XEMBED_FOCUS_IN 4 #define XEMBED_FOCUS_OUT 5 @@ -301,7 +293,7 @@ getbuttoninfo(XEvent *e) selnormalize(); sel.type = SEL_REGULAR; - for (type = 1; type < selmaskslen; ++type) { + for (type = 1; type < LEN(selmasks); ++type) { if (match(selmasks[type], state)) { sel.type = type; break; @@ -384,7 +376,7 @@ bpress(XEvent *e) return; } - for (ms = mshortcuts; ms < mshortcuts + mshortcutslen; ms++) { + for (ms = mshortcuts; ms < mshortcuts + LEN(mshortcuts); ms++) { if (e->xbutton.button == ms->b && match(ms->mask, e->xbutton.state)) { ttysend(ms->s, strlen(ms->s)); @@ -728,7 +720,7 @@ xloadcols(void) static int loaded; Color *cp; - dc.collen = MAX(colornamelen, 256); + dc.collen = MAX(LEN(colorname), 256); dc.col = xmalloc(dc.collen * sizeof(Color)); if (loaded) { @@ -1653,16 +1645,16 @@ kmap(KeySym k, uint state) int i; /* Check for mapped keys out of X11 function keys. */ - for (i = 0; i < mappedkeyslen; i++) { + for (i = 0; i < LEN(mappedkeys); i++) { if (mappedkeys[i] == k) break; } - if (i == mappedkeyslen) { + if (i == LEN(mappedkeys)) { if ((k & 0xFFFF) < 0xFD00) return NULL; } - for (kp = key; kp < key + keyslen; kp++) { + for (kp = key; kp < key + LEN(key); kp++) { if (kp->k != k) continue; @@ -1702,7 +1694,7 @@ kpress(XEvent *ev) len = XmbLookupString(xw.xic, e, buf, sizeof buf, &ksym, &status); /* 1. shortcuts */ - for (bp = shortcuts; bp < shortcuts + shortcutslen; bp++) { + for (bp = shortcuts; bp < shortcuts + LEN(shortcuts); bp++) { if (ksym == bp->keysym && match(bp->mod, e->state)) { bp->func(&(bp->arg)); return; From 416dd257274fd334be082b1138338adffa3e2d5e Mon Sep 17 00:00:00 2001 From: "Devin J. Pohly" Date: Tue, 17 Oct 2017 16:46:26 -0500 Subject: [PATCH 038/185] Move X-related config.h types into x.c No need to expose Shortcut, MouseShortcut, and Key anymore. Signed-off-by: Devin J. Pohly --- st.h | 23 ----------------------- x.c | 24 ++++++++++++++++++++++++ 2 files changed, 24 insertions(+), 23 deletions(-) diff --git a/st.h b/st.h index 9314607..71c79f4 100644 --- a/st.h +++ b/st.h @@ -134,12 +134,6 @@ typedef struct { int cursor; /* cursor style */ } TermWindow; -typedef struct { - uint b; - uint mask; - char *s; -} MouseShortcut; - typedef struct { int mode; int type; @@ -170,23 +164,6 @@ typedef union { const void *v; } Arg; -typedef struct { - uint mod; - KeySym keysym; - void (*func)(const Arg *); - const Arg arg; -} Shortcut; - -typedef struct { - KeySym k; - uint mask; - char *s; - /* three valued logic variables: 0 indifferent, 1 on, -1 off */ - signed char appkey; /* application keypad */ - signed char appcursor; /* application cursor */ - signed char crlf; /* crlf mode */ -} Key; - void die(const char *, ...); void redraw(void); diff --git a/x.c b/x.c index 03555d1..24f6991 100644 --- a/x.c +++ b/x.c @@ -20,6 +20,30 @@ static char *argv0; #include "st.h" #include "win.h" +/* types used in config.h */ +typedef struct { + uint mod; + KeySym keysym; + void (*func)(const Arg *); + const Arg arg; +} Shortcut; + +typedef struct { + uint b; + uint mask; + char *s; +} MouseShortcut; + +typedef struct { + KeySym k; + uint mask; + char *s; + /* three valued logic variables: 0 indifferent, 1 on, -1 off */ + signed char appkey; /* application keypad */ + signed char appcursor; /* application cursor */ + signed char crlf; /* crlf mode */ +} Key; + /* function definitions used in config.h */ static void clipcopy(const Arg *); static void clippaste(const Arg *); From 323d38da20c8a1d295ab1dbc0fc7ce947ef824e1 Mon Sep 17 00:00:00 2001 From: "Devin J. Pohly" Date: Mon, 6 Nov 2017 17:57:45 -0600 Subject: [PATCH 039/185] Make win variable internal to x.c There was only a single reference to the `win` variable in st.c, so exporting that to x.c allows us to rid ourselves of another extern. Signed-off-by: Devin J. Pohly --- st.c | 6 +----- st.h | 1 - win.h | 1 + x.c | 11 +++++++++++ 4 files changed, 13 insertions(+), 6 deletions(-) diff --git a/st.c b/st.c index ec747cc..fdf697b 100644 --- a/st.c +++ b/st.c @@ -170,7 +170,6 @@ static char *base64dec(const char *); static ssize_t xwrite(int, const char *, size_t); /* Globals */ -TermWindow win; Term term; Selection sel; int cmdfd; @@ -1683,11 +1682,8 @@ csihandle(void) case ' ': switch (csiescseq.mode[1]) { case 'q': /* DECSCUSR -- Set Cursor Style */ - DEFAULT(csiescseq.arg[0], 1); - if (!BETWEEN(csiescseq.arg[0], 0, 6)) { + if (xsetcursor(csiescseq.arg[0])) goto unknown; - } - win.cursor = csiescseq.arg[0]; break; default: goto unknown; diff --git a/st.h b/st.h index 71c79f4..8637d35 100644 --- a/st.h +++ b/st.h @@ -201,7 +201,6 @@ void *xrealloc(void *, size_t); char *xstrdup(char *); /* Globals */ -extern TermWindow win; extern Term term; extern Selection sel; extern int cmdfd; diff --git a/win.h b/win.h index beb458d..c6a5337 100644 --- a/win.h +++ b/win.h @@ -14,5 +14,6 @@ void xhints(void); void xloadcols(void); int xsetcolorname(int, const char *); void xsettitle(char *); +int xsetcursor(int); void xsetpointermotion(int); void xsetsel(char *, Time); diff --git a/x.c b/x.c index 24f6991..04e2e05 100644 --- a/x.c +++ b/x.c @@ -187,6 +187,7 @@ static void (*handler[LASTEvent])(XEvent *) = { static DC dc; static XWindow xw; static XSelection xsel; +static TermWindow win; enum window_state { WIN_VISIBLE = 1, @@ -1615,6 +1616,16 @@ xsetpointermotion(int set) XChangeWindowAttributes(xw.dpy, xw.win, CWEventMask, &xw.attrs); } +int +xsetcursor(int cursor) +{ + DEFAULT(cursor, 1); + if (!BETWEEN(cursor, 0, 6)) + return 1; + win.cursor = cursor; + return 0; +} + void xseturgency(int add) { From 3bb900cd6c1c7a5364bd79bce63fdd8711bc878b Mon Sep 17 00:00:00 2001 From: "Devin J. Pohly" Date: Mon, 6 Nov 2017 18:25:58 -0600 Subject: [PATCH 040/185] Remove Time argument from xsetsel This is an X type and should be internal to x.c. The selcopy() function was a single line and only used in one place, so it was inlined to reduce LOC. Signed-off-by: Devin J. Pohly --- st.c | 2 +- win.h | 2 +- x.c | 18 +++++++++--------- 3 files changed, 11 insertions(+), 11 deletions(-) diff --git a/st.c b/st.c index fdf697b..bcb6473 100644 --- a/st.c +++ b/st.c @@ -1747,7 +1747,7 @@ strhandle(void) dec = base64dec(strescseq.args[2]); if (dec) { - xsetsel(dec, CurrentTime); + xsetsel(dec); xclipcopy(); } else { fprintf(stderr, "erresc: invalid base64\n"); diff --git a/win.h b/win.h index c6a5337..f95a679 100644 --- a/win.h +++ b/win.h @@ -16,4 +16,4 @@ int xsetcolorname(int, const char *); void xsettitle(char *); int xsetcursor(int); void xsetpointermotion(int); -void xsetsel(char *, Time); +void xsetsel(char *); diff --git a/x.c b/x.c index 04e2e05..a332ac9 100644 --- a/x.c +++ b/x.c @@ -148,7 +148,7 @@ static void propnotify(XEvent *); static void selnotify(XEvent *); static void selclear_(XEvent *); static void selrequest(XEvent *); -static void selcopy(Time); +static void setsel(char *, Time); static void getbuttoninfo(XEvent *); static void mousereport(XEvent *); static char *kmap(KeySym, uint); @@ -440,12 +440,6 @@ bpress(XEvent *e) } } -void -selcopy(Time t) -{ - xsetsel(getsel(), t); -} - void propnotify(XEvent *e) { @@ -620,7 +614,7 @@ selrequest(XEvent *e) } void -xsetsel(char *str, Time t) +setsel(char *str, Time t) { free(sel.primary); sel.primary = str; @@ -630,6 +624,12 @@ xsetsel(char *str, Time t) selclear_(NULL); } +void +xsetsel(char *str) +{ + setsel(str, CurrentTime); +} + void brelease(XEvent *e) { @@ -643,7 +643,7 @@ brelease(XEvent *e) } else if (e->xbutton.button == Button1) { if (sel.mode == SEL_READY) { getbuttoninfo(e); - selcopy(e->xbutton.time); + setsel(getsel(), e->xbutton.time); } else selclear_(NULL); sel.mode = SEL_IDLE; From 8b564c1a3f51c08e64c2f589852a02b8595d44ca Mon Sep 17 00:00:00 2001 From: "Devin J. Pohly" Date: Mon, 6 Nov 2017 18:30:45 -0600 Subject: [PATCH 041/185] Remove X and fontconfig from st.c None of the X-related includes are needed any longer. In addition, move the X modifier defines into x.c, as they are not used outside. Signed-off-by: Devin J. Pohly --- st.c | 5 ----- win.h | 5 ----- x.c | 5 +++++ 3 files changed, 5 insertions(+), 10 deletions(-) diff --git a/st.c b/st.c index bcb6473..9cfa547 100644 --- a/st.c +++ b/st.c @@ -21,13 +21,8 @@ #include #include #include -#include #include -/* X11 */ -#include -#include - #include "st.h" #include "win.h" diff --git a/win.h b/win.h index f95a679..123662e 100644 --- a/win.h +++ b/win.h @@ -1,10 +1,5 @@ /* See LICENSE for license details. */ -/* X modifiers */ -#define XK_ANY_MOD UINT_MAX -#define XK_NO_MOD 0 -#define XK_SWITCH_MOD (1<<13) - void draw(void); void drawregion(int, int, int, int); diff --git a/x.c b/x.c index a332ac9..e5b236d 100644 --- a/x.c +++ b/x.c @@ -44,6 +44,11 @@ typedef struct { signed char crlf; /* crlf mode */ } Key; +/* X modifiers */ +#define XK_ANY_MOD UINT_MAX +#define XK_NO_MOD 0 +#define XK_SWITCH_MOD (1<<13) + /* function definitions used in config.h */ static void clipcopy(const Arg *); static void clippaste(const Arg *); From d84f3f4bd15e7d65fc0334cf7d62913c901bad00 Mon Sep 17 00:00:00 2001 From: "Devin J. Pohly" Date: Wed, 21 Feb 2018 22:28:41 -0600 Subject: [PATCH 042/185] Rely on ttyresize to set tty size This removes ttynew's dependency on cresize being called first, and then allows us to absorb the ttyresize call into cresize (which always precedes it). Signed-off-by: Devin J. Pohly --- st.c | 3 +-- x.c | 6 ++---- 2 files changed, 3 insertions(+), 6 deletions(-) diff --git a/st.c b/st.c index 9cfa547..dae7b91 100644 --- a/st.c +++ b/st.c @@ -670,7 +670,6 @@ void ttynew(char *line, char *out, char **args) { int m, s; - struct winsize w = {term.row, term.col, 0, 0}; if (out) { term.mode |= MODE_PRINT; @@ -691,7 +690,7 @@ ttynew(char *line, char *out, char **args) } /* seems to work fine on linux, openbsd and freebsd */ - if (openpty(&m, &s, NULL, NULL, &w) < 0) + if (openpty(&m, &s, NULL, NULL, NULL) < 0) die("openpty failed: %s\n", strerror(errno)); switch (pid = fork()) { diff --git a/x.c b/x.c index e5b236d..7bfa1b7 100644 --- a/x.c +++ b/x.c @@ -276,7 +276,6 @@ zoomabs(const Arg *arg) xunloadfonts(); xloadfonts(usedfont, arg->f); cresize(0, 0); - ttyresize(win.tw, win.th); redraw(); xhints(); } @@ -695,6 +694,7 @@ cresize(int width, int height) tresize(col, row); xresize(col, row); + ttyresize(win.tw, win.th); } void @@ -1794,7 +1794,6 @@ resize(XEvent *e) return; cresize(e->xconfigure.width, e->xconfigure.height); - ttyresize(win.tw, win.th); } void @@ -1823,9 +1822,8 @@ run(void) } } while (ev.type != MapNotify); - cresize(w, h); ttynew(opt_line, opt_io, opt_cmd); - ttyresize(win.tw, win.th); + cresize(w, h); clock_gettime(CLOCK_MONOTONIC, &last); lastblink = last; From 138caf294ea4d7968df36ead9d5ff5fc49f6215f Mon Sep 17 00:00:00 2001 From: "Devin J. Pohly" Date: Wed, 21 Feb 2018 22:48:28 -0600 Subject: [PATCH 043/185] Have selected() check whether selection exists Signed-off-by: Devin J. Pohly --- st.c | 3 ++- x.c | 10 ++++------ 2 files changed, 6 insertions(+), 7 deletions(-) diff --git a/st.c b/st.c index dae7b91..d4dfe6e 100644 --- a/st.c +++ b/st.c @@ -419,7 +419,8 @@ selnormalize(void) int selected(int x, int y) { - if (sel.mode == SEL_EMPTY) + if (sel.mode == SEL_EMPTY || sel.ob.x == -1 || + sel.alt != IS_SET(MODE_ALTSCREEN)) return 0; if (sel.type == SEL_RECTANGULAR) diff --git a/x.c b/x.c index 7bfa1b7..e3e5451 100644 --- a/x.c +++ b/x.c @@ -1418,7 +1418,6 @@ xdrawcursor(void) static int oldx = 0, oldy = 0; int curx; Glyph g = {' ', ATTR_NULL, defaultbg, defaultcs}, og; - int ena_sel = sel.ob.x != -1 && sel.alt == IS_SET(MODE_ALTSCREEN); Color drawcol; LIMIT(oldx, 0, term.col-1); @@ -1434,7 +1433,7 @@ xdrawcursor(void) /* remove the old cursor */ og = term.line[oldy][oldx]; - if (ena_sel && selected(oldx, oldy)) + if (selected(oldx, oldy)) og.mode ^= ATTR_REVERSE; xdrawglyph(og, oldx, oldy); @@ -1448,7 +1447,7 @@ xdrawcursor(void) if (IS_SET(MODE_REVERSE)) { g.mode |= ATTR_REVERSE; g.bg = defaultfg; - if (ena_sel && selected(term.c.x, term.c.y)) { + if (selected(term.c.x, term.c.y)) { drawcol = dc.col[defaultcs]; g.fg = defaultrcs; } else { @@ -1456,7 +1455,7 @@ xdrawcursor(void) g.fg = defaultcs; } } else { - if (ena_sel && selected(term.c.x, term.c.y)) { + if (selected(term.c.x, term.c.y)) { drawcol = dc.col[defaultrcs]; g.fg = defaultfg; g.bg = defaultrcs; @@ -1555,7 +1554,6 @@ drawregion(int x1, int y1, int x2, int y2) int i, x, y, ox, numspecs; Glyph base, new; XftGlyphFontSpec *specs; - int ena_sel = sel.ob.x != -1 && sel.alt == IS_SET(MODE_ALTSCREEN); if (!(win.state & WIN_VISIBLE)) return; @@ -1574,7 +1572,7 @@ drawregion(int x1, int y1, int x2, int y2) new = term.line[y][x]; if (new.mode == ATTR_WDUMMY) continue; - if (ena_sel && selected(x, y)) + if (selected(x, y)) new.mode ^= ATTR_REVERSE; if (i > 0 && ATTRCMP(base, new)) { xdrawglyphfontspecs(specs, base, i, ox, y); From 5683b1f80c5ac274adf98517ce2217b4d4896243 Mon Sep 17 00:00:00 2001 From: "Devin J. Pohly" Date: Wed, 21 Feb 2018 22:56:02 -0600 Subject: [PATCH 044/185] Move X-specific selection info into XSelection Data about PRIMARY/CLIPBOARD and clicks are part of the front-end, not the terminal. Signed-off-by: Devin J. Pohly --- st.c | 4 ---- st.h | 5 ----- x.c | 31 +++++++++++++++++++------------ 3 files changed, 19 insertions(+), 21 deletions(-) diff --git a/st.c b/st.c index d4dfe6e..ea0726c 100644 --- a/st.c +++ b/st.c @@ -365,13 +365,9 @@ base64dec(const char *src) void selinit(void) { - clock_gettime(CLOCK_MONOTONIC, &sel.tclick1); - clock_gettime(CLOCK_MONOTONIC, &sel.tclick2); sel.mode = SEL_IDLE; sel.snap = 0; sel.ob.x = -1; - sel.primary = NULL; - sel.clipboard = NULL; } int diff --git a/st.h b/st.h index 8637d35..79dd47e 100644 --- a/st.h +++ b/st.h @@ -149,12 +149,7 @@ typedef struct { int x, y; } nb, ne, ob, oe; - char *primary, *clipboard; int alt; - struct timespec tclick1; - struct timespec tclick2; - - //Atom xtarget; } Selection; typedef union { diff --git a/x.c b/x.c index e3e5451..9f506b1 100644 --- a/x.c +++ b/x.c @@ -94,6 +94,9 @@ typedef struct { typedef struct { Atom xtarget; + char *primary, *clipboard; + struct timespec tclick1; + struct timespec tclick2; } XSelection; /* Font structure */ @@ -234,11 +237,11 @@ clipcopy(const Arg *dummy) { Atom clipboard; - if (sel.clipboard != NULL) - free(sel.clipboard); + if (xsel.clipboard != NULL) + free(xsel.clipboard); - if (sel.primary != NULL) { - sel.clipboard = xstrdup(sel.primary); + if (xsel.primary != NULL) { + xsel.clipboard = xstrdup(xsel.primary); clipboard = XInternAtom(xw.dpy, "CLIPBOARD", 0); XSetSelectionOwner(xw.dpy, clipboard, xw.win, CurrentTime); } @@ -427,9 +430,9 @@ bpress(XEvent *e) * If the user clicks below predefined timeouts specific * snapping behaviour is exposed. */ - if (TIMEDIFF(now, sel.tclick2) <= tripleclicktimeout) { + if (TIMEDIFF(now, xsel.tclick2) <= tripleclicktimeout) { sel.snap = SNAP_LINE; - } else if (TIMEDIFF(now, sel.tclick1) <= doubleclicktimeout) { + } else if (TIMEDIFF(now, xsel.tclick1) <= doubleclicktimeout) { sel.snap = SNAP_WORD; } else { sel.snap = 0; @@ -439,8 +442,8 @@ bpress(XEvent *e) if (sel.snap != 0) sel.mode = SEL_READY; tsetdirt(sel.nb.y, sel.ne.y); - sel.tclick2 = sel.tclick1; - sel.tclick1 = now; + xsel.tclick2 = xsel.tclick1; + xsel.tclick1 = now; } } @@ -594,9 +597,9 @@ selrequest(XEvent *e) */ clipboard = XInternAtom(xw.dpy, "CLIPBOARD", 0); if (xsre->selection == XA_PRIMARY) { - seltext = sel.primary; + seltext = xsel.primary; } else if (xsre->selection == clipboard) { - seltext = sel.clipboard; + seltext = xsel.clipboard; } else { fprintf(stderr, "Unhandled clipboard selection 0x%lx\n", @@ -620,8 +623,8 @@ selrequest(XEvent *e) void setsel(char *str, Time t) { - free(sel.primary); - sel.primary = str; + free(xsel.primary); + xsel.primary = str; XSetSelectionOwner(xw.dpy, XA_PRIMARY, xw.win, t); if (XGetSelectionOwner(xw.dpy, XA_PRIMARY) != xw.win) @@ -1127,6 +1130,10 @@ xinit(void) xhints(); XSync(xw.dpy, False); + clock_gettime(CLOCK_MONOTONIC, &xsel.tclick1); + clock_gettime(CLOCK_MONOTONIC, &xsel.tclick2); + xsel.primary = NULL; + xsel.clipboard = NULL; xsel.xtarget = XInternAtom(xw.dpy, "UTF8_STRING", 0); if (xsel.xtarget == None) xsel.xtarget = XA_STRING; From bcb5d3adbe57ead05a829e5144c2ba1dc465865f Mon Sep 17 00:00:00 2001 From: "Devin J. Pohly" Date: Wed, 21 Feb 2018 23:29:41 -0600 Subject: [PATCH 045/185] Move terminal-related selection logic into st.c The front-end determines information about mouse clicks and motion, and the terminal handles the actual selection start/extend/dirty logic by row and column. While we're in the neighborhood, we'll also rename getbuttoninfo() to mousesel() which is, at least, less wrong. Signed-off-by: Devin J. Pohly --- st.c | 37 +++++++++++++++++++++++++++++++++++++ st.h | 3 ++- x.c | 55 +++++++++++++++---------------------------------------- 3 files changed, 54 insertions(+), 41 deletions(-) diff --git a/st.c b/st.c index ea0726c..21cba9e 100644 --- a/st.c +++ b/st.c @@ -140,6 +140,7 @@ static void tscrollup(int, int); static void tscrolldown(int, int); static void tsetattr(int *, int); static void tsetchar(Rune, Glyph *, int, int); +static void tsetdirt(int, int); static void tsetscroll(int, int); static void tswapscreen(void); static void tsetmode(int, int, int *, int); @@ -384,6 +385,42 @@ tlinelen(int y) return i; } +void +selstart(int col, int row, int snap) +{ + selclear(); + sel.mode = SEL_EMPTY; + sel.type = SEL_REGULAR; + sel.snap = snap; + sel.oe.x = sel.ob.x = col; + sel.oe.y = sel.ob.y = row; + selnormalize(); + + if (sel.snap != 0) + sel.mode = SEL_READY; + tsetdirt(sel.nb.y, sel.ne.y); +} + +void +selextend(int col, int row, int type) +{ + int oldey, oldex, oldsby, oldsey, oldtype; + oldey = sel.oe.y; + oldex = sel.oe.x; + oldsby = sel.nb.y; + oldsey = sel.ne.y; + oldtype = sel.type; + + sel.alt = IS_SET(MODE_ALTSCREEN); + sel.oe.x = col; + sel.oe.y = row; + selnormalize(); + sel.type = type; + + if (oldey != sel.oe.y || oldex != sel.oe.x || oldtype != sel.type) + tsetdirt(MIN(sel.nb.y, oldsby), MAX(sel.ne.y, oldsey)); +} + void selnormalize(void) { diff --git a/st.h b/st.h index 79dd47e..c5ff5cf 100644 --- a/st.h +++ b/st.h @@ -172,7 +172,6 @@ void toggleprinter(const Arg *); int tattrset(int); void tnew(int, int); void tresize(int, int); -void tsetdirt(int, int); void tsetdirtattr(int); void ttynew(char *, char *, char **); size_t ttyread(void); @@ -184,6 +183,8 @@ void resettitle(void); void selclear(void); void selinit(void); +void selstart(int, int, int); +void selextend(int, int, int); void selnormalize(void); int selected(int, int); char *getsel(void); diff --git a/x.c b/x.c index 9f506b1..ddae6b6 100644 --- a/x.c +++ b/x.c @@ -157,7 +157,7 @@ static void selnotify(XEvent *); static void selclear_(XEvent *); static void selrequest(XEvent *); static void setsel(char *, Time); -static void getbuttoninfo(XEvent *); +static void mousesel(XEvent *); static void mousereport(XEvent *); static char *kmap(KeySym, uint); static int match(uint, uint); @@ -313,24 +313,19 @@ y2row(int y) } void -getbuttoninfo(XEvent *e) +mousesel(XEvent *e) { - int type; + int type, seltype = SEL_REGULAR; uint state = e->xbutton.state & ~(Button1Mask | forceselmod); - sel.alt = IS_SET(MODE_ALTSCREEN); - - sel.oe.x = x2col(e->xbutton.x); - sel.oe.y = y2row(e->xbutton.y); - selnormalize(); - - sel.type = SEL_REGULAR; for (type = 1; type < LEN(selmasks); ++type) { if (match(selmasks[type], state)) { - sel.type = type; + seltype = type; break; } } + + selextend(x2col(e->xbutton.x), y2row(e->xbutton.y), seltype); } void @@ -402,6 +397,7 @@ bpress(XEvent *e) { struct timespec now; MouseShortcut *ms; + int snap; if (IS_SET(MODE_MOUSE) && !(e->xbutton.state & forceselmod)) { mousereport(e); @@ -417,33 +413,22 @@ bpress(XEvent *e) } if (e->xbutton.button == Button1) { - clock_gettime(CLOCK_MONOTONIC, &now); - - /* Clear previous selection, logically and visually. */ - selclear_(NULL); - sel.mode = SEL_EMPTY; - sel.type = SEL_REGULAR; - sel.oe.x = sel.ob.x = x2col(e->xbutton.x); - sel.oe.y = sel.ob.y = y2row(e->xbutton.y); - /* * If the user clicks below predefined timeouts specific * snapping behaviour is exposed. */ + clock_gettime(CLOCK_MONOTONIC, &now); if (TIMEDIFF(now, xsel.tclick2) <= tripleclicktimeout) { - sel.snap = SNAP_LINE; + snap = SNAP_LINE; } else if (TIMEDIFF(now, xsel.tclick1) <= doubleclicktimeout) { - sel.snap = SNAP_WORD; + snap = SNAP_WORD; } else { - sel.snap = 0; + snap = 0; } - selnormalize(); - - if (sel.snap != 0) - sel.mode = SEL_READY; - tsetdirt(sel.nb.y, sel.ne.y); xsel.tclick2 = xsel.tclick1; xsel.tclick1 = now; + + selstart(x2col(e->xbutton.x), y2row(e->xbutton.y), snap); } } @@ -649,20 +634,17 @@ brelease(XEvent *e) selpaste(NULL); } else if (e->xbutton.button == Button1) { if (sel.mode == SEL_READY) { - getbuttoninfo(e); + mousesel(e); setsel(getsel(), e->xbutton.time); } else selclear_(NULL); sel.mode = SEL_IDLE; - tsetdirt(sel.nb.y, sel.ne.y); } } void bmotion(XEvent *e) { - int oldey, oldex, oldsby, oldsey; - if (IS_SET(MODE_MOUSE) && !(e->xbutton.state & forceselmod)) { mousereport(e); return; @@ -672,14 +654,7 @@ bmotion(XEvent *e) return; sel.mode = SEL_READY; - oldey = sel.oe.y; - oldex = sel.oe.x; - oldsby = sel.nb.y; - oldsey = sel.ne.y; - getbuttoninfo(e); - - if (oldey != sel.oe.y || oldex != sel.oe.x) - tsetdirt(MIN(sel.nb.y, oldsby), MAX(sel.ne.y, oldsey)); + mousesel(e); } void From cfc7acdfd923924ae150a32061fb95987697b159 Mon Sep 17 00:00:00 2001 From: "Devin J. Pohly" Date: Wed, 21 Feb 2018 23:54:29 -0600 Subject: [PATCH 046/185] Move remaining selection mode logic into selextend The "done" parameter indicates a change which finalizes the selection (e.g. a mouse button release as opposed to motion). Signed-off-by: Devin J. Pohly --- st.c | 14 ++++++++++++-- st.h | 3 +-- x.c | 27 +++++++++------------------ 3 files changed, 22 insertions(+), 22 deletions(-) diff --git a/st.c b/st.c index 21cba9e..b7e215e 100644 --- a/st.c +++ b/st.c @@ -167,11 +167,11 @@ static ssize_t xwrite(int, const char *, size_t); /* Globals */ Term term; -Selection sel; int cmdfd; pid_t pid; int oldbutton = 3; /* button event on startup: 3 = release */ +static Selection sel; static CSIEscape csiescseq; static STREscape strescseq; static int iofd = 1; @@ -402,9 +402,17 @@ selstart(int col, int row, int snap) } void -selextend(int col, int row, int type) +selextend(int col, int row, int type, int done) { int oldey, oldex, oldsby, oldsey, oldtype; + + if (!sel.mode) + return; + if (done && sel.mode == SEL_EMPTY) { + selclear(); + return; + } + oldey = sel.oe.y; oldex = sel.oe.x; oldsby = sel.nb.y; @@ -419,6 +427,8 @@ selextend(int col, int row, int type) if (oldey != sel.oe.y || oldex != sel.oe.x || oldtype != sel.type) tsetdirt(MIN(sel.nb.y, oldsby), MAX(sel.ne.y, oldsey)); + + sel.mode = done ? SEL_IDLE : SEL_READY; } void diff --git a/st.h b/st.h index c5ff5cf..a34e31c 100644 --- a/st.h +++ b/st.h @@ -184,7 +184,7 @@ void resettitle(void); void selclear(void); void selinit(void); void selstart(int, int, int); -void selextend(int, int, int); +void selextend(int, int, int, int); void selnormalize(void); int selected(int, int); char *getsel(void); @@ -198,7 +198,6 @@ char *xstrdup(char *); /* Globals */ extern Term term; -extern Selection sel; extern int cmdfd; extern pid_t pid; extern int oldbutton; diff --git a/x.c b/x.c index ddae6b6..a7f619e 100644 --- a/x.c +++ b/x.c @@ -157,7 +157,7 @@ static void selnotify(XEvent *); static void selclear_(XEvent *); static void selrequest(XEvent *); static void setsel(char *, Time); -static void mousesel(XEvent *); +static void mousesel(XEvent *, int); static void mousereport(XEvent *); static char *kmap(KeySym, uint); static int match(uint, uint); @@ -313,7 +313,7 @@ y2row(int y) } void -mousesel(XEvent *e) +mousesel(XEvent *e, int done) { int type, seltype = SEL_REGULAR; uint state = e->xbutton.state & ~(Button1Mask | forceselmod); @@ -324,8 +324,9 @@ mousesel(XEvent *e) break; } } - - selextend(x2col(e->xbutton.x), y2row(e->xbutton.y), seltype); + selextend(x2col(e->xbutton.x), y2row(e->xbutton.y), seltype, done); + if (done) + setsel(getsel(), e->xbutton.time); } void @@ -630,16 +631,10 @@ brelease(XEvent *e) return; } - if (e->xbutton.button == Button2) { + if (e->xbutton.button == Button2) selpaste(NULL); - } else if (e->xbutton.button == Button1) { - if (sel.mode == SEL_READY) { - mousesel(e); - setsel(getsel(), e->xbutton.time); - } else - selclear_(NULL); - sel.mode = SEL_IDLE; - } + else if (e->xbutton.button == Button1) + mousesel(e, 1); } void @@ -650,11 +645,7 @@ bmotion(XEvent *e) return; } - if (!sel.mode) - return; - - sel.mode = SEL_READY; - mousesel(e); + mousesel(e, 0); } void From 52d6fb1ab1f7d41839edebb63c3408578cd44e3c Mon Sep 17 00:00:00 2001 From: "Devin J. Pohly" Date: Thu, 22 Feb 2018 00:42:23 -0600 Subject: [PATCH 047/185] Move terminal echo logic into st.c The only thing differentiating ttywrite and ttysend was the potential for echo; make this a parameter and remove ttysend. Signed-off-by: Devin J. Pohly --- st.c | 23 +++++++++-------------- st.h | 3 +-- x.c | 18 +++++++++--------- 3 files changed, 19 insertions(+), 25 deletions(-) diff --git a/st.c b/st.c index b7e215e..7d546da 100644 --- a/st.c +++ b/st.c @@ -784,12 +784,15 @@ ttyread(void) } void -ttywrite(const char *s, size_t n) +ttywrite(const char *s, size_t n, int may_echo) { fd_set wfd, rfd; ssize_t r; size_t lim = 256; + if (may_echo && IS_SET(MODE_ECHO)) + twrite(s, n, 1); + /* * Remember that we are using a pty, which might be a modem line. * Writing too much will clog the line. That's why we are doing this @@ -840,14 +843,6 @@ write_error: die("write error on tty: %s\n", strerror(errno)); } -void -ttysend(char *s, size_t n) -{ - ttywrite(s, n); - if (IS_SET(MODE_ECHO)) - twrite(s, n, 1); -} - void ttyresize(int tw, int th) { @@ -1570,7 +1565,7 @@ csihandle(void) break; case 'c': /* DA -- Device Attributes */ if (csiescseq.arg[0] == 0) - ttywrite(vtiden, strlen(vtiden)); + ttywrite(vtiden, strlen(vtiden), 0); break; case 'C': /* CUF -- Cursor Forward */ case 'a': /* HPR -- Cursor Forward */ @@ -1698,7 +1693,7 @@ csihandle(void) if (csiescseq.arg[0] == 6) { len = snprintf(buf, sizeof(buf),"\033[%i;%iR", term.c.y+1, term.c.x+1); - ttywrite(buf, len); + ttywrite(buf, len, 0); } break; case 'r': /* DECSTBM -- Set Scrolling Region */ @@ -1916,7 +1911,7 @@ iso14755(const Arg *arg) (*e != '\n' && *e != '\0')) return; - ttysend(uc, utf8encode(utf32, uc)); + ttywrite(uc, utf8encode(utf32, uc), 1); } void @@ -2129,7 +2124,7 @@ tcontrolcode(uchar ascii) case 0x99: /* TODO: SGCI */ break; case 0x9a: /* DECID -- Identify Terminal */ - ttywrite(vtiden, strlen(vtiden)); + ttywrite(vtiden, strlen(vtiden), 0); break; case 0x9b: /* TODO: CSI */ case 0x9c: /* TODO: ST */ @@ -2201,7 +2196,7 @@ eschandle(uchar ascii) } break; case 'Z': /* DECID -- Identify Terminal */ - ttywrite(vtiden, strlen(vtiden)); + ttywrite(vtiden, strlen(vtiden), 0); break; case 'c': /* RIS -- Reset to inital state */ treset(); diff --git a/st.h b/st.h index a34e31c..d7738a0 100644 --- a/st.h +++ b/st.h @@ -176,8 +176,7 @@ void tsetdirtattr(int); void ttynew(char *, char *, char **); size_t ttyread(void); void ttyresize(int, int); -void ttysend(char *, size_t); -void ttywrite(const char *, size_t); +void ttywrite(const char *, size_t, int); void resettitle(void); diff --git a/x.c b/x.c index a7f619e..49a22e4 100644 --- a/x.c +++ b/x.c @@ -390,7 +390,7 @@ mousereport(XEvent *e) return; } - ttywrite(buf, len); + ttywrite(buf, len, 0); } void @@ -408,7 +408,7 @@ bpress(XEvent *e) for (ms = mshortcuts; ms < mshortcuts + LEN(mshortcuts); ms++) { if (e->xbutton.button == ms->b && match(ms->mask, e->xbutton.state)) { - ttysend(ms->s, strlen(ms->s)); + ttywrite(ms->s, strlen(ms->s), 1); return; } } @@ -520,10 +520,10 @@ selnotify(XEvent *e) } if (IS_SET(MODE_BRCKTPASTE) && ofs == 0) - ttywrite("\033[200~", 6); - ttysend((char *)data, nitems * format / 8); + ttywrite("\033[200~", 6, 0); + ttywrite((char *)data, nitems * format / 8, 1); if (IS_SET(MODE_BRCKTPASTE) && rem == 0) - ttywrite("\033[201~", 6); + ttywrite("\033[201~", 6, 0); XFree(data); /* number of 32-bit chunks returned */ ofs += nitems * format / 32; @@ -1634,12 +1634,12 @@ focus(XEvent *ev) win.state |= WIN_FOCUSED; xseturgency(0); if (IS_SET(MODE_FOCUS)) - ttywrite("\033[I", 3); + ttywrite("\033[I", 3, 0); } else { XUnsetICFocus(xw.xic); win.state &= ~WIN_FOCUSED; if (IS_SET(MODE_FOCUS)) - ttywrite("\033[O", 3); + ttywrite("\033[O", 3, 0); } } @@ -1714,7 +1714,7 @@ kpress(XEvent *ev) /* 2. custom keys from config.h */ if ((customkey = kmap(ksym, e->state))) { - ttysend(customkey, strlen(customkey)); + ttywrite(customkey, strlen(customkey), 1); return; } @@ -1733,7 +1733,7 @@ kpress(XEvent *ev) len = 2; } } - ttysend(buf, len); + ttywrite(buf, len, 1); } From 33201ac65f74e45b4fa60822ba9a538c3cfa9b25 Mon Sep 17 00:00:00 2001 From: "Devin J. Pohly" Date: Thu, 22 Feb 2018 01:05:12 -0600 Subject: [PATCH 048/185] Move CRLF input processing into ttywrite This also allows us to remove the crlf field from the Key struct, since the only difference it made was converting "\r" to "\r\n" (which is now done automatically in ttywrite). In addition, MODE_CRLF is no longer referenced from x.c. Signed-off-by: Devin J. Pohly --- config.def.h | 423 +++++++++++++++++++++++++-------------------------- st.c | 32 +++- x.c | 6 +- 3 files changed, 240 insertions(+), 221 deletions(-) diff --git a/config.def.h b/config.def.h index 1c181ab..616616a 100644 --- a/config.def.h +++ b/config.def.h @@ -229,219 +229,216 @@ static uint forceselmod = ShiftMask; * world. Please decide about changes wisely. */ static Key key[] = { - /* keysym mask string appkey appcursor crlf */ - { XK_KP_Home, ShiftMask, "\033[2J", 0, -1, 0}, - { XK_KP_Home, ShiftMask, "\033[1;2H", 0, +1, 0}, - { XK_KP_Home, XK_ANY_MOD, "\033[H", 0, -1, 0}, - { XK_KP_Home, XK_ANY_MOD, "\033[1~", 0, +1, 0}, - { XK_KP_Up, XK_ANY_MOD, "\033Ox", +1, 0, 0}, - { XK_KP_Up, XK_ANY_MOD, "\033[A", 0, -1, 0}, - { XK_KP_Up, XK_ANY_MOD, "\033OA", 0, +1, 0}, - { XK_KP_Down, XK_ANY_MOD, "\033Or", +1, 0, 0}, - { XK_KP_Down, XK_ANY_MOD, "\033[B", 0, -1, 0}, - { XK_KP_Down, XK_ANY_MOD, "\033OB", 0, +1, 0}, - { XK_KP_Left, XK_ANY_MOD, "\033Ot", +1, 0, 0}, - { XK_KP_Left, XK_ANY_MOD, "\033[D", 0, -1, 0}, - { XK_KP_Left, XK_ANY_MOD, "\033OD", 0, +1, 0}, - { XK_KP_Right, XK_ANY_MOD, "\033Ov", +1, 0, 0}, - { XK_KP_Right, XK_ANY_MOD, "\033[C", 0, -1, 0}, - { XK_KP_Right, XK_ANY_MOD, "\033OC", 0, +1, 0}, - { XK_KP_Prior, ShiftMask, "\033[5;2~", 0, 0, 0}, - { XK_KP_Prior, XK_ANY_MOD, "\033[5~", 0, 0, 0}, - { XK_KP_Begin, XK_ANY_MOD, "\033[E", 0, 0, 0}, - { XK_KP_End, ControlMask, "\033[J", -1, 0, 0}, - { XK_KP_End, ControlMask, "\033[1;5F", +1, 0, 0}, - { XK_KP_End, ShiftMask, "\033[K", -1, 0, 0}, - { XK_KP_End, ShiftMask, "\033[1;2F", +1, 0, 0}, - { XK_KP_End, XK_ANY_MOD, "\033[4~", 0, 0, 0}, - { XK_KP_Next, ShiftMask, "\033[6;2~", 0, 0, 0}, - { XK_KP_Next, XK_ANY_MOD, "\033[6~", 0, 0, 0}, - { XK_KP_Insert, ShiftMask, "\033[2;2~", +1, 0, 0}, - { XK_KP_Insert, ShiftMask, "\033[4l", -1, 0, 0}, - { XK_KP_Insert, ControlMask, "\033[L", -1, 0, 0}, - { XK_KP_Insert, ControlMask, "\033[2;5~", +1, 0, 0}, - { XK_KP_Insert, XK_ANY_MOD, "\033[4h", -1, 0, 0}, - { XK_KP_Insert, XK_ANY_MOD, "\033[2~", +1, 0, 0}, - { XK_KP_Delete, ControlMask, "\033[M", -1, 0, 0}, - { XK_KP_Delete, ControlMask, "\033[3;5~", +1, 0, 0}, - { XK_KP_Delete, ShiftMask, "\033[2K", -1, 0, 0}, - { XK_KP_Delete, ShiftMask, "\033[3;2~", +1, 0, 0}, - { XK_KP_Delete, XK_ANY_MOD, "\033[P", -1, 0, 0}, - { XK_KP_Delete, XK_ANY_MOD, "\033[3~", +1, 0, 0}, - { XK_KP_Multiply, XK_ANY_MOD, "\033Oj", +2, 0, 0}, - { XK_KP_Add, XK_ANY_MOD, "\033Ok", +2, 0, 0}, - { XK_KP_Enter, XK_ANY_MOD, "\033OM", +2, 0, 0}, - { XK_KP_Enter, XK_ANY_MOD, "\r", -1, 0, -1}, - { XK_KP_Enter, XK_ANY_MOD, "\r\n", -1, 0, +1}, - { XK_KP_Subtract, XK_ANY_MOD, "\033Om", +2, 0, 0}, - { XK_KP_Decimal, XK_ANY_MOD, "\033On", +2, 0, 0}, - { XK_KP_Divide, XK_ANY_MOD, "\033Oo", +2, 0, 0}, - { XK_KP_0, XK_ANY_MOD, "\033Op", +2, 0, 0}, - { XK_KP_1, XK_ANY_MOD, "\033Oq", +2, 0, 0}, - { XK_KP_2, XK_ANY_MOD, "\033Or", +2, 0, 0}, - { XK_KP_3, XK_ANY_MOD, "\033Os", +2, 0, 0}, - { XK_KP_4, XK_ANY_MOD, "\033Ot", +2, 0, 0}, - { XK_KP_5, XK_ANY_MOD, "\033Ou", +2, 0, 0}, - { XK_KP_6, XK_ANY_MOD, "\033Ov", +2, 0, 0}, - { XK_KP_7, XK_ANY_MOD, "\033Ow", +2, 0, 0}, - { XK_KP_8, XK_ANY_MOD, "\033Ox", +2, 0, 0}, - { XK_KP_9, XK_ANY_MOD, "\033Oy", +2, 0, 0}, - { XK_Up, ShiftMask, "\033[1;2A", 0, 0, 0}, - { XK_Up, Mod1Mask, "\033[1;3A", 0, 0, 0}, - { XK_Up, ShiftMask|Mod1Mask,"\033[1;4A", 0, 0, 0}, - { XK_Up, ControlMask, "\033[1;5A", 0, 0, 0}, - { XK_Up, ShiftMask|ControlMask,"\033[1;6A", 0, 0, 0}, - { XK_Up, ControlMask|Mod1Mask,"\033[1;7A", 0, 0, 0}, - { XK_Up,ShiftMask|ControlMask|Mod1Mask,"\033[1;8A", 0, 0, 0}, - { XK_Up, XK_ANY_MOD, "\033[A", 0, -1, 0}, - { XK_Up, XK_ANY_MOD, "\033OA", 0, +1, 0}, - { XK_Down, ShiftMask, "\033[1;2B", 0, 0, 0}, - { XK_Down, Mod1Mask, "\033[1;3B", 0, 0, 0}, - { XK_Down, ShiftMask|Mod1Mask,"\033[1;4B", 0, 0, 0}, - { XK_Down, ControlMask, "\033[1;5B", 0, 0, 0}, - { XK_Down, ShiftMask|ControlMask,"\033[1;6B", 0, 0, 0}, - { XK_Down, ControlMask|Mod1Mask,"\033[1;7B", 0, 0, 0}, - { XK_Down,ShiftMask|ControlMask|Mod1Mask,"\033[1;8B",0, 0, 0}, - { XK_Down, XK_ANY_MOD, "\033[B", 0, -1, 0}, - { XK_Down, XK_ANY_MOD, "\033OB", 0, +1, 0}, - { XK_Left, ShiftMask, "\033[1;2D", 0, 0, 0}, - { XK_Left, Mod1Mask, "\033[1;3D", 0, 0, 0}, - { XK_Left, ShiftMask|Mod1Mask,"\033[1;4D", 0, 0, 0}, - { XK_Left, ControlMask, "\033[1;5D", 0, 0, 0}, - { XK_Left, ShiftMask|ControlMask,"\033[1;6D", 0, 0, 0}, - { XK_Left, ControlMask|Mod1Mask,"\033[1;7D", 0, 0, 0}, - { XK_Left,ShiftMask|ControlMask|Mod1Mask,"\033[1;8D",0, 0, 0}, - { XK_Left, XK_ANY_MOD, "\033[D", 0, -1, 0}, - { XK_Left, XK_ANY_MOD, "\033OD", 0, +1, 0}, - { XK_Right, ShiftMask, "\033[1;2C", 0, 0, 0}, - { XK_Right, Mod1Mask, "\033[1;3C", 0, 0, 0}, - { XK_Right, ShiftMask|Mod1Mask,"\033[1;4C", 0, 0, 0}, - { XK_Right, ControlMask, "\033[1;5C", 0, 0, 0}, - { XK_Right, ShiftMask|ControlMask,"\033[1;6C", 0, 0, 0}, - { XK_Right, ControlMask|Mod1Mask,"\033[1;7C", 0, 0, 0}, - { XK_Right,ShiftMask|ControlMask|Mod1Mask,"\033[1;8C",0, 0, 0}, - { XK_Right, XK_ANY_MOD, "\033[C", 0, -1, 0}, - { XK_Right, XK_ANY_MOD, "\033OC", 0, +1, 0}, - { XK_ISO_Left_Tab, ShiftMask, "\033[Z", 0, 0, 0}, - { XK_Return, Mod1Mask, "\033\r", 0, 0, -1}, - { XK_Return, Mod1Mask, "\033\r\n", 0, 0, +1}, - { XK_Return, XK_ANY_MOD, "\r", 0, 0, -1}, - { XK_Return, XK_ANY_MOD, "\r\n", 0, 0, +1}, - { XK_Insert, ShiftMask, "\033[4l", -1, 0, 0}, - { XK_Insert, ShiftMask, "\033[2;2~", +1, 0, 0}, - { XK_Insert, ControlMask, "\033[L", -1, 0, 0}, - { XK_Insert, ControlMask, "\033[2;5~", +1, 0, 0}, - { XK_Insert, XK_ANY_MOD, "\033[4h", -1, 0, 0}, - { XK_Insert, XK_ANY_MOD, "\033[2~", +1, 0, 0}, - { XK_Delete, ControlMask, "\033[M", -1, 0, 0}, - { XK_Delete, ControlMask, "\033[3;5~", +1, 0, 0}, - { XK_Delete, ShiftMask, "\033[2K", -1, 0, 0}, - { XK_Delete, ShiftMask, "\033[3;2~", +1, 0, 0}, - { XK_Delete, XK_ANY_MOD, "\033[P", -1, 0, 0}, - { XK_Delete, XK_ANY_MOD, "\033[3~", +1, 0, 0}, - { XK_BackSpace, XK_NO_MOD, "\177", 0, 0, 0}, - { XK_BackSpace, Mod1Mask, "\033\177", 0, 0, 0}, - { XK_Home, ShiftMask, "\033[2J", 0, -1, 0}, - { XK_Home, ShiftMask, "\033[1;2H", 0, +1, 0}, - { XK_Home, XK_ANY_MOD, "\033[H", 0, -1, 0}, - { XK_Home, XK_ANY_MOD, "\033[1~", 0, +1, 0}, - { XK_End, ControlMask, "\033[J", -1, 0, 0}, - { XK_End, ControlMask, "\033[1;5F", +1, 0, 0}, - { XK_End, ShiftMask, "\033[K", -1, 0, 0}, - { XK_End, ShiftMask, "\033[1;2F", +1, 0, 0}, - { XK_End, XK_ANY_MOD, "\033[4~", 0, 0, 0}, - { XK_Prior, ControlMask, "\033[5;5~", 0, 0, 0}, - { XK_Prior, ShiftMask, "\033[5;2~", 0, 0, 0}, - { XK_Prior, XK_ANY_MOD, "\033[5~", 0, 0, 0}, - { XK_Next, ControlMask, "\033[6;5~", 0, 0, 0}, - { XK_Next, ShiftMask, "\033[6;2~", 0, 0, 0}, - { XK_Next, XK_ANY_MOD, "\033[6~", 0, 0, 0}, - { XK_F1, XK_NO_MOD, "\033OP" , 0, 0, 0}, - { XK_F1, /* F13 */ ShiftMask, "\033[1;2P", 0, 0, 0}, - { XK_F1, /* F25 */ ControlMask, "\033[1;5P", 0, 0, 0}, - { XK_F1, /* F37 */ Mod4Mask, "\033[1;6P", 0, 0, 0}, - { XK_F1, /* F49 */ Mod1Mask, "\033[1;3P", 0, 0, 0}, - { XK_F1, /* F61 */ Mod3Mask, "\033[1;4P", 0, 0, 0}, - { XK_F2, XK_NO_MOD, "\033OQ" , 0, 0, 0}, - { XK_F2, /* F14 */ ShiftMask, "\033[1;2Q", 0, 0, 0}, - { XK_F2, /* F26 */ ControlMask, "\033[1;5Q", 0, 0, 0}, - { XK_F2, /* F38 */ Mod4Mask, "\033[1;6Q", 0, 0, 0}, - { XK_F2, /* F50 */ Mod1Mask, "\033[1;3Q", 0, 0, 0}, - { XK_F2, /* F62 */ Mod3Mask, "\033[1;4Q", 0, 0, 0}, - { XK_F3, XK_NO_MOD, "\033OR" , 0, 0, 0}, - { XK_F3, /* F15 */ ShiftMask, "\033[1;2R", 0, 0, 0}, - { XK_F3, /* F27 */ ControlMask, "\033[1;5R", 0, 0, 0}, - { XK_F3, /* F39 */ Mod4Mask, "\033[1;6R", 0, 0, 0}, - { XK_F3, /* F51 */ Mod1Mask, "\033[1;3R", 0, 0, 0}, - { XK_F3, /* F63 */ Mod3Mask, "\033[1;4R", 0, 0, 0}, - { XK_F4, XK_NO_MOD, "\033OS" , 0, 0, 0}, - { XK_F4, /* F16 */ ShiftMask, "\033[1;2S", 0, 0, 0}, - { XK_F4, /* F28 */ ControlMask, "\033[1;5S", 0, 0, 0}, - { XK_F4, /* F40 */ Mod4Mask, "\033[1;6S", 0, 0, 0}, - { XK_F4, /* F52 */ Mod1Mask, "\033[1;3S", 0, 0, 0}, - { XK_F5, XK_NO_MOD, "\033[15~", 0, 0, 0}, - { XK_F5, /* F17 */ ShiftMask, "\033[15;2~", 0, 0, 0}, - { XK_F5, /* F29 */ ControlMask, "\033[15;5~", 0, 0, 0}, - { XK_F5, /* F41 */ Mod4Mask, "\033[15;6~", 0, 0, 0}, - { XK_F5, /* F53 */ Mod1Mask, "\033[15;3~", 0, 0, 0}, - { XK_F6, XK_NO_MOD, "\033[17~", 0, 0, 0}, - { XK_F6, /* F18 */ ShiftMask, "\033[17;2~", 0, 0, 0}, - { XK_F6, /* F30 */ ControlMask, "\033[17;5~", 0, 0, 0}, - { XK_F6, /* F42 */ Mod4Mask, "\033[17;6~", 0, 0, 0}, - { XK_F6, /* F54 */ Mod1Mask, "\033[17;3~", 0, 0, 0}, - { XK_F7, XK_NO_MOD, "\033[18~", 0, 0, 0}, - { XK_F7, /* F19 */ ShiftMask, "\033[18;2~", 0, 0, 0}, - { XK_F7, /* F31 */ ControlMask, "\033[18;5~", 0, 0, 0}, - { XK_F7, /* F43 */ Mod4Mask, "\033[18;6~", 0, 0, 0}, - { XK_F7, /* F55 */ Mod1Mask, "\033[18;3~", 0, 0, 0}, - { XK_F8, XK_NO_MOD, "\033[19~", 0, 0, 0}, - { XK_F8, /* F20 */ ShiftMask, "\033[19;2~", 0, 0, 0}, - { XK_F8, /* F32 */ ControlMask, "\033[19;5~", 0, 0, 0}, - { XK_F8, /* F44 */ Mod4Mask, "\033[19;6~", 0, 0, 0}, - { XK_F8, /* F56 */ Mod1Mask, "\033[19;3~", 0, 0, 0}, - { XK_F9, XK_NO_MOD, "\033[20~", 0, 0, 0}, - { XK_F9, /* F21 */ ShiftMask, "\033[20;2~", 0, 0, 0}, - { XK_F9, /* F33 */ ControlMask, "\033[20;5~", 0, 0, 0}, - { XK_F9, /* F45 */ Mod4Mask, "\033[20;6~", 0, 0, 0}, - { XK_F9, /* F57 */ Mod1Mask, "\033[20;3~", 0, 0, 0}, - { XK_F10, XK_NO_MOD, "\033[21~", 0, 0, 0}, - { XK_F10, /* F22 */ ShiftMask, "\033[21;2~", 0, 0, 0}, - { XK_F10, /* F34 */ ControlMask, "\033[21;5~", 0, 0, 0}, - { XK_F10, /* F46 */ Mod4Mask, "\033[21;6~", 0, 0, 0}, - { XK_F10, /* F58 */ Mod1Mask, "\033[21;3~", 0, 0, 0}, - { XK_F11, XK_NO_MOD, "\033[23~", 0, 0, 0}, - { XK_F11, /* F23 */ ShiftMask, "\033[23;2~", 0, 0, 0}, - { XK_F11, /* F35 */ ControlMask, "\033[23;5~", 0, 0, 0}, - { XK_F11, /* F47 */ Mod4Mask, "\033[23;6~", 0, 0, 0}, - { XK_F11, /* F59 */ Mod1Mask, "\033[23;3~", 0, 0, 0}, - { XK_F12, XK_NO_MOD, "\033[24~", 0, 0, 0}, - { XK_F12, /* F24 */ ShiftMask, "\033[24;2~", 0, 0, 0}, - { XK_F12, /* F36 */ ControlMask, "\033[24;5~", 0, 0, 0}, - { XK_F12, /* F48 */ Mod4Mask, "\033[24;6~", 0, 0, 0}, - { XK_F12, /* F60 */ Mod1Mask, "\033[24;3~", 0, 0, 0}, - { XK_F13, XK_NO_MOD, "\033[1;2P", 0, 0, 0}, - { XK_F14, XK_NO_MOD, "\033[1;2Q", 0, 0, 0}, - { XK_F15, XK_NO_MOD, "\033[1;2R", 0, 0, 0}, - { XK_F16, XK_NO_MOD, "\033[1;2S", 0, 0, 0}, - { XK_F17, XK_NO_MOD, "\033[15;2~", 0, 0, 0}, - { XK_F18, XK_NO_MOD, "\033[17;2~", 0, 0, 0}, - { XK_F19, XK_NO_MOD, "\033[18;2~", 0, 0, 0}, - { XK_F20, XK_NO_MOD, "\033[19;2~", 0, 0, 0}, - { XK_F21, XK_NO_MOD, "\033[20;2~", 0, 0, 0}, - { XK_F22, XK_NO_MOD, "\033[21;2~", 0, 0, 0}, - { XK_F23, XK_NO_MOD, "\033[23;2~", 0, 0, 0}, - { XK_F24, XK_NO_MOD, "\033[24;2~", 0, 0, 0}, - { XK_F25, XK_NO_MOD, "\033[1;5P", 0, 0, 0}, - { XK_F26, XK_NO_MOD, "\033[1;5Q", 0, 0, 0}, - { XK_F27, XK_NO_MOD, "\033[1;5R", 0, 0, 0}, - { XK_F28, XK_NO_MOD, "\033[1;5S", 0, 0, 0}, - { XK_F29, XK_NO_MOD, "\033[15;5~", 0, 0, 0}, - { XK_F30, XK_NO_MOD, "\033[17;5~", 0, 0, 0}, - { XK_F31, XK_NO_MOD, "\033[18;5~", 0, 0, 0}, - { XK_F32, XK_NO_MOD, "\033[19;5~", 0, 0, 0}, - { XK_F33, XK_NO_MOD, "\033[20;5~", 0, 0, 0}, - { XK_F34, XK_NO_MOD, "\033[21;5~", 0, 0, 0}, - { XK_F35, XK_NO_MOD, "\033[23;5~", 0, 0, 0}, + /* keysym mask string appkey appcursor */ + { XK_KP_Home, ShiftMask, "\033[2J", 0, -1}, + { XK_KP_Home, ShiftMask, "\033[1;2H", 0, +1}, + { XK_KP_Home, XK_ANY_MOD, "\033[H", 0, -1}, + { XK_KP_Home, XK_ANY_MOD, "\033[1~", 0, +1}, + { XK_KP_Up, XK_ANY_MOD, "\033Ox", +1, 0}, + { XK_KP_Up, XK_ANY_MOD, "\033[A", 0, -1}, + { XK_KP_Up, XK_ANY_MOD, "\033OA", 0, +1}, + { XK_KP_Down, XK_ANY_MOD, "\033Or", +1, 0}, + { XK_KP_Down, XK_ANY_MOD, "\033[B", 0, -1}, + { XK_KP_Down, XK_ANY_MOD, "\033OB", 0, +1}, + { XK_KP_Left, XK_ANY_MOD, "\033Ot", +1, 0}, + { XK_KP_Left, XK_ANY_MOD, "\033[D", 0, -1}, + { XK_KP_Left, XK_ANY_MOD, "\033OD", 0, +1}, + { XK_KP_Right, XK_ANY_MOD, "\033Ov", +1, 0}, + { XK_KP_Right, XK_ANY_MOD, "\033[C", 0, -1}, + { XK_KP_Right, XK_ANY_MOD, "\033OC", 0, +1}, + { XK_KP_Prior, ShiftMask, "\033[5;2~", 0, 0}, + { XK_KP_Prior, XK_ANY_MOD, "\033[5~", 0, 0}, + { XK_KP_Begin, XK_ANY_MOD, "\033[E", 0, 0}, + { XK_KP_End, ControlMask, "\033[J", -1, 0}, + { XK_KP_End, ControlMask, "\033[1;5F", +1, 0}, + { XK_KP_End, ShiftMask, "\033[K", -1, 0}, + { XK_KP_End, ShiftMask, "\033[1;2F", +1, 0}, + { XK_KP_End, XK_ANY_MOD, "\033[4~", 0, 0}, + { XK_KP_Next, ShiftMask, "\033[6;2~", 0, 0}, + { XK_KP_Next, XK_ANY_MOD, "\033[6~", 0, 0}, + { XK_KP_Insert, ShiftMask, "\033[2;2~", +1, 0}, + { XK_KP_Insert, ShiftMask, "\033[4l", -1, 0}, + { XK_KP_Insert, ControlMask, "\033[L", -1, 0}, + { XK_KP_Insert, ControlMask, "\033[2;5~", +1, 0}, + { XK_KP_Insert, XK_ANY_MOD, "\033[4h", -1, 0}, + { XK_KP_Insert, XK_ANY_MOD, "\033[2~", +1, 0}, + { XK_KP_Delete, ControlMask, "\033[M", -1, 0}, + { XK_KP_Delete, ControlMask, "\033[3;5~", +1, 0}, + { XK_KP_Delete, ShiftMask, "\033[2K", -1, 0}, + { XK_KP_Delete, ShiftMask, "\033[3;2~", +1, 0}, + { XK_KP_Delete, XK_ANY_MOD, "\033[P", -1, 0}, + { XK_KP_Delete, XK_ANY_MOD, "\033[3~", +1, 0}, + { XK_KP_Multiply, XK_ANY_MOD, "\033Oj", +2, 0}, + { XK_KP_Add, XK_ANY_MOD, "\033Ok", +2, 0}, + { XK_KP_Enter, XK_ANY_MOD, "\033OM", +2, 0}, + { XK_KP_Enter, XK_ANY_MOD, "\r", -1, 0}, + { XK_KP_Subtract, XK_ANY_MOD, "\033Om", +2, 0}, + { XK_KP_Decimal, XK_ANY_MOD, "\033On", +2, 0}, + { XK_KP_Divide, XK_ANY_MOD, "\033Oo", +2, 0}, + { XK_KP_0, XK_ANY_MOD, "\033Op", +2, 0}, + { XK_KP_1, XK_ANY_MOD, "\033Oq", +2, 0}, + { XK_KP_2, XK_ANY_MOD, "\033Or", +2, 0}, + { XK_KP_3, XK_ANY_MOD, "\033Os", +2, 0}, + { XK_KP_4, XK_ANY_MOD, "\033Ot", +2, 0}, + { XK_KP_5, XK_ANY_MOD, "\033Ou", +2, 0}, + { XK_KP_6, XK_ANY_MOD, "\033Ov", +2, 0}, + { XK_KP_7, XK_ANY_MOD, "\033Ow", +2, 0}, + { XK_KP_8, XK_ANY_MOD, "\033Ox", +2, 0}, + { XK_KP_9, XK_ANY_MOD, "\033Oy", +2, 0}, + { XK_Up, ShiftMask, "\033[1;2A", 0, 0}, + { XK_Up, Mod1Mask, "\033[1;3A", 0, 0}, + { XK_Up, ShiftMask|Mod1Mask,"\033[1;4A", 0, 0}, + { XK_Up, ControlMask, "\033[1;5A", 0, 0}, + { XK_Up, ShiftMask|ControlMask,"\033[1;6A", 0, 0}, + { XK_Up, ControlMask|Mod1Mask,"\033[1;7A", 0, 0}, + { XK_Up,ShiftMask|ControlMask|Mod1Mask,"\033[1;8A", 0, 0}, + { XK_Up, XK_ANY_MOD, "\033[A", 0, -1}, + { XK_Up, XK_ANY_MOD, "\033OA", 0, +1}, + { XK_Down, ShiftMask, "\033[1;2B", 0, 0}, + { XK_Down, Mod1Mask, "\033[1;3B", 0, 0}, + { XK_Down, ShiftMask|Mod1Mask,"\033[1;4B", 0, 0}, + { XK_Down, ControlMask, "\033[1;5B", 0, 0}, + { XK_Down, ShiftMask|ControlMask,"\033[1;6B", 0, 0}, + { XK_Down, ControlMask|Mod1Mask,"\033[1;7B", 0, 0}, + { XK_Down,ShiftMask|ControlMask|Mod1Mask,"\033[1;8B",0, 0}, + { XK_Down, XK_ANY_MOD, "\033[B", 0, -1}, + { XK_Down, XK_ANY_MOD, "\033OB", 0, +1}, + { XK_Left, ShiftMask, "\033[1;2D", 0, 0}, + { XK_Left, Mod1Mask, "\033[1;3D", 0, 0}, + { XK_Left, ShiftMask|Mod1Mask,"\033[1;4D", 0, 0}, + { XK_Left, ControlMask, "\033[1;5D", 0, 0}, + { XK_Left, ShiftMask|ControlMask,"\033[1;6D", 0, 0}, + { XK_Left, ControlMask|Mod1Mask,"\033[1;7D", 0, 0}, + { XK_Left,ShiftMask|ControlMask|Mod1Mask,"\033[1;8D",0, 0}, + { XK_Left, XK_ANY_MOD, "\033[D", 0, -1}, + { XK_Left, XK_ANY_MOD, "\033OD", 0, +1}, + { XK_Right, ShiftMask, "\033[1;2C", 0, 0}, + { XK_Right, Mod1Mask, "\033[1;3C", 0, 0}, + { XK_Right, ShiftMask|Mod1Mask,"\033[1;4C", 0, 0}, + { XK_Right, ControlMask, "\033[1;5C", 0, 0}, + { XK_Right, ShiftMask|ControlMask,"\033[1;6C", 0, 0}, + { XK_Right, ControlMask|Mod1Mask,"\033[1;7C", 0, 0}, + { XK_Right,ShiftMask|ControlMask|Mod1Mask,"\033[1;8C",0, 0}, + { XK_Right, XK_ANY_MOD, "\033[C", 0, -1}, + { XK_Right, XK_ANY_MOD, "\033OC", 0, +1}, + { XK_ISO_Left_Tab, ShiftMask, "\033[Z", 0, 0}, + { XK_Return, Mod1Mask, "\033\r", 0, 0}, + { XK_Return, XK_ANY_MOD, "\r", 0, 0}, + { XK_Insert, ShiftMask, "\033[4l", -1, 0}, + { XK_Insert, ShiftMask, "\033[2;2~", +1, 0}, + { XK_Insert, ControlMask, "\033[L", -1, 0}, + { XK_Insert, ControlMask, "\033[2;5~", +1, 0}, + { XK_Insert, XK_ANY_MOD, "\033[4h", -1, 0}, + { XK_Insert, XK_ANY_MOD, "\033[2~", +1, 0}, + { XK_Delete, ControlMask, "\033[M", -1, 0}, + { XK_Delete, ControlMask, "\033[3;5~", +1, 0}, + { XK_Delete, ShiftMask, "\033[2K", -1, 0}, + { XK_Delete, ShiftMask, "\033[3;2~", +1, 0}, + { XK_Delete, XK_ANY_MOD, "\033[P", -1, 0}, + { XK_Delete, XK_ANY_MOD, "\033[3~", +1, 0}, + { XK_BackSpace, XK_NO_MOD, "\177", 0, 0}, + { XK_BackSpace, Mod1Mask, "\033\177", 0, 0}, + { XK_Home, ShiftMask, "\033[2J", 0, -1}, + { XK_Home, ShiftMask, "\033[1;2H", 0, +1}, + { XK_Home, XK_ANY_MOD, "\033[H", 0, -1}, + { XK_Home, XK_ANY_MOD, "\033[1~", 0, +1}, + { XK_End, ControlMask, "\033[J", -1, 0}, + { XK_End, ControlMask, "\033[1;5F", +1, 0}, + { XK_End, ShiftMask, "\033[K", -1, 0}, + { XK_End, ShiftMask, "\033[1;2F", +1, 0}, + { XK_End, XK_ANY_MOD, "\033[4~", 0, 0}, + { XK_Prior, ControlMask, "\033[5;5~", 0, 0}, + { XK_Prior, ShiftMask, "\033[5;2~", 0, 0}, + { XK_Prior, XK_ANY_MOD, "\033[5~", 0, 0}, + { XK_Next, ControlMask, "\033[6;5~", 0, 0}, + { XK_Next, ShiftMask, "\033[6;2~", 0, 0}, + { XK_Next, XK_ANY_MOD, "\033[6~", 0, 0}, + { XK_F1, XK_NO_MOD, "\033OP" , 0, 0}, + { XK_F1, /* F13 */ ShiftMask, "\033[1;2P", 0, 0}, + { XK_F1, /* F25 */ ControlMask, "\033[1;5P", 0, 0}, + { XK_F1, /* F37 */ Mod4Mask, "\033[1;6P", 0, 0}, + { XK_F1, /* F49 */ Mod1Mask, "\033[1;3P", 0, 0}, + { XK_F1, /* F61 */ Mod3Mask, "\033[1;4P", 0, 0}, + { XK_F2, XK_NO_MOD, "\033OQ" , 0, 0}, + { XK_F2, /* F14 */ ShiftMask, "\033[1;2Q", 0, 0}, + { XK_F2, /* F26 */ ControlMask, "\033[1;5Q", 0, 0}, + { XK_F2, /* F38 */ Mod4Mask, "\033[1;6Q", 0, 0}, + { XK_F2, /* F50 */ Mod1Mask, "\033[1;3Q", 0, 0}, + { XK_F2, /* F62 */ Mod3Mask, "\033[1;4Q", 0, 0}, + { XK_F3, XK_NO_MOD, "\033OR" , 0, 0}, + { XK_F3, /* F15 */ ShiftMask, "\033[1;2R", 0, 0}, + { XK_F3, /* F27 */ ControlMask, "\033[1;5R", 0, 0}, + { XK_F3, /* F39 */ Mod4Mask, "\033[1;6R", 0, 0}, + { XK_F3, /* F51 */ Mod1Mask, "\033[1;3R", 0, 0}, + { XK_F3, /* F63 */ Mod3Mask, "\033[1;4R", 0, 0}, + { XK_F4, XK_NO_MOD, "\033OS" , 0, 0}, + { XK_F4, /* F16 */ ShiftMask, "\033[1;2S", 0, 0}, + { XK_F4, /* F28 */ ControlMask, "\033[1;5S", 0, 0}, + { XK_F4, /* F40 */ Mod4Mask, "\033[1;6S", 0, 0}, + { XK_F4, /* F52 */ Mod1Mask, "\033[1;3S", 0, 0}, + { XK_F5, XK_NO_MOD, "\033[15~", 0, 0}, + { XK_F5, /* F17 */ ShiftMask, "\033[15;2~", 0, 0}, + { XK_F5, /* F29 */ ControlMask, "\033[15;5~", 0, 0}, + { XK_F5, /* F41 */ Mod4Mask, "\033[15;6~", 0, 0}, + { XK_F5, /* F53 */ Mod1Mask, "\033[15;3~", 0, 0}, + { XK_F6, XK_NO_MOD, "\033[17~", 0, 0}, + { XK_F6, /* F18 */ ShiftMask, "\033[17;2~", 0, 0}, + { XK_F6, /* F30 */ ControlMask, "\033[17;5~", 0, 0}, + { XK_F6, /* F42 */ Mod4Mask, "\033[17;6~", 0, 0}, + { XK_F6, /* F54 */ Mod1Mask, "\033[17;3~", 0, 0}, + { XK_F7, XK_NO_MOD, "\033[18~", 0, 0}, + { XK_F7, /* F19 */ ShiftMask, "\033[18;2~", 0, 0}, + { XK_F7, /* F31 */ ControlMask, "\033[18;5~", 0, 0}, + { XK_F7, /* F43 */ Mod4Mask, "\033[18;6~", 0, 0}, + { XK_F7, /* F55 */ Mod1Mask, "\033[18;3~", 0, 0}, + { XK_F8, XK_NO_MOD, "\033[19~", 0, 0}, + { XK_F8, /* F20 */ ShiftMask, "\033[19;2~", 0, 0}, + { XK_F8, /* F32 */ ControlMask, "\033[19;5~", 0, 0}, + { XK_F8, /* F44 */ Mod4Mask, "\033[19;6~", 0, 0}, + { XK_F8, /* F56 */ Mod1Mask, "\033[19;3~", 0, 0}, + { XK_F9, XK_NO_MOD, "\033[20~", 0, 0}, + { XK_F9, /* F21 */ ShiftMask, "\033[20;2~", 0, 0}, + { XK_F9, /* F33 */ ControlMask, "\033[20;5~", 0, 0}, + { XK_F9, /* F45 */ Mod4Mask, "\033[20;6~", 0, 0}, + { XK_F9, /* F57 */ Mod1Mask, "\033[20;3~", 0, 0}, + { XK_F10, XK_NO_MOD, "\033[21~", 0, 0}, + { XK_F10, /* F22 */ ShiftMask, "\033[21;2~", 0, 0}, + { XK_F10, /* F34 */ ControlMask, "\033[21;5~", 0, 0}, + { XK_F10, /* F46 */ Mod4Mask, "\033[21;6~", 0, 0}, + { XK_F10, /* F58 */ Mod1Mask, "\033[21;3~", 0, 0}, + { XK_F11, XK_NO_MOD, "\033[23~", 0, 0}, + { XK_F11, /* F23 */ ShiftMask, "\033[23;2~", 0, 0}, + { XK_F11, /* F35 */ ControlMask, "\033[23;5~", 0, 0}, + { XK_F11, /* F47 */ Mod4Mask, "\033[23;6~", 0, 0}, + { XK_F11, /* F59 */ Mod1Mask, "\033[23;3~", 0, 0}, + { XK_F12, XK_NO_MOD, "\033[24~", 0, 0}, + { XK_F12, /* F24 */ ShiftMask, "\033[24;2~", 0, 0}, + { XK_F12, /* F36 */ ControlMask, "\033[24;5~", 0, 0}, + { XK_F12, /* F48 */ Mod4Mask, "\033[24;6~", 0, 0}, + { XK_F12, /* F60 */ Mod1Mask, "\033[24;3~", 0, 0}, + { XK_F13, XK_NO_MOD, "\033[1;2P", 0, 0}, + { XK_F14, XK_NO_MOD, "\033[1;2Q", 0, 0}, + { XK_F15, XK_NO_MOD, "\033[1;2R", 0, 0}, + { XK_F16, XK_NO_MOD, "\033[1;2S", 0, 0}, + { XK_F17, XK_NO_MOD, "\033[15;2~", 0, 0}, + { XK_F18, XK_NO_MOD, "\033[17;2~", 0, 0}, + { XK_F19, XK_NO_MOD, "\033[18;2~", 0, 0}, + { XK_F20, XK_NO_MOD, "\033[19;2~", 0, 0}, + { XK_F21, XK_NO_MOD, "\033[20;2~", 0, 0}, + { XK_F22, XK_NO_MOD, "\033[21;2~", 0, 0}, + { XK_F23, XK_NO_MOD, "\033[23;2~", 0, 0}, + { XK_F24, XK_NO_MOD, "\033[24;2~", 0, 0}, + { XK_F25, XK_NO_MOD, "\033[1;5P", 0, 0}, + { XK_F26, XK_NO_MOD, "\033[1;5Q", 0, 0}, + { XK_F27, XK_NO_MOD, "\033[1;5R", 0, 0}, + { XK_F28, XK_NO_MOD, "\033[1;5S", 0, 0}, + { XK_F29, XK_NO_MOD, "\033[15;5~", 0, 0}, + { XK_F30, XK_NO_MOD, "\033[17;5~", 0, 0}, + { XK_F31, XK_NO_MOD, "\033[18;5~", 0, 0}, + { XK_F32, XK_NO_MOD, "\033[19;5~", 0, 0}, + { XK_F33, XK_NO_MOD, "\033[20;5~", 0, 0}, + { XK_F34, XK_NO_MOD, "\033[21;5~", 0, 0}, + { XK_F35, XK_NO_MOD, "\033[23;5~", 0, 0}, }; /* diff --git a/st.c b/st.c index 7d546da..3ebf8c6 100644 --- a/st.c +++ b/st.c @@ -108,6 +108,7 @@ typedef struct { static void execsh(char **); static void stty(char **); static void sigchld(int); +static void ttywriteraw(const char *, size_t); static void csidump(void); static void csihandle(void); @@ -786,13 +787,38 @@ ttyread(void) void ttywrite(const char *s, size_t n, int may_echo) { - fd_set wfd, rfd; - ssize_t r; - size_t lim = 256; + const char *next; if (may_echo && IS_SET(MODE_ECHO)) twrite(s, n, 1); + if (!IS_SET(MODE_CRLF)) { + ttywriteraw(s, n); + return; + } + + /* This is similar to how the kernel handles ONLCR for ttys */ + while (n > 0) { + if (*s == '\r') { + next = s + 1; + ttywriteraw("\r\n", 2); + } else { + next = memchr(s, '\r', n); + DEFAULT(next, s + n); + ttywriteraw(s, next - s); + } + n -= next - s; + s = next; + } +} + +void +ttywriteraw(const char *s, size_t n) +{ + fd_set wfd, rfd; + ssize_t r; + size_t lim = 256; + /* * Remember that we are using a pty, which might be a modem line. * Writing too much will clog the line. That's why we are doing this diff --git a/x.c b/x.c index 49a22e4..76fb910 100644 --- a/x.c +++ b/x.c @@ -38,10 +38,9 @@ typedef struct { KeySym k; uint mask; char *s; - /* three valued logic variables: 0 indifferent, 1 on, -1 off */ + /* three-valued logic variables: 0 indifferent, 1 on, -1 off */ signed char appkey; /* application keypad */ signed char appcursor; /* application cursor */ - signed char crlf; /* crlf mode */ } Key; /* X modifiers */ @@ -1680,9 +1679,6 @@ kmap(KeySym k, uint state) if (IS_SET(MODE_APPCURSOR) ? kp->appcursor < 0 : kp->appcursor > 0) continue; - if (IS_SET(MODE_CRLF) ? kp->crlf < 0 : kp->crlf > 0) - continue; - return kp->s; } From 05c66cb37d9ff278a3e0c45682c4b5e7945deb42 Mon Sep 17 00:00:00 2001 From: "Devin J. Pohly" Date: Fri, 23 Feb 2018 14:16:52 -0600 Subject: [PATCH 049/185] Split mode bits between Term and TermWindow Moves the mode bits used by x.c from Term to TermWindow, absorbing UI/input-related mode bits (visible/focused/numlock) along the way. This is gradually reducing external references to Term. Since TermWindow is already internal to x.c, we add xsetmode() to allow st to modify window bits in accordance with escape sequences. IS_SET() is redefined accordingly (term.mode in st.c, win.mode in x.c). Signed-off-by: Devin J. Pohly --- st.c | 64 +++++++++++++++++++++++++++++------------------------------ st.h | 33 +----------------------------- win.h | 24 ++++++++++++++++++++++ x.c | 50 ++++++++++++++++++++++++++++------------------ 4 files changed, 88 insertions(+), 83 deletions(-) diff --git a/st.c b/st.c index 3ebf8c6..01791a5 100644 --- a/st.c +++ b/st.c @@ -42,6 +42,7 @@ #define STR_ARG_SIZ ESC_ARG_SIZ /* macros */ +#define IS_SET(flag) ((term.mode & (flag)) != 0) #define NUMMAXLEN(x) ((int)(sizeof(x) * 2.56 + 0.5) + 1) #define ISCONTROLC0(c) (BETWEEN(c, 0, 0x1f) || (c) == '\177') #define ISCONTROLC1(c) (BETWEEN(c, 0x80, 0x9f)) @@ -51,6 +52,17 @@ /* constants */ #define ISO14755CMD "dmenu -w \"$WINDOWID\" -p codepoint: ': /* DECPNM -- Normal keypad */ - term.mode &= ~MODE_APPKEYPAD; + xsetmode(0, MODE_APPKEYPAD); break; case '7': /* DECSC -- Save Cursor */ tcursor(CURSOR_SAVE); @@ -2526,9 +2532,3 @@ redraw(void) tfulldirt(); draw(); } - -void -numlock(const Arg *dummy) -{ - term.numlock ^= 1; -} diff --git a/st.h b/st.h index d7738a0..3382d61 100644 --- a/st.h +++ b/st.h @@ -13,7 +13,6 @@ #define LIMIT(x, a, b) (x) = (x) < (a) ? (a) : (x) > (b) ? (b) : (x) #define ATTRCMP(a, b) ((a).mode != (b).mode || (a).fg != (b).fg || \ (a).bg != (b).bg) -#define IS_SET(flag) ((term.mode & (flag)) != 0) #define TIMEDIFF(t1, t2) ((t1.tv_sec-t2.tv_sec)*1000 + \ (t1.tv_nsec-t2.tv_nsec)/1E6) #define MODBIT(x, set, bit) ((set) ? ((x) |= (bit)) : ((x) &= ~(bit))) @@ -37,34 +36,6 @@ enum glyph_attribute { ATTR_BOLD_FAINT = ATTR_BOLD | ATTR_FAINT, }; -enum term_mode { - MODE_WRAP = 1 << 0, - MODE_INSERT = 1 << 1, - MODE_APPKEYPAD = 1 << 2, - MODE_ALTSCREEN = 1 << 3, - MODE_CRLF = 1 << 4, - MODE_MOUSEBTN = 1 << 5, - MODE_MOUSEMOTION = 1 << 6, - MODE_REVERSE = 1 << 7, - MODE_KBDLOCK = 1 << 8, - MODE_HIDE = 1 << 9, - MODE_ECHO = 1 << 10, - MODE_APPCURSOR = 1 << 11, - MODE_MOUSESGR = 1 << 12, - MODE_8BIT = 1 << 13, - MODE_BLINK = 1 << 14, - MODE_FBLINK = 1 << 15, - MODE_FOCUS = 1 << 16, - MODE_MOUSEX10 = 1 << 17, - MODE_MOUSEMANY = 1 << 18, - MODE_BRCKTPASTE = 1 << 19, - MODE_PRINT = 1 << 20, - MODE_UTF8 = 1 << 21, - MODE_SIXEL = 1 << 22, - MODE_MOUSE = MODE_MOUSEBTN|MODE_MOUSEMOTION|MODE_MOUSEX10\ - |MODE_MOUSEMANY, -}; - enum selection_mode { SEL_IDLE = 0, SEL_EMPTY = 1, @@ -120,7 +91,6 @@ typedef struct { char trantbl[4]; /* charset table translation */ int charset; /* current charset */ int icharset; /* selected charset for sequence */ - int numlock; /* lock numbers in keyboard */ int *tabs; } Term; @@ -130,7 +100,7 @@ typedef struct { int w, h; /* window width and height */ int ch; /* char height */ int cw; /* char width */ - char state; /* focus, redraw, visible */ + int mode; /* window state/mode flags */ int cursor; /* cursor style */ } TermWindow; @@ -163,7 +133,6 @@ void die(const char *, ...); void redraw(void); void iso14755(const Arg *); -void numlock(const Arg *); void printscreen(const Arg *); void printsel(const Arg *); void sendbreak(const Arg *); diff --git a/win.h b/win.h index 123662e..1e08b16 100644 --- a/win.h +++ b/win.h @@ -1,5 +1,28 @@ /* See LICENSE for license details. */ +enum win_mode { + MODE_VISIBLE = 1 << 0, + MODE_FOCUSED = 1 << 1, + MODE_APPKEYPAD = 1 << 2, + MODE_MOUSEBTN = 1 << 3, + MODE_MOUSEMOTION = 1 << 4, + MODE_REVERSE = 1 << 5, + MODE_KBDLOCK = 1 << 6, + MODE_HIDE = 1 << 7, + MODE_APPCURSOR = 1 << 8, + MODE_MOUSESGR = 1 << 9, + MODE_8BIT = 1 << 10, + MODE_BLINK = 1 << 11, + MODE_FBLINK = 1 << 12, + MODE_FOCUS = 1 << 13, + MODE_MOUSEX10 = 1 << 14, + MODE_MOUSEMANY = 1 << 15, + MODE_BRCKTPASTE = 1 << 16, + MODE_NUMLOCK = 1 << 17, + MODE_MOUSE = MODE_MOUSEBTN|MODE_MOUSEMOTION|MODE_MOUSEX10\ + |MODE_MOUSEMANY, +}; + void draw(void); void drawregion(int, int, int, int); @@ -10,5 +33,6 @@ void xloadcols(void); int xsetcolorname(int, const char *); void xsettitle(char *); int xsetcursor(int); +void xsetmode(int, unsigned int); void xsetpointermotion(int); void xsetsel(char *); diff --git a/x.c b/x.c index 76fb910..c5826cf 100644 --- a/x.c +++ b/x.c @@ -51,6 +51,7 @@ typedef struct { /* function definitions used in config.h */ static void clipcopy(const Arg *); static void clippaste(const Arg *); +static void numlock(const Arg *); static void selpaste(const Arg *); static void zoom(const Arg *); static void zoomabs(const Arg *); @@ -64,6 +65,7 @@ static void zoomreset(const Arg *); #define XEMBED_FOCUS_OUT 5 /* macros */ +#define IS_SET(flag) ((win.mode & (flag)) != 0) #define TRUERED(x) (((x) & 0xff0000) >> 8) #define TRUEGREEN(x) (((x) & 0xff00)) #define TRUEBLUE(x) (((x) & 0xff) << 8) @@ -196,11 +198,6 @@ static XWindow xw; static XSelection xsel; static TermWindow win; -enum window_state { - WIN_VISIBLE = 1, - WIN_FOCUSED = 2 -}; - /* Font Ring Cache */ enum { FRC_NORMAL, @@ -263,6 +260,12 @@ selpaste(const Arg *dummy) xw.win, CurrentTime); } +void +numlock(const Arg *dummy) +{ + win.mode ^= MODE_NUMLOCK; +} + void zoom(const Arg *arg) { @@ -1090,6 +1093,7 @@ xinit(void) XChangeProperty(xw.dpy, xw.win, xw.netwmpid, XA_CARDINAL, 32, PropModeReplace, (uchar *)&thispid, 1); + win.mode = MODE_NUMLOCK; resettitle(); XMapWindow(xw.dpy, xw.win); xhints(); @@ -1319,14 +1323,13 @@ xdrawglyphfontspecs(const XftGlyphFontSpec *specs, Glyph base, int len, int x, i fg = &revfg; } - if (base.mode & ATTR_REVERSE) { temp = fg; fg = bg; bg = temp; } - if (base.mode & ATTR_BLINK && term.mode & MODE_BLINK) + if (base.mode & ATTR_BLINK && win.mode & MODE_BLINK) fg = bg; if (base.mode & ATTR_INVISIBLE) @@ -1440,7 +1443,7 @@ xdrawcursor(void) return; /* draw the new one */ - if (win.state & WIN_FOCUSED) { + if (IS_SET(MODE_FOCUSED)) { switch (win.cursor) { case 7: /* st extension: snowman */ utf8decode("☃", &g.u, UTF_SIZ); @@ -1527,7 +1530,7 @@ drawregion(int x1, int y1, int x2, int y2) Glyph base, new; XftGlyphFontSpec *specs; - if (!(win.state & WIN_VISIBLE)) + if (!(IS_SET(MODE_VISIBLE))) return; for (y = y1; y < y2; y++) { @@ -1575,13 +1578,13 @@ visibility(XEvent *ev) { XVisibilityEvent *e = &ev->xvisibility; - MODBIT(win.state, e->state != VisibilityFullyObscured, WIN_VISIBLE); + MODBIT(win.mode, e->state != VisibilityFullyObscured, MODE_VISIBLE); } void unmap(XEvent *ev) { - win.state &= ~WIN_VISIBLE; + win.mode &= ~MODE_VISIBLE; } void @@ -1591,6 +1594,15 @@ xsetpointermotion(int set) XChangeWindowAttributes(xw.dpy, xw.win, CWEventMask, &xw.attrs); } +void +xsetmode(int set, unsigned int flags) +{ + int mode = win.mode; + MODBIT(win.mode, set, flags); + if ((win.mode & MODE_REVERSE) != (mode & MODE_REVERSE)) + redraw(); +} + int xsetcursor(int cursor) { @@ -1614,7 +1626,7 @@ xseturgency(int add) void xbell(void) { - if (!(win.state & WIN_FOCUSED)) + if (!(IS_SET(MODE_FOCUSED))) xseturgency(1); if (bellvolume) XkbBell(xw.dpy, xw.win, bellvolume, (Atom)NULL); @@ -1630,13 +1642,13 @@ focus(XEvent *ev) if (ev->type == FocusIn) { XSetICFocus(xw.xic); - win.state |= WIN_FOCUSED; + win.mode |= MODE_FOCUSED; xseturgency(0); if (IS_SET(MODE_FOCUS)) ttywrite("\033[I", 3, 0); } else { XUnsetICFocus(xw.xic); - win.state &= ~WIN_FOCUSED; + win.mode &= ~MODE_FOCUSED; if (IS_SET(MODE_FOCUS)) ttywrite("\033[O", 3, 0); } @@ -1673,7 +1685,7 @@ kmap(KeySym k, uint state) if (IS_SET(MODE_APPKEYPAD) ? kp->appkey < 0 : kp->appkey > 0) continue; - if (term.numlock && kp->appkey == 2) + if (IS_SET(MODE_NUMLOCK) && kp->appkey == 2) continue; if (IS_SET(MODE_APPCURSOR) ? kp->appcursor < 0 : kp->appcursor > 0) @@ -1742,10 +1754,10 @@ cmessage(XEvent *e) */ if (e->xclient.message_type == xw.xembed && e->xclient.format == 32) { if (e->xclient.data.l[1] == XEMBED_FOCUS_IN) { - win.state |= WIN_FOCUSED; + win.mode |= MODE_FOCUSED; xseturgency(0); } else if (e->xclient.data.l[1] == XEMBED_FOCUS_OUT) { - win.state &= ~WIN_FOCUSED; + win.mode &= ~MODE_FOCUSED; } } else if (e->xclient.data.l[0] == xw.wmdeletewin) { /* Send SIGHUP to shell */ @@ -1810,7 +1822,7 @@ run(void) if (blinktimeout) { blinkset = tattrset(ATTR_BLINK); if (!blinkset) - MODBIT(term.mode, 0, MODE_BLINK); + MODBIT(win.mode, 0, MODE_BLINK); } } @@ -1825,7 +1837,7 @@ run(void) dodraw = 0; if (blinktimeout && TIMEDIFF(now, lastblink) > blinktimeout) { tsetdirtattr(ATTR_BLINK); - term.mode ^= MODE_BLINK; + win.mode ^= MODE_BLINK; lastblink = now; dodraw = 1; } From 88d8293fb4ba150a5f19d58d133b5db93d9dcfa5 Mon Sep 17 00:00:00 2001 From: "Devin J. Pohly" Date: Sat, 24 Feb 2018 14:53:23 -0600 Subject: [PATCH 050/185] Move win-agnostic parts of draw/drawregion to st.c Introduces three functions to encapsulate X-specific behavior: * xdrawline: draws a portion of a single line (used by drawregion) * xbegindraw: called to prepare for drawing (will be useful for e.g. Wayland) and returns true if drawing should happen * xfinishdraw: called to finish drawing (used by draw) Signed-off-by: Devin J. Pohly --- st.c | 25 +++++++++++++++++ st.h | 1 + win.h | 7 ++--- x.c | 87 +++++++++++++++++++++++++++-------------------------------- 4 files changed, 69 insertions(+), 51 deletions(-) diff --git a/st.c b/st.c index 01791a5..504239e 100644 --- a/st.c +++ b/st.c @@ -166,6 +166,8 @@ static int32_t tdefcolor(int *, int *, int); static void tdeftran(char); static void tstrsequence(uchar); +static void drawregion(int, int, int, int); + static void selscroll(int, int); static void selsnap(int *, int *, int); @@ -2526,6 +2528,29 @@ resettitle(void) xsettitle(NULL); } +void +drawregion(int x1, int y1, int x2, int y2) +{ + int y; + for (y = y1; y < y2; y++) { + if (!term.dirty[y]) + continue; + + term.dirty[y] = 0; + xdrawline(term.line[y], x1, y, x2); + } +} + +void +draw(void) +{ + if (!xstartdraw()) + return; + drawregion(0, 0, term.col, term.row); + xdrawcursor(); + xfinishdraw(); +} + void redraw(void) { diff --git a/st.h b/st.h index 3382d61..7026de8 100644 --- a/st.h +++ b/st.h @@ -131,6 +131,7 @@ typedef union { void die(const char *, ...); void redraw(void); +void draw(void); void iso14755(const Arg *); void printscreen(const Arg *); diff --git a/win.h b/win.h index 1e08b16..6e662af 100644 --- a/win.h +++ b/win.h @@ -23,12 +23,12 @@ enum win_mode { |MODE_MOUSEMANY, }; -void draw(void); -void drawregion(int, int, int, int); - void xbell(void); void xclipcopy(void); +void xdrawcursor(void); +void xdrawline(Line, int, int, int); void xhints(void); +void xfinishdraw(void); void xloadcols(void); int xsetcolorname(int, const char *); void xsettitle(char *); @@ -36,3 +36,4 @@ int xsetcursor(int); void xsetmode(int, unsigned int); void xsetpointermotion(int); void xsetsel(char *); +int xstartdraw(void); diff --git a/x.c b/x.c index c5826cf..96944ee 100644 --- a/x.c +++ b/x.c @@ -129,7 +129,6 @@ static int xmakeglyphfontspecs(XftGlyphFontSpec *, const Glyph *, int, int, int) static void xdrawglyphfontspecs(const XftGlyphFontSpec *, Glyph, int, int, int); static void xdrawglyph(Glyph, int, int); static void xclear(int, int, int, int); -static void xdrawcursor(void); static int xgeommasktogravity(int); static void xinit(void); static void cresize(int, int); @@ -1512,10 +1511,46 @@ xsettitle(char *p) XFree(prop.value); } -void -draw(void) +int +xstartdraw(void) +{ + return IS_SET(MODE_VISIBLE); +} + +void +xdrawline(Line line, int x1, int y1, int x2) +{ + int i, x, ox, numspecs; + Glyph base, new; + XftGlyphFontSpec *specs = xw.specbuf; + + numspecs = xmakeglyphfontspecs(specs, &line[x1], x2 - x1, x1, y1); + i = ox = 0; + for (x = x1; x < x2 && i < numspecs; x++) { + new = line[x]; + if (new.mode == ATTR_WDUMMY) + continue; + if (selected(x, y1)) + new.mode ^= ATTR_REVERSE; + if (i > 0 && ATTRCMP(base, new)) { + xdrawglyphfontspecs(specs, base, i, ox, y1); + specs += i; + numspecs -= i; + i = 0; + } + if (i == 0) { + ox = x; + base = new; + } + i++; + } + if (i > 0) + xdrawglyphfontspecs(specs, base, i, ox, y1); +} + +void +xfinishdraw(void) { - drawregion(0, 0, term.col, term.row); XCopyArea(xw.dpy, xw.buf, xw.win, dc.gc, 0, 0, win.w, win.h, 0, 0); XSetForeground(xw.dpy, dc.gc, @@ -1523,50 +1558,6 @@ draw(void) defaultfg : defaultbg].pixel); } -void -drawregion(int x1, int y1, int x2, int y2) -{ - int i, x, y, ox, numspecs; - Glyph base, new; - XftGlyphFontSpec *specs; - - if (!(IS_SET(MODE_VISIBLE))) - return; - - for (y = y1; y < y2; y++) { - if (!term.dirty[y]) - continue; - - term.dirty[y] = 0; - - specs = xw.specbuf; - numspecs = xmakeglyphfontspecs(specs, &term.line[y][x1], x2 - x1, x1, y); - - i = ox = 0; - for (x = x1; x < x2 && i < numspecs; x++) { - new = term.line[y][x]; - if (new.mode == ATTR_WDUMMY) - continue; - if (selected(x, y)) - new.mode ^= ATTR_REVERSE; - if (i > 0 && ATTRCMP(base, new)) { - xdrawglyphfontspecs(specs, base, i, ox, y); - specs += i; - numspecs -= i; - i = 0; - } - if (i == 0) { - ox = x; - base = new; - } - i++; - } - if (i > 0) - xdrawglyphfontspecs(specs, base, i, ox, y); - } - xdrawcursor(); -} - void expose(XEvent *ev) { From a5dc1b46976b2252f9d7bb68f126c4b0f351dd1a Mon Sep 17 00:00:00 2001 From: "Devin J. Pohly" Date: Sat, 24 Feb 2018 14:58:54 -0600 Subject: [PATCH 051/185] Pull term references out of xdrawcursor Gradually reducing x.c dependency on Term object. Old and new cursor glyph/position are passed to xdrawcursor. (There may be an opportunity to refactor further if we can unify "clear old cursor" and "draw new cursor" functionality.) Signed-off-by: Devin J. Pohly --- st.c | 15 ++++++++++++++- st.h | 4 +++- win.h | 2 +- x.c | 61 +++++++++++++++++++++-------------------------------------- 4 files changed, 40 insertions(+), 42 deletions(-) diff --git a/st.c b/st.c index 504239e..4bf6378 100644 --- a/st.c +++ b/st.c @@ -2544,10 +2544,23 @@ drawregion(int x1, int y1, int x2, int y2) void draw(void) { + int cx = term.c.x; + if (!xstartdraw()) return; + + /* adjust cursor position */ + LIMIT(term.ocx, 0, term.col-1); + LIMIT(term.ocy, 0, term.row-1); + if (term.line[term.ocy][term.ocx].mode & ATTR_WDUMMY) + term.ocx--; + if (term.line[term.c.y][cx].mode & ATTR_WDUMMY) + cx--; + drawregion(0, 0, term.col, term.row); - xdrawcursor(); + xdrawcursor(cx, term.c.y, term.line[term.c.y][cx], + term.ocx, term.ocy, term.line[term.ocy][term.ocx]); + term.ocx = cx, term.ocy = term.c.y; xfinishdraw(); } diff --git a/st.h b/st.h index 7026de8..27c48cf 100644 --- a/st.h +++ b/st.h @@ -82,8 +82,10 @@ typedef struct { int col; /* nb col */ Line *line; /* screen */ Line *alt; /* alternate screen */ - int *dirty; /* dirtyness of lines */ + int *dirty; /* dirtyness of lines */ TCursor c; /* cursor */ + int ocx; /* old cursor col */ + int ocy; /* old cursor row */ int top; /* top scroll limit */ int bot; /* bottom scroll limit */ int mode; /* terminal mode flags */ diff --git a/win.h b/win.h index 6e662af..7a866fd 100644 --- a/win.h +++ b/win.h @@ -25,7 +25,7 @@ enum win_mode { void xbell(void); void xclipcopy(void); -void xdrawcursor(void); +void xdrawcursor(int, int, Glyph, int, int, Glyph); void xdrawline(Line, int, int, int); void xhints(void); void xfinishdraw(void); diff --git a/x.c b/x.c index 96944ee..d205ca7 100644 --- a/x.c +++ b/x.c @@ -1387,41 +1387,26 @@ xdrawglyph(Glyph g, int x, int y) } void -xdrawcursor(void) +xdrawcursor(int cx, int cy, Glyph g, int ox, int oy, Glyph og) { - static int oldx = 0, oldy = 0; - int curx; - Glyph g = {' ', ATTR_NULL, defaultbg, defaultcs}, og; Color drawcol; - LIMIT(oldx, 0, term.col-1); - LIMIT(oldy, 0, term.row-1); - - curx = term.c.x; - - /* adjust position if in dummy */ - if (term.line[oldy][oldx].mode & ATTR_WDUMMY) - oldx--; - if (term.line[term.c.y][curx].mode & ATTR_WDUMMY) - curx--; - /* remove the old cursor */ - og = term.line[oldy][oldx]; - if (selected(oldx, oldy)) + if (selected(ox, oy)) og.mode ^= ATTR_REVERSE; - xdrawglyph(og, oldx, oldy); - - g.u = term.line[term.c.y][term.c.x].u; - g.mode |= term.line[term.c.y][term.c.x].mode & - (ATTR_BOLD | ATTR_ITALIC | ATTR_UNDERLINE | ATTR_STRUCK); + xdrawglyph(og, ox, oy); /* * Select the right color for the right mode. */ + g.mode &= ATTR_BOLD|ATTR_ITALIC|ATTR_UNDERLINE|ATTR_STRUCK|ATTR_WIDE; + g.fg = defaultbg; + g.bg = defaultcs; + if (IS_SET(MODE_REVERSE)) { g.mode |= ATTR_REVERSE; g.bg = defaultfg; - if (selected(term.c.x, term.c.y)) { + if (selected(cx, cy)) { drawcol = dc.col[defaultcs]; g.fg = defaultrcs; } else { @@ -1429,7 +1414,7 @@ xdrawcursor(void) g.fg = defaultcs; } } else { - if (selected(term.c.x, term.c.y)) { + if (selected(cx, cy)) { drawcol = dc.col[defaultrcs]; g.fg = defaultfg; g.bg = defaultrcs; @@ -1449,44 +1434,42 @@ xdrawcursor(void) case 0: /* Blinking Block */ case 1: /* Blinking Block (Default) */ case 2: /* Steady Block */ - g.mode |= term.line[term.c.y][curx].mode & ATTR_WIDE; - xdrawglyph(g, term.c.x, term.c.y); + xdrawglyph(g, cx, cy); break; case 3: /* Blinking Underline */ case 4: /* Steady Underline */ XftDrawRect(xw.draw, &drawcol, - borderpx + curx * win.cw, - borderpx + (term.c.y + 1) * win.ch - \ + borderpx + cx * win.cw, + borderpx + (cy + 1) * win.ch - \ cursorthickness, win.cw, cursorthickness); break; case 5: /* Blinking bar */ case 6: /* Steady bar */ XftDrawRect(xw.draw, &drawcol, - borderpx + curx * win.cw, - borderpx + term.c.y * win.ch, + borderpx + cx * win.cw, + borderpx + cy * win.ch, cursorthickness, win.ch); break; } } else { XftDrawRect(xw.draw, &drawcol, - borderpx + curx * win.cw, - borderpx + term.c.y * win.ch, + borderpx + cx * win.cw, + borderpx + cy * win.ch, win.cw - 1, 1); XftDrawRect(xw.draw, &drawcol, - borderpx + curx * win.cw, - borderpx + term.c.y * win.ch, + borderpx + cx * win.cw, + borderpx + cy * win.ch, 1, win.ch - 1); XftDrawRect(xw.draw, &drawcol, - borderpx + (curx + 1) * win.cw - 1, - borderpx + term.c.y * win.ch, + borderpx + (cx + 1) * win.cw - 1, + borderpx + cy * win.ch, 1, win.ch - 1); XftDrawRect(xw.draw, &drawcol, - borderpx + curx * win.cw, - borderpx + (term.c.y + 1) * win.ch - 1, + borderpx + cx * win.cw, + borderpx + (cy + 1) * win.ch - 1, win.cw, 1); } - oldx = curx, oldy = term.c.y; } void From a3beb626d2dae9d4d0883c7c8cb6ba58b0609105 Mon Sep 17 00:00:00 2001 From: "Devin J. Pohly" Date: Sat, 24 Feb 2018 15:32:48 -0600 Subject: [PATCH 052/185] Remove x.c dependency on term The xinit function only needs to the rows/cols, so pass those in rather than accessing term directly. With a bit of arithmetic, we are able to avoid the need for term.row and term.col in x2col, y2row, and xdrawglyphfontspecs as well, completing the removal. Term is now fully internal to st.c. Signed-off-by: Devin J. Pohly --- st.c | 22 +++++++++++++++++++++- st.h | 21 --------------------- x.c | 35 ++++++++++++++++++----------------- 3 files changed, 39 insertions(+), 39 deletions(-) diff --git a/st.c b/st.c index 4bf6378..da832ed 100644 --- a/st.c +++ b/st.c @@ -95,6 +95,26 @@ enum escape_state { ESC_DCS =128, }; +/* Internal representation of the screen */ +typedef struct { + int row; /* nb row */ + int col; /* nb col */ + Line *line; /* screen */ + Line *alt; /* alternate screen */ + int *dirty; /* dirtyness of lines */ + TCursor c; /* cursor */ + int ocx; /* old cursor col */ + int ocy; /* old cursor row */ + int top; /* top scroll limit */ + int bot; /* bottom scroll limit */ + int mode; /* terminal mode flags */ + int esc; /* escape state flags */ + char trantbl[4]; /* charset table translation */ + int charset; /* current charset */ + int icharset; /* selected charset for sequence */ + int *tabs; +} Term; + /* CSI Escape sequence structs */ /* ESC '[' [[ [] [;]] []] */ typedef struct { @@ -181,11 +201,11 @@ static char *base64dec(const char *); static ssize_t xwrite(int, const char *, size_t); /* Globals */ -Term term; int cmdfd; pid_t pid; int oldbutton = 3; /* button event on startup: 3 = release */ +static Term term; static Selection sel; static CSIEscape csiescseq; static STREscape strescseq; diff --git a/st.h b/st.h index 27c48cf..b5bc1b5 100644 --- a/st.h +++ b/st.h @@ -76,26 +76,6 @@ typedef struct { char state; } TCursor; -/* Internal representation of the screen */ -typedef struct { - int row; /* nb row */ - int col; /* nb col */ - Line *line; /* screen */ - Line *alt; /* alternate screen */ - int *dirty; /* dirtyness of lines */ - TCursor c; /* cursor */ - int ocx; /* old cursor col */ - int ocy; /* old cursor row */ - int top; /* top scroll limit */ - int bot; /* bottom scroll limit */ - int mode; /* terminal mode flags */ - int esc; /* escape state flags */ - char trantbl[4]; /* charset table translation */ - int charset; /* current charset */ - int icharset; /* selected charset for sequence */ - int *tabs; -} Term; - /* Purely graphic info */ typedef struct { int tw, th; /* tty width and height */ @@ -168,7 +148,6 @@ void *xrealloc(void *, size_t); char *xstrdup(char *); /* Globals */ -extern Term term; extern int cmdfd; extern pid_t pid; extern int oldbutton; diff --git a/x.c b/x.c index d205ca7..873ff08 100644 --- a/x.c +++ b/x.c @@ -130,7 +130,7 @@ static void xdrawglyphfontspecs(const XftGlyphFontSpec *, Glyph, int, int, int); static void xdrawglyph(Glyph, int, int); static void xclear(int, int, int, int); static int xgeommasktogravity(int); -static void xinit(void); +static void xinit(int, int); static void cresize(int, int); static void xresize(int, int); static int xloadfont(Font *, FcPattern *); @@ -299,18 +299,16 @@ int x2col(int x) { x -= borderpx; - x /= win.cw; - - return LIMIT(x, 0, term.col-1); + LIMIT(x, 0, win.tw - 1); + return x / win.cw; } int y2row(int y) { y -= borderpx; - y /= win.ch; - - return LIMIT(y, 0, term.row-1); + LIMIT(y, 0, win.th - 1); + return y / win.ch; } void @@ -984,7 +982,7 @@ xunloadfonts(void) } void -xinit(void) +xinit(int cols, int rows) { XGCValues gcvalues; Cursor cursor; @@ -1009,8 +1007,8 @@ xinit(void) xloadcols(); /* adjust fixed window geometry */ - win.w = 2 * borderpx + term.col * win.cw; - win.h = 2 * borderpx + term.row * win.ch; + win.w = 2 * borderpx + cols * win.cw; + win.h = 2 * borderpx + rows * win.ch; if (xw.gm & XNegative) xw.l += DisplayWidth(xw.dpy, xw.scr) - win.w - 2; if (xw.gm & YNegative) @@ -1042,7 +1040,7 @@ xinit(void) XFillRectangle(xw.dpy, xw.buf, dc.gc, 0, 0, win.w, win.h); /* font spec buffer */ - xw.specbuf = xmalloc(term.col * sizeof(GlyphFontSpec)); + xw.specbuf = xmalloc(cols * sizeof(GlyphFontSpec)); /* Xft rendering context */ xw.draw = XftDrawCreate(xw.dpy, xw.buf, xw.vis, xw.cmap); @@ -1337,15 +1335,16 @@ xdrawglyphfontspecs(const XftGlyphFontSpec *specs, Glyph base, int len, int x, i /* Intelligent cleaning up of the borders. */ if (x == 0) { xclear(0, (y == 0)? 0 : winy, borderpx, - winy + win.ch + ((y >= term.row-1)? win.h : 0)); + winy + win.ch + + ((winy + win.ch >= borderpx + win.th)? win.h : 0)); } - if (x + charlen >= term.col) { + if (winx + width >= borderpx + win.tw) { xclear(winx + width, (y == 0)? 0 : winy, win.w, - ((y >= term.row-1)? win.h : (winy + win.ch))); + ((winy + win.ch >= borderpx + win.th)? win.h : (winy + win.ch))); } if (y == 0) xclear(winx, 0, winx + width, borderpx); - if (y == term.row-1) + if (winy + win.ch >= borderpx + win.th) xclear(winx, winy + win.ch, winx + width, win.h); /* Clean up the region we want to draw to. */ @@ -1930,8 +1929,10 @@ run: } setlocale(LC_CTYPE, ""); XSetLocaleModifiers(""); - tnew(MAX(cols, 1), MAX(rows, 1)); - xinit(); + cols = MAX(cols, 1); + rows = MAX(rows, 1); + tnew(cols, rows); + xinit(cols, rows); xsetenv(); selinit(); run(); From 30683c70ab62fd37b5921cf72077b9aef2cb842e Mon Sep 17 00:00:00 2001 From: "Devin J. Pohly" Date: Sat, 24 Feb 2018 16:16:12 -0600 Subject: [PATCH 053/185] Limit usage of extern to config.h globals Prefer passing arguments to declaring external global variables. The only remaining usage of extern is for config.h variables which are needed in st.c instead of x.c (where it is now included). Signed-off-by: Devin J. Pohly --- config.def.h | 2 +- st.c | 29 +++++++++++++++++------------ st.h | 9 ++------- x.c | 16 +++++++++------- 4 files changed, 29 insertions(+), 27 deletions(-) diff --git a/config.def.h b/config.def.h index 616616a..82b1b09 100644 --- a/config.def.h +++ b/config.def.h @@ -16,7 +16,7 @@ static int borderpx = 2; * 4: value of shell in /etc/passwd * 5: value of shell in config.h */ -char *shell = "/bin/sh"; +static char *shell = "/bin/sh"; char *utmp = NULL; char *stty_args = "stty raw pass8 nl -echo -iexten -cstopb 38400"; diff --git a/st.c b/st.c index da832ed..ce32cc0 100644 --- a/st.c +++ b/st.c @@ -136,8 +136,7 @@ typedef struct { int narg; /* nb of args */ } STREscape; - -static void execsh(char **); +static void execsh(char *, char **); static void stty(char **); static void sigchld(int); static void ttywriteraw(const char *, size_t); @@ -201,15 +200,13 @@ static char *base64dec(const char *); static ssize_t xwrite(int, const char *, size_t); /* Globals */ -int cmdfd; -pid_t pid; -int oldbutton = 3; /* button event on startup: 3 = release */ - static Term term; static Selection sel; static CSIEscape csiescseq; static STREscape strescseq; static int iofd = 1; +static int cmdfd; +static pid_t pid; static uchar utfbyte[UTF_SIZ + 1] = {0x80, 0, 0xC0, 0xE0, 0xF0}; static uchar utfmask[UTF_SIZ + 1] = {0xC0, 0x80, 0xE0, 0xF0, 0xF8}; @@ -659,7 +656,7 @@ die(const char *errstr, ...) } void -execsh(char **args) +execsh(char *cmd, char **args) { char *sh, *prog; const struct passwd *pw; @@ -673,7 +670,7 @@ execsh(char **args) } if ((sh = getenv("SHELL")) == NULL) - sh = (pw->pw_shell[0]) ? pw->pw_shell : shell; + sh = (pw->pw_shell[0]) ? pw->pw_shell : cmd; if (args) prog = args[0]; @@ -745,8 +742,8 @@ stty(char **args) perror("Couldn't call stty"); } -void -ttynew(char *line, char *out, char **args) +int +ttynew(char *line, char *cmd, char *out, char **args) { int m, s; @@ -765,7 +762,7 @@ ttynew(char *line, char *out, char **args) die("open line failed: %s\n", strerror(errno)); dup2(cmdfd, 0); stty(args); - return; + return cmdfd; } /* seems to work fine on linux, openbsd and freebsd */ @@ -786,7 +783,7 @@ ttynew(char *line, char *out, char **args) die("ioctl TIOCSCTTY failed: %s\n", strerror(errno)); close(s); close(m); - execsh(args); + execsh(cmd, args); break; default: close(s); @@ -794,6 +791,7 @@ ttynew(char *line, char *out, char **args) signal(SIGCHLD, sigchld); break; } + return cmdfd; } size_t @@ -916,6 +914,13 @@ ttyresize(int tw, int th) fprintf(stderr, "Couldn't set window size: %s\n", strerror(errno)); } +void +ttyhangup() +{ + /* Send SIGHUP to shell */ + kill(pid, SIGHUP); +} + int tattrset(int attr) { diff --git a/st.h b/st.h index b5bc1b5..0a7472b 100644 --- a/st.h +++ b/st.h @@ -125,7 +125,8 @@ int tattrset(int); void tnew(int, int); void tresize(int, int); void tsetdirtattr(int); -void ttynew(char *, char *, char **); +void ttyhangup(void); +int ttynew(char *, char *, char *, char **); size_t ttyread(void); void ttyresize(int, int); void ttywrite(const char *, size_t, int); @@ -147,13 +148,7 @@ void *xmalloc(size_t); void *xrealloc(void *, size_t); char *xstrdup(char *); -/* Globals */ -extern int cmdfd; -extern pid_t pid; -extern int oldbutton; - /* config.h globals */ -extern char *shell; extern char *utmp; extern char *stty_args; extern char *vtiden; diff --git a/x.c b/x.c index 873ff08..970d6dd 100644 --- a/x.c +++ b/x.c @@ -227,6 +227,8 @@ static char *opt_line = NULL; static char *opt_name = NULL; static char *opt_title = NULL; +static int oldbutton = 3; /* button event on startup: 3 = release */ + void clipcopy(const Arg *dummy) { @@ -1733,8 +1735,7 @@ cmessage(XEvent *e) win.mode &= ~MODE_FOCUSED; } } else if (e->xclient.data.l[0] == xw.wmdeletewin) { - /* Send SIGHUP to shell */ - kill(pid, SIGHUP); + ttyhangup(); exit(0); } } @@ -1755,6 +1756,7 @@ run(void) int w = win.w, h = win.h; fd_set rfd; int xfd = XConnectionNumber(xw.dpy), xev, blinkset = 0, dodraw = 0; + int ttyfd; struct timespec drawtimeout, *tv = NULL, now, last, lastblink; long deltatime; @@ -1774,7 +1776,7 @@ run(void) } } while (ev.type != MapNotify); - ttynew(opt_line, opt_io, opt_cmd); + ttyfd = ttynew(opt_line, shell, opt_io, opt_cmd); cresize(w, h); clock_gettime(CLOCK_MONOTONIC, &last); @@ -1782,15 +1784,15 @@ run(void) for (xev = actionfps;;) { FD_ZERO(&rfd); - FD_SET(cmdfd, &rfd); + FD_SET(ttyfd, &rfd); FD_SET(xfd, &rfd); - if (pselect(MAX(xfd, cmdfd)+1, &rfd, NULL, NULL, tv, NULL) < 0) { + if (pselect(MAX(xfd, ttyfd)+1, &rfd, NULL, NULL, tv, NULL) < 0) { if (errno == EINTR) continue; die("select failed: %s\n", strerror(errno)); } - if (FD_ISSET(cmdfd, &rfd)) { + if (FD_ISSET(ttyfd, &rfd)) { ttyread(); if (blinktimeout) { blinkset = tattrset(ATTR_BLINK); @@ -1834,7 +1836,7 @@ run(void) if (xev && !FD_ISSET(xfd, &rfd)) xev--; - if (!FD_ISSET(cmdfd, &rfd) && !FD_ISSET(xfd, &rfd)) { + if (!FD_ISSET(ttyfd, &rfd) && !FD_ISSET(xfd, &rfd)) { if (blinkset) { if (TIMEDIFF(now, lastblink) \ > blinktimeout) { From e0215d53770a9b6bc6e5d7b9a603ecd34dbd7100 Mon Sep 17 00:00:00 2001 From: "Devin J. Pohly" Date: Sat, 24 Feb 2018 16:32:20 -0600 Subject: [PATCH 054/185] Reduce visibility wherever possible When possible, declare functions/variables static and move struct definitions out of headers. In order to allow utf8decode to become internal, use codepoint for DECSCUSR extension directly. Signed-off-by: Devin J. Pohly --- st.c | 31 ++++++++++++++++++++++++++++++- st.h | 40 ---------------------------------------- win.h | 1 - x.c | 15 +++++++++++++-- 4 files changed, 43 insertions(+), 44 deletions(-) diff --git a/st.c b/st.c index ce32cc0..f46aab6 100644 --- a/st.c +++ b/st.c @@ -36,6 +36,7 @@ /* Arbitrary sizes */ #define UTF_INVALID 0xFFFD +#define UTF_SIZ 4 #define ESC_BUF_SIZ (128*UTF_SIZ) #define ESC_ARG_SIZ 16 #define STR_BUF_SIZ ESC_BUF_SIZ @@ -95,6 +96,31 @@ enum escape_state { ESC_DCS =128, }; +typedef struct { + Glyph attr; /* current char attributes */ + int x; + int y; + char state; +} TCursor; + +typedef struct { + int mode; + int type; + int snap; + /* + * Selection variables: + * nb – normalized coordinates of the beginning of the selection + * ne – normalized coordinates of the end of the selection + * ob – original coordinates of the beginning of the selection + * oe – original coordinates of the end of the selection + */ + struct { + int x, y; + } nb, ne, ob, oe; + + int alt; +} Selection; + /* Internal representation of the screen */ typedef struct { int row; /* nb row */ @@ -187,15 +213,18 @@ static void tstrsequence(uchar); static void drawregion(int, int, int, int); +static void selnormalize(void); static void selscroll(int, int); static void selsnap(int *, int *, int); +static size_t utf8decode(const char *, Rune *, size_t); static Rune utf8decodebyte(char, size_t *); static char utf8encodebyte(Rune, size_t); -static char *utf8strchr(char *s, Rune u); +static char *utf8strchr(char *, Rune); static size_t utf8validate(Rune *, size_t); static char *base64dec(const char *); +static char base64dec_getc(const char **); static ssize_t xwrite(int, const char *, size_t); diff --git a/st.h b/st.h index 0a7472b..1015fc6 100644 --- a/st.h +++ b/st.h @@ -1,8 +1,5 @@ /* See LICENSE for license details. */ -/* Arbitrary sizes */ -#define UTF_SIZ 4 - /* macros */ #define MIN(a, b) ((a) < (b) ? (a) : (b)) #define MAX(a, b) ((a) < (b) ? (b) : (a)) @@ -69,41 +66,6 @@ typedef struct { typedef Glyph *Line; -typedef struct { - Glyph attr; /* current char attributes */ - int x; - int y; - char state; -} TCursor; - -/* Purely graphic info */ -typedef struct { - int tw, th; /* tty width and height */ - int w, h; /* window width and height */ - int ch; /* char height */ - int cw; /* char width */ - int mode; /* window state/mode flags */ - int cursor; /* cursor style */ -} TermWindow; - -typedef struct { - int mode; - int type; - int snap; - /* - * Selection variables: - * nb – normalized coordinates of the beginning of the selection - * ne – normalized coordinates of the end of the selection - * ob – original coordinates of the beginning of the selection - * oe – original coordinates of the end of the selection - */ - struct { - int x, y; - } nb, ne, ob, oe; - - int alt; -} Selection; - typedef union { int i; uint ui; @@ -137,11 +99,9 @@ void selclear(void); void selinit(void); void selstart(int, int, int); void selextend(int, int, int, int); -void selnormalize(void); int selected(int, int); char *getsel(void); -size_t utf8decode(const char *, Rune *, size_t); size_t utf8encode(Rune, char *); void *xmalloc(size_t); diff --git a/win.h b/win.h index 7a866fd..31f327d 100644 --- a/win.h +++ b/win.h @@ -27,7 +27,6 @@ void xbell(void); void xclipcopy(void); void xdrawcursor(int, int, Glyph, int, int, Glyph); void xdrawline(Line, int, int, int); -void xhints(void); void xfinishdraw(void); void xloadcols(void); int xsetcolorname(int, const char *); diff --git a/x.c b/x.c index 970d6dd..f7b0528 100644 --- a/x.c +++ b/x.c @@ -75,6 +75,15 @@ typedef XftColor Color; typedef XftGlyphFontSpec GlyphFontSpec; /* Purely graphic info */ +typedef struct { + int tw, th; /* tty width and height */ + int w, h; /* window width and height */ + int ch; /* char height */ + int cw; /* char width */ + int mode; /* window state/mode flags */ + int cursor; /* cursor style */ +} TermWindow; + typedef struct { Display *dpy; Colormap cmap; @@ -133,6 +142,8 @@ static int xgeommasktogravity(int); static void xinit(int, int); static void cresize(int, int); static void xresize(int, int); +static void xhints(void); +static int xloadcolor(int, const char *, Color *); static int xloadfont(Font *, FcPattern *); static void xloadfonts(char *, double); static void xunloadfont(Font *); @@ -1430,8 +1441,8 @@ xdrawcursor(int cx, int cy, Glyph g, int ox, int oy, Glyph og) /* draw the new one */ if (IS_SET(MODE_FOCUSED)) { switch (win.cursor) { - case 7: /* st extension: snowman */ - utf8decode("☃", &g.u, UTF_SIZ); + case 7: /* st extension: snowman (U+2603) */ + g.u = 0x2603; case 0: /* Blinking Block */ case 1: /* Blinking Block (Default) */ case 2: /* Steady Block */ From 403c57ebb5b3745ff93e49b87e526c49dc59a5b9 Mon Sep 17 00:00:00 2001 From: "Devin J. Pohly" Date: Sat, 24 Feb 2018 16:45:42 -0600 Subject: [PATCH 055/185] Clean up #includes Signed-off-by: Devin J. Pohly --- st.c | 7 ------- st.h | 3 +++ x.c | 3 +-- 3 files changed, 4 insertions(+), 9 deletions(-) diff --git a/st.c b/st.c index f46aab6..543c615 100644 --- a/st.c +++ b/st.c @@ -3,24 +3,17 @@ #include #include #include -#include #include #include #include #include #include #include -#include #include #include -#include -#include #include #include -#include -#include #include -#include #include #include "st.h" diff --git a/st.h b/st.h index 1015fc6..dac64d8 100644 --- a/st.h +++ b/st.h @@ -1,5 +1,8 @@ /* See LICENSE for license details. */ +#include +#include + /* 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 f7b0528..d0acfee 100644 --- a/x.c +++ b/x.c @@ -1,15 +1,14 @@ /* See LICENSE for license details. */ #include +#include #include #include -#include #include #include #include #include #include #include -#include #include #include #include From 20e0da7f14cc5f30863e0b8014fa223fbaff1e30 Mon Sep 17 00:00:00 2001 From: "Devin J. Pohly" Date: Sat, 24 Feb 2018 17:09:13 -0600 Subject: [PATCH 056/185] General cleanup Simplifies logic in a couple places and removes a redundant function call. Signed-off-by: Devin J. Pohly --- st.c | 1 - x.c | 46 +++++++++++++++++++++------------------------- 2 files changed, 21 insertions(+), 26 deletions(-) diff --git a/st.c b/st.c index 543c615..75c3dd1 100644 --- a/st.c +++ b/st.c @@ -1693,7 +1693,6 @@ csihandle(void) tputtab(csiescseq.arg[0]); break; case 'J': /* ED -- Clear screen */ - selclear(); switch (csiescseq.arg[0]) { case 0: /* below */ tclearregion(term.c.x, term.c.y, term.col-1, term.c.y); diff --git a/x.c b/x.c index d0acfee..f005687 100644 --- a/x.c +++ b/x.c @@ -149,8 +149,8 @@ static void xunloadfont(Font *); static void xunloadfonts(void); static void xsetenv(void); static void xseturgency(int); -static int x2col(int); -static int y2row(int); +static int evcol(XEvent *); +static int evrow(XEvent *); static void expose(XEvent *); static void visibility(XEvent *); @@ -308,17 +308,17 @@ zoomreset(const Arg *arg) } int -x2col(int x) +evcol(XEvent *e) { - x -= borderpx; + int x = e->xbutton.x - borderpx; LIMIT(x, 0, win.tw - 1); return x / win.cw; } int -y2row(int y) +evrow(XEvent *e) { - y -= borderpx; + int y = e->xbutton.y - borderpx; LIMIT(y, 0, win.th - 1); return y / win.ch; } @@ -335,7 +335,7 @@ mousesel(XEvent *e, int done) break; } } - selextend(x2col(e->xbutton.x), y2row(e->xbutton.y), seltype, done); + selextend(evcol(e), evrow(e), seltype, done); if (done) setsel(getsel(), e->xbutton.time); } @@ -343,9 +343,8 @@ mousesel(XEvent *e, int done) void mousereport(XEvent *e) { - int x = x2col(e->xbutton.x), y = y2row(e->xbutton.y), - button = e->xbutton.button, state = e->xbutton.state, - len; + int len, x = evcol(e), y = evrow(e), + button = e->xbutton.button, state = e->xbutton.state; char buf[40]; static int ox, oy; @@ -440,7 +439,7 @@ bpress(XEvent *e) xsel.tclick2 = xsel.tclick1; xsel.tclick1 = now; - selstart(x2col(e->xbutton.x), y2row(e->xbutton.y), snap); + selstart(evcol(e), evrow(e), snap); } } @@ -464,18 +463,16 @@ selnotify(XEvent *e) ulong nitems, ofs, rem; int format; uchar *data, *last, *repl; - Atom type, incratom, property; + Atom type, incratom, property = None; incratom = XInternAtom(xw.dpy, "INCR", 0); ofs = 0; - if (e->type == SelectionNotify) { + if (e->type == SelectionNotify) property = e->xselection.property; - } else if(e->type == PropertyNotify) { + else if (e->type == PropertyNotify) property = e->xproperty.atom; - } else { - return; - } + if (property == None) return; @@ -625,7 +622,7 @@ setsel(char *str, Time t) XSetSelectionOwner(xw.dpy, XA_PRIMARY, xw.win, t); if (XGetSelectionOwner(xw.dpy, XA_PRIMARY) != xw.win) - selclear_(NULL); + selclear(); } void @@ -1407,12 +1404,13 @@ xdrawcursor(int cx, int cy, Glyph g, int ox, int oy, Glyph og) og.mode ^= ATTR_REVERSE; xdrawglyph(og, ox, oy); + if (IS_SET(MODE_HIDE)) + return; + /* * Select the right color for the right mode. */ g.mode &= ATTR_BOLD|ATTR_ITALIC|ATTR_UNDERLINE|ATTR_STRUCK|ATTR_WIDE; - g.fg = defaultbg; - g.bg = defaultcs; if (IS_SET(MODE_REVERSE)) { g.mode |= ATTR_REVERSE; @@ -1426,17 +1424,15 @@ xdrawcursor(int cx, int cy, Glyph g, int ox, int oy, Glyph og) } } else { if (selected(cx, cy)) { - drawcol = dc.col[defaultrcs]; g.fg = defaultfg; g.bg = defaultrcs; } else { - drawcol = dc.col[defaultcs]; + g.fg = defaultbg; + g.bg = defaultcs; } + drawcol = dc.col[g.bg]; } - if (IS_SET(MODE_HIDE)) - return; - /* draw the new one */ if (IS_SET(MODE_FOCUSED)) { switch (win.cursor) { From 8b8255ac0e188445f6904ff16272e9e93093cbde Mon Sep 17 00:00:00 2001 From: Hiltjo Posthuma Date: Fri, 9 Mar 2018 15:35:34 +0100 Subject: [PATCH 057/185] regression: include termios.h for tcsendbreak etc --- st.c | 1 + 1 file changed, 1 insertion(+) diff --git a/st.c b/st.c index 75c3dd1..65a0cb6 100644 --- a/st.c +++ b/st.c @@ -13,6 +13,7 @@ #include #include #include +#include #include #include From b81888ee7d95d9ecb7b1749630b09a96065a8fea Mon Sep 17 00:00:00 2001 From: Hiltjo Posthuma Date: Fri, 9 Mar 2018 15:36:25 +0100 Subject: [PATCH 058/185] xhints: no need to initialize sizeh --- x.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/x.c b/x.c index f005687..537322b 100644 --- a/x.c +++ b/x.c @@ -780,7 +780,7 @@ xhints(void) XClassHint class = {opt_name ? opt_name : termname, opt_class ? opt_class : termname}; XWMHints wm = {.flags = InputHint, .input = 1}; - XSizeHints *sizeh = NULL; + XSizeHints *sizeh; sizeh = XAllocSizeHints(); From c5ba9c025b7ebc34979e839453528f6e4a18712d Mon Sep 17 00:00:00 2001 From: Hiltjo Posthuma Date: Fri, 9 Mar 2018 15:36:38 +0100 Subject: [PATCH 059/185] use math.h for ceilf --- x.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/x.c b/x.c index 537322b..d43a529 100644 --- a/x.c +++ b/x.c @@ -1,5 +1,6 @@ /* See LICENSE for license details. */ #include +#include #include #include #include @@ -901,7 +902,6 @@ xloadfonts(char *fontstr, double fontsize) { FcPattern *pattern; double fontval; - float ceilf(float); if (fontstr[0] == '-') { pattern = XftXlfdParse(fontstr, False, False); From 49a4f91fc5caf4b8b64f2b73a6be506fa06917b9 Mon Sep 17 00:00:00 2001 From: Hiltjo Posthuma Date: Wed, 14 Mar 2018 19:50:37 +0100 Subject: [PATCH 060/185] bump version to 0.8 --- config.mk | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/config.mk b/config.mk index 0aceec4..b2eac10 100644 --- a/config.mk +++ b/config.mk @@ -1,5 +1,5 @@ # st version -VERSION = 0.7 +VERSION = 0.8 # Customize below to fit your system From 0f245dfeb9bb4e87a005da2ecdcd7a88ba6e7f32 Mon Sep 17 00:00:00 2001 From: Hiltjo Posthuma Date: Wed, 14 Mar 2018 19:54:50 +0100 Subject: [PATCH 061/185] Makefile: add all files to make dist --- Makefile | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/Makefile b/Makefile index c678ead..0b3cecd 100644 --- a/Makefile +++ b/Makefile @@ -34,7 +34,9 @@ clean: dist: clean mkdir -p st-$(VERSION) - cp -R LICENSE Makefile README config.mk config.def.h st.info st.1 arg.h $(SRC) st-$(VERSION) + cp -R FAQ LEGACY TODO LICENSE Makefile README config.mk\ + config.def.h st.info st.1 arg.h st.h win.h $(SRC)\ + st-$(VERSION) tar -cf - st-$(VERSION) | gzip > st-$(VERSION).tar.gz rm -rf st-$(VERSION) From a712c2dd1815f83a56be33e8055eee29e05dfef7 Mon Sep 17 00:00:00 2001 From: Hiltjo Posthuma Date: Wed, 14 Mar 2018 20:00:35 +0100 Subject: [PATCH 062/185] update LICENSE: major contributors --- LICENSE | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/LICENSE b/LICENSE index fa0c63e..84c4d19 100644 --- a/LICENSE +++ b/LICENSE @@ -1,8 +1,11 @@ MIT/X Consortium License +© 2014-2018 Hiltjo Posthuma +© 2018 Devin J. Pohly +© 2017-2017 Quentin Rameau © 2009-2012 Aurélien APTEL © 2009 Anselm R Garbe -© 2012-2016 Roberto E. Vargas Caballero +© 2012-2017 Roberto E. Vargas Caballero © 2012-2016 Christoph Lohmann <20h at r-36 dot net> © 2013 Eon S. Jeon © 2013 Alexander Sedov From 8ab629031bd958d93deee9203fea32c38fe8ac30 Mon Sep 17 00:00:00 2001 From: Hiltjo Posthuma Date: Wed, 14 Mar 2018 20:06:42 +0100 Subject: [PATCH 063/185] LICENSE: fix a few years --- LICENSE | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/LICENSE b/LICENSE index 84c4d19..c356c39 100644 --- a/LICENSE +++ b/LICENSE @@ -2,15 +2,15 @@ MIT/X Consortium License © 2014-2018 Hiltjo Posthuma © 2018 Devin J. Pohly -© 2017-2017 Quentin Rameau +© 2014-2017 Quentin Rameau © 2009-2012 Aurélien APTEL -© 2009 Anselm R Garbe +© 2008-2017 Anselm R Garbe © 2012-2017 Roberto E. Vargas Caballero © 2012-2016 Christoph Lohmann <20h at r-36 dot net> © 2013 Eon S. Jeon © 2013 Alexander Sedov © 2013 Mark Edgar -© 2013 Eric Pruitt +© 2013-2014 Eric Pruitt © 2013 Michael Forney © 2013-2014 Markus Teich © 2014-2015 Laslo Hunhold From e7ef3c4ce95a21cfcd0988d493f636e1d0115871 Mon Sep 17 00:00:00 2001 From: Quentin Rameau Date: Fri, 16 Mar 2018 15:03:10 +0100 Subject: [PATCH 064/185] Fix regression from 69e32a6 when setting title. --- x.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/x.c b/x.c index d43a529..06e53d3 100644 --- a/x.c +++ b/x.c @@ -1492,7 +1492,7 @@ void xsettitle(char *p) { XTextProperty prop; - DEFAULT(p, "st"); + DEFAULT(p, opt_title); Xutf8TextListToTextProperty(xw.dpy, &p, 1, XUTF8StringStyle, &prop); From 0b507bb73138c636fd0ad6f2321624aae1346d58 Mon Sep 17 00:00:00 2001 From: Quentin Rameau Date: Fri, 16 Mar 2018 16:19:18 +0100 Subject: [PATCH 065/185] Fix title initialization --- x.c | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/x.c b/x.c index 06e53d3..d0b26ac 100644 --- a/x.c +++ b/x.c @@ -1929,12 +1929,12 @@ main(int argc, char *argv[]) } ARGEND; run: - if (argc > 0) { - /* eat all remaining arguments */ + if (argc > 0) /* eat all remaining arguments */ opt_cmd = argv; - if (!opt_title && !opt_line) - opt_title = basename(xstrdup(argv[0])); - } + + if (!opt_title) + opt_title = (opt_line || !opt_cmd) ? "st" : opt_cmd[0]; + setlocale(LC_CTYPE, ""); XSetLocaleModifiers(""); cols = MAX(cols, 1); From 7648697f711d5ae336af142e9cd464d943341b24 Mon Sep 17 00:00:00 2001 From: Hiltjo Posthuma Date: Fri, 16 Mar 2018 16:45:58 +0100 Subject: [PATCH 066/185] minor code-style: whitespace fixes --- st.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/st.c b/st.c index 65a0cb6..581647b 100644 --- a/st.c +++ b/st.c @@ -740,7 +740,6 @@ sigchld(int a) exit(0); } - void stty(char **args) { @@ -762,7 +761,7 @@ stty(char **args) } *q = '\0'; if (system(cmd) != 0) - perror("Couldn't call stty"); + perror("Couldn't call stty"); } int From 5345db3c9be1a22ca19202035b881b951c2f0f9e Mon Sep 17 00:00:00 2001 From: Hiltjo Posthuma Date: Sat, 17 Mar 2018 13:48:10 +0100 Subject: [PATCH 067/185] clipcopy: no need to check for free(NULL), set to NULL after free --- x.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/x.c b/x.c index d0b26ac..12bc86b 100644 --- a/x.c +++ b/x.c @@ -245,8 +245,8 @@ clipcopy(const Arg *dummy) { Atom clipboard; - if (xsel.clipboard != NULL) - free(xsel.clipboard); + free(xsel.clipboard); + xsel.clipboard = NULL; if (xsel.primary != NULL) { xsel.clipboard = xstrdup(xsel.primary); From 6ac8c8aa50d521f82d491ab54eb972660fdf3207 Mon Sep 17 00:00:00 2001 From: Hiltjo Posthuma Date: Sat, 17 Mar 2018 13:48:29 +0100 Subject: [PATCH 068/185] selextend: clarify: !sel.mode == SEL_IDLE --- st.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/st.c b/st.c index 581647b..46c954b 100644 --- a/st.c +++ b/st.c @@ -461,7 +461,7 @@ selextend(int col, int row, int type, int done) { int oldey, oldex, oldsby, oldsey, oldtype; - if (!sel.mode) + if (sel.mode == SEL_IDLE) return; if (done && sel.mode == SEL_EMPTY) { selclear(); From a5a928bfc1dd049780a45e072cb4ee42de7219bf Mon Sep 17 00:00:00 2001 From: Hiltjo Posthuma Date: Tue, 20 Mar 2018 21:22:27 +0100 Subject: [PATCH 069/185] don't modify argv, use a counter on some platforms (OpenBSD) this changes the exposed argv in tools using the kvm_* interface, such as ps and pgrep. --- arg.h | 22 ++++++++++++---------- 1 file changed, 12 insertions(+), 10 deletions(-) diff --git a/arg.h b/arg.h index ba3fb3f..a22e019 100644 --- a/arg.h +++ b/arg.h @@ -21,28 +21,30 @@ extern char *argv0; argc--;\ break;\ }\ - for (brk_ = 0, argv[0]++, argv_ = argv;\ - argv[0][0] && !brk_;\ - argv[0]++) {\ + int i_;\ + for (i_ = 1, brk_ = 0, argv_ = argv;\ + argv[0][i_] && !brk_;\ + i_++) {\ if (argv_ != argv)\ break;\ - argc_ = argv[0][0];\ + argc_ = argv[0][i_];\ switch (argc_) + #define ARGEND }\ } #define ARGC() argc_ -#define EARGF(x) ((argv[0][1] == '\0' && argv[1] == NULL)?\ +#define EARGF(x) ((argv[0][i_+1] == '\0' && argv[1] == NULL)?\ ((x), abort(), (char *)0) :\ - (brk_ = 1, (argv[0][1] != '\0')?\ - (&argv[0][1]) :\ + (brk_ = 1, (argv[0][i_+1] != '\0')?\ + (&argv[0][i_+1]) :\ (argc--, argv++, argv[0]))) -#define ARGF() ((argv[0][1] == '\0' && argv[1] == NULL)?\ +#define ARGF() ((argv[0][i_+1] == '\0' && argv[1] == NULL)?\ (char *)0 :\ - (brk_ = 1, (argv[0][1] != '\0')?\ - (&argv[0][1]) :\ + (brk_ = 1, (argv[0][i_+1] != '\0')?\ + (&argv[0][i_+1]) :\ (argc--, argv++, argv[0]))) #endif From f4020b2cc4fe45c9e8bfe47fc73deccd867cf9de Mon Sep 17 00:00:00 2001 From: Hiltjo Posthuma Date: Tue, 20 Mar 2018 21:25:30 +0100 Subject: [PATCH 070/185] fix regression by selecting clipboard text "restore the old behaviour that the primary doesn't get deleted by a simple left click" Patch by Daniel Tameling , thanks! --- x.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/x.c b/x.c index 12bc86b..c343ba2 100644 --- a/x.c +++ b/x.c @@ -618,6 +618,9 @@ selrequest(XEvent *e) void setsel(char *str, Time t) { + if (!str) + return; + free(xsel.primary); xsel.primary = str; From 6f0f2b7ec3713351de274707672fbadb6cc727a2 Mon Sep 17 00:00:00 2001 From: Hiltjo Posthuma Date: Tue, 20 Mar 2018 21:29:10 +0100 Subject: [PATCH 071/185] bump version to 0.8.1 --- config.mk | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/config.mk b/config.mk index b2eac10..039c42c 100644 --- a/config.mk +++ b/config.mk @@ -1,5 +1,5 @@ # st version -VERSION = 0.8 +VERSION = 0.8.1 # Customize below to fit your system From 74cff67bd7746c2636ea7bc78a0b8af2f8c44838 Mon Sep 17 00:00:00 2001 From: Daniel Tameling Date: Wed, 28 Mar 2018 21:27:58 +0200 Subject: [PATCH 072/185] set sel.alt in selstart instead of selextend --- st.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/st.c b/st.c index 46c954b..2612c95 100644 --- a/st.c +++ b/st.c @@ -446,6 +446,7 @@ selstart(int col, int row, int snap) selclear(); sel.mode = SEL_EMPTY; sel.type = SEL_REGULAR; + sel.alt = IS_SET(MODE_ALTSCREEN); sel.snap = snap; sel.oe.x = sel.ob.x = col; sel.oe.y = sel.ob.y = row; @@ -474,7 +475,6 @@ selextend(int col, int row, int type, int done) oldsey = sel.ne.y; oldtype = sel.type; - sel.alt = IS_SET(MODE_ALTSCREEN); sel.oe.x = col; sel.oe.y = row; selnormalize(); From bd3f7fd84270025696790512cf3c2dafaf5bc77f Mon Sep 17 00:00:00 2001 From: Hiltjo Posthuma Date: Thu, 29 Mar 2018 18:18:30 +0200 Subject: [PATCH 073/185] st -v: remove years and copyright text --- x.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/x.c b/x.c index c343ba2..f0195c9 100644 --- a/x.c +++ b/x.c @@ -1925,7 +1925,7 @@ main(int argc, char *argv[]) opt_embed = EARGF(usage()); break; case 'v': - die("%s " VERSION " (c) 2010-2016 st engineers\n", argv0); + die("%s " VERSION "\n", argv0); break; default: usage(); From 041912a791e8c2f4d5d2415b16210d29d7e701c5 Mon Sep 17 00:00:00 2001 From: Hiltjo Posthuma Date: Thu, 29 Mar 2018 18:30:05 +0200 Subject: [PATCH 074/185] error message style and use strerror in a few places --- st.c | 21 +++++++++++---------- x.c | 27 +++++++++++++-------------- 2 files changed, 24 insertions(+), 24 deletions(-) diff --git a/st.c b/st.c index 2612c95..0628707 100644 --- a/st.c +++ b/st.c @@ -256,10 +256,10 @@ xwrite(int fd, const char *s, size_t len) void * xmalloc(size_t len) { - void *p = malloc(len); + void *p; - if (!p) - die("Out of memory\n"); + if (!(p = malloc(len))) + die("malloc: %s\n", strerror(errno)); return p; } @@ -268,7 +268,7 @@ void * xrealloc(void *p, size_t len) { if ((p = realloc(p, len)) == NULL) - die("Out of memory\n"); + die("realloc: %s\n", strerror(errno)); return p; } @@ -277,7 +277,7 @@ char * xstrdup(char *s) { if ((s = strdup(s)) == NULL) - die("Out of memory\n"); + die("strdup: %s\n", strerror(errno)); return s; } @@ -687,7 +687,7 @@ execsh(char *cmd, char **args) errno = 0; if ((pw = getpwuid(getuid())) == NULL) { if (errno) - die("getpwuid:%s\n", strerror(errno)); + die("getpwuid: %s\n", strerror(errno)); else die("who are you?\n"); } @@ -730,7 +730,7 @@ sigchld(int a) pid_t p; if ((p = waitpid(pid, &stat, WNOHANG)) < 0) - die("Waiting for pid %hd failed: %s\n", pid, strerror(errno)); + die("waiting for pid %hd failed: %s\n", pid, strerror(errno)); if (pid != p) return; @@ -781,7 +781,8 @@ ttynew(char *line, char *cmd, char *out, char **args) if (line) { if ((cmdfd = open(line, O_RDWR)) < 0) - die("open line failed: %s\n", strerror(errno)); + die("open line '%s' failed: %s\n", + line, strerror(errno)); dup2(cmdfd, 0); stty(args); return cmdfd; @@ -793,7 +794,7 @@ ttynew(char *line, char *cmd, char *out, char **args) switch (pid = fork()) { case -1: - die("fork failed\n"); + die("fork failed: %s\n", strerror(errno)); break; case 0: close(iofd); @@ -826,7 +827,7 @@ ttyread(void) /* append read bytes to unprocessed bytes */ if ((ret = read(cmdfd, buf+buflen, LEN(buf)-buflen)) < 0) - die("Couldn't read from shell: %s\n", strerror(errno)); + die("couldn't read from shell: %s\n", strerror(errno)); buflen += ret; written = twrite(buf, buflen, 0); diff --git a/x.c b/x.c index f0195c9..c0bd890 100644 --- a/x.c +++ b/x.c @@ -742,9 +742,9 @@ xloadcols(void) for (i = 0; i < dc.collen; i++) if (!xloadcolor(i, NULL, &dc.col[i])) { if (colorname[i]) - die("Could not allocate color '%s'\n", colorname[i]); + die("could not allocate color '%s'\n", colorname[i]); else - die("Could not allocate color %d\n", i); + die("could not allocate color %d\n", i); } loaded = 1; } @@ -869,7 +869,7 @@ xloadfont(Font *f, FcPattern *pattern) if ((XftPatternGetInteger(f->match->pattern, "slant", 0, &haveattr) != XftResultMatch) || haveattr < wantattr) { f->badslant = 1; - fputs("st: font slant does not match\n", stderr); + fputs("font slant does not match\n", stderr); } } @@ -878,7 +878,7 @@ xloadfont(Font *f, FcPattern *pattern) if ((XftPatternGetInteger(f->match->pattern, "weight", 0, &haveattr) != XftResultMatch) || haveattr != wantattr) { f->badweight = 1; - fputs("st: font weight does not match\n", stderr); + fputs("font weight does not match\n", stderr); } } @@ -906,14 +906,13 @@ xloadfonts(char *fontstr, double fontsize) FcPattern *pattern; double fontval; - if (fontstr[0] == '-') { + if (fontstr[0] == '-') pattern = XftXlfdParse(fontstr, False, False); - } else { + else pattern = FcNameParse((FcChar8 *)fontstr); - } if (!pattern) - die("st: can't open font %s\n", fontstr); + die("can't open font %s\n", fontstr); if (fontsize > 1) { FcPatternDel(pattern, FC_PIXEL_SIZE); @@ -939,7 +938,7 @@ xloadfonts(char *fontstr, double fontsize) } if (xloadfont(&dc.font, pattern)) - die("st: can't open font %s\n", fontstr); + die("can't open font %s\n", fontstr); if (usedfontsize < 0) { FcPatternGetDouble(dc.font.match->pattern, @@ -956,17 +955,17 @@ xloadfonts(char *fontstr, double fontsize) FcPatternDel(pattern, FC_SLANT); FcPatternAddInteger(pattern, FC_SLANT, FC_SLANT_ITALIC); if (xloadfont(&dc.ifont, pattern)) - die("st: can't open font %s\n", fontstr); + die("can't open font %s\n", fontstr); FcPatternDel(pattern, FC_WEIGHT); FcPatternAddInteger(pattern, FC_WEIGHT, FC_WEIGHT_BOLD); if (xloadfont(&dc.ibfont, pattern)) - die("st: can't open font %s\n", fontstr); + die("can't open font %s\n", fontstr); FcPatternDel(pattern, FC_SLANT); FcPatternAddInteger(pattern, FC_SLANT, FC_SLANT_ROMAN); if (xloadfont(&dc.bfont, pattern)) - die("st: can't open font %s\n", fontstr); + die("can't open font %s\n", fontstr); FcPatternDestroy(pattern); } @@ -1003,13 +1002,13 @@ xinit(int cols, int rows) XColor xmousefg, xmousebg; if (!(xw.dpy = XOpenDisplay(NULL))) - die("Can't open display\n"); + die("can't open display\n"); xw.scr = XDefaultScreen(xw.dpy); xw.vis = XDefaultVisual(xw.dpy, xw.scr); /* font */ if (!FcInit()) - die("Could not init fontconfig.\n"); + die("could not init fontconfig.\n"); usedfont = (opt_font == NULL)? font : opt_font; xloadfonts(usedfont, 0); From 30ce2cc002585409b36c630512c6ca4db8f88f15 Mon Sep 17 00:00:00 2001 From: Hiltjo Posthuma Date: Fri, 25 May 2018 11:59:28 +0200 Subject: [PATCH 075/185] Pledge on OpenBSD --- config.mk | 6 ++++++ st.c | 8 ++++++++ 2 files changed, 14 insertions(+) diff --git a/config.mk b/config.mk index 039c42c..c7355f1 100644 --- a/config.mk +++ b/config.mk @@ -23,6 +23,12 @@ CPPFLAGS = -DVERSION=\"$(VERSION)\" -D_XOPEN_SOURCE=600 STCFLAGS = $(INCS) $(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` + # compiler and linker # CC = c99 diff --git a/st.c b/st.c index 0628707..b9750f2 100644 --- a/st.c +++ b/st.c @@ -28,6 +28,10 @@ #include #endif +#ifndef __OpenBSD__ +#define pledge(a,b) 0 +#endif + /* Arbitrary sizes */ #define UTF_INVALID 0xFFFD #define UTF_SIZ 4 @@ -806,9 +810,13 @@ ttynew(char *line, char *cmd, char *out, char **args) die("ioctl TIOCSCTTY failed: %s\n", strerror(errno)); close(s); close(m); + if (pledge("stdio getpw proc exec", NULL) == -1) + die("pledge\n"); execsh(cmd, args); break; default: + if (pledge("stdio rpath tty proc", NULL) == -1) + die("pledge\n"); close(s); cmdfd = m; signal(SIGCHLD, sigchld); From 235a783e039986fca3ccefec08ea45804dab196f Mon Sep 17 00:00:00 2001 From: Hiltjo Posthuma Date: Fri, 25 May 2018 13:04:09 +0200 Subject: [PATCH 076/185] code-style for pledge(2) feedback from Klemens, thanks --- st.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/st.c b/st.c index b9750f2..76bb3ea 100644 --- a/st.c +++ b/st.c @@ -28,10 +28,6 @@ #include #endif -#ifndef __OpenBSD__ -#define pledge(a,b) 0 -#endif - /* Arbitrary sizes */ #define UTF_INVALID 0xFFFD #define UTF_SIZ 4 @@ -810,13 +806,17 @@ ttynew(char *line, char *cmd, char *out, char **args) die("ioctl TIOCSCTTY failed: %s\n", strerror(errno)); close(s); close(m); +#ifdef __OpenBSD__ if (pledge("stdio getpw proc exec", NULL) == -1) die("pledge\n"); +#endif execsh(cmd, args); break; default: +#ifdef __OpenBSD__ if (pledge("stdio rpath tty proc", NULL) == -1) die("pledge\n"); +#endif close(s); cmdfd = m; signal(SIGCHLD, sigchld); From dc3b5babf1f8639a0d65cd347fc7879ac0461012 Mon Sep 17 00:00:00 2001 From: Hiltjo Posthuma Date: Sat, 2 Jun 2018 17:11:14 +0200 Subject: [PATCH 077/185] config.mk: remove extra newline before EOF --- config.mk | 1 - 1 file changed, 1 deletion(-) diff --git a/config.mk b/config.mk index c7355f1..e676105 100644 --- a/config.mk +++ b/config.mk @@ -31,4 +31,3 @@ STLDFLAGS = $(LIBS) $(LDFLAGS) # compiler and linker # CC = c99 - From 29f341da7cf32888f45005e08de202d9a372d972 Mon Sep 17 00:00:00 2001 From: Jules Maselbas Date: Wed, 27 Jun 2018 17:08:30 +0200 Subject: [PATCH 078/185] Fix crash on resize Prevent to realloc xw.specbuc with a negative number of col. Add proper hints for the minimal size, for one character. --- x.c | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/x.c b/x.c index c0bd890..00cb6b1 100644 --- a/x.c +++ b/x.c @@ -672,6 +672,8 @@ cresize(int width, int height) col = (win.w - 2 * borderpx) / win.cw; row = (win.h - 2 * borderpx) / win.ch; + col = MAX(1, col); + row = MAX(1, row); tresize(col, row); xresize(col, row); @@ -681,8 +683,8 @@ cresize(int width, int height) void xresize(int col, int row) { - win.tw = MAX(1, col * win.cw); - win.th = MAX(1, row * win.ch); + win.tw = col * win.cw; + win.th = row * win.ch; XFreePixmap(xw.dpy, xw.buf); xw.buf = XCreatePixmap(xw.dpy, xw.win, win.w, win.h, @@ -788,15 +790,17 @@ xhints(void) sizeh = XAllocSizeHints(); - sizeh->flags = PSize | PResizeInc | PBaseSize; + sizeh->flags = PSize | PResizeInc | PBaseSize | PMinSize; sizeh->height = win.h; sizeh->width = win.w; sizeh->height_inc = win.ch; sizeh->width_inc = win.cw; sizeh->base_height = 2 * borderpx; sizeh->base_width = 2 * borderpx; + sizeh->min_height = win.ch + 2 * borderpx; + sizeh->min_width = win.cw + 2 * borderpx; if (xw.isfixed) { - sizeh->flags |= PMaxSize | PMinSize; + sizeh->flags |= PMaxSize; sizeh->min_width = sizeh->max_width = win.w; sizeh->min_height = sizeh->max_height = win.h; } From 1911c9274d9b03f3d7999c6ce26e2d5169642d26 Mon Sep 17 00:00:00 2001 From: Jules Maselbas Date: Sat, 14 Jul 2018 11:16:36 +0200 Subject: [PATCH 079/185] Simplify cursor color handling --- x.c | 30 ++++++++++++------------------ 1 file changed, 12 insertions(+), 18 deletions(-) diff --git a/x.c b/x.c index 00cb6b1..ffd005f 100644 --- a/x.c +++ b/x.c @@ -1418,25 +1418,19 @@ xdrawcursor(int cx, int cy, Glyph g, int ox, int oy, Glyph og) */ g.mode &= ATTR_BOLD|ATTR_ITALIC|ATTR_UNDERLINE|ATTR_STRUCK|ATTR_WIDE; - if (IS_SET(MODE_REVERSE)) { - g.mode |= ATTR_REVERSE; - g.bg = defaultfg; - if (selected(cx, cy)) { - drawcol = dc.col[defaultcs]; - g.fg = defaultrcs; - } else { - drawcol = dc.col[defaultrcs]; - g.fg = defaultcs; - } + if (selected(cx, cy)) { + g.bg = defaultrcs; + g.fg = defaultfg; } else { - if (selected(cx, cy)) { - g.fg = defaultfg; - g.bg = defaultrcs; - } else { - g.fg = defaultbg; - g.bg = defaultcs; - } - drawcol = dc.col[g.bg]; + g.bg = defaultcs; + g.fg = defaultbg; + } + drawcol = dc.col[g.bg]; + + if (IS_SET(MODE_REVERSE)) { + drawcol.color.red = ~drawcol.color.red; + drawcol.color.green = ~drawcol.color.green; + drawcol.color.blue = ~drawcol.color.blue; } /* draw the new one */ From b51bcd5553af3db394014efbd78acf7828fa48ff Mon Sep 17 00:00:00 2001 From: Jules Maselbas Date: Sat, 14 Jul 2018 11:16:37 +0200 Subject: [PATCH 080/185] Make cursor follow text color --- config.def.h | 2 -- x.c | 12 +++++++----- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/config.def.h b/config.def.h index 82b1b09..ca6b0db 100644 --- a/config.def.h +++ b/config.def.h @@ -118,8 +118,6 @@ static const char *colorname[] = { */ unsigned int defaultfg = 7; unsigned int defaultbg = 0; -static unsigned int defaultcs = 256; -static unsigned int defaultrcs = 257; /* * Default shape of cursor diff --git a/x.c b/x.c index ffd005f..b51821d 100644 --- a/x.c +++ b/x.c @@ -1419,13 +1419,15 @@ xdrawcursor(int cx, int cy, Glyph g, int ox, int oy, Glyph og) g.mode &= ATTR_BOLD|ATTR_ITALIC|ATTR_UNDERLINE|ATTR_STRUCK|ATTR_WIDE; if (selected(cx, cy)) { - g.bg = defaultrcs; - g.fg = defaultfg; + drawcol = dc.col[g.bg]; } else { - g.bg = defaultcs; - g.fg = defaultbg; + g.mode |= ATTR_REVERSE; + + if (g.mode & ATTR_BOLD && BETWEEN(g.fg, 0, 7)) + drawcol = dc.col[g.fg + 8]; + else + drawcol = dc.col[g.fg]; } - drawcol = dc.col[g.bg]; if (IS_SET(MODE_REVERSE)) { drawcol.color.red = ~drawcol.color.red; From 5535c1f04c665c05faff2a65d5558246b7748d49 Mon Sep 17 00:00:00 2001 From: Jules Maselbas Date: Sun, 15 Jul 2018 13:53:37 +0200 Subject: [PATCH 081/185] Fix crash when cursor color is truecolor Reported-by: Ivan Tham --- x.c | 17 +++++++++++++---- 1 file changed, 13 insertions(+), 4 deletions(-) diff --git a/x.c b/x.c index b51821d..4155a70 100644 --- a/x.c +++ b/x.c @@ -1404,6 +1404,7 @@ void xdrawcursor(int cx, int cy, Glyph g, int ox, int oy, Glyph og) { Color drawcol; + uint32_t cc; /* remove the old cursor */ if (selected(ox, oy)) @@ -1419,14 +1420,22 @@ xdrawcursor(int cx, int cy, Glyph g, int ox, int oy, Glyph og) g.mode &= ATTR_BOLD|ATTR_ITALIC|ATTR_UNDERLINE|ATTR_STRUCK|ATTR_WIDE; if (selected(cx, cy)) { - drawcol = dc.col[g.bg]; + cc = g.bg; } else { g.mode |= ATTR_REVERSE; - if (g.mode & ATTR_BOLD && BETWEEN(g.fg, 0, 7)) - drawcol = dc.col[g.fg + 8]; + cc = g.fg + 8; else - drawcol = dc.col[g.fg]; + cc = g.fg; + } + + if (IS_TRUECOL(cc)) { + drawcol.color.alpha = 0xffff; + drawcol.color.red = TRUERED(cc); + drawcol.color.green = TRUEGREEN(cc); + drawcol.color.blue = TRUEBLUE(cc); + } else { + drawcol = dc.col[cc]; } if (IS_SET(MODE_REVERSE)) { From 732be223ee7ba5486713c63f944699fd6285af96 Mon Sep 17 00:00:00 2001 From: Hiltjo Posthuma Date: Tue, 17 Jul 2018 20:01:54 +0200 Subject: [PATCH 082/185] Revert "Fix crash when cursor color is truecolor" This reverts commit 5535c1f04c665c05faff2a65d5558246b7748d49. --- x.c | 19 +++++-------------- 1 file changed, 5 insertions(+), 14 deletions(-) diff --git a/x.c b/x.c index 4155a70..b51821d 100644 --- a/x.c +++ b/x.c @@ -1404,7 +1404,6 @@ void xdrawcursor(int cx, int cy, Glyph g, int ox, int oy, Glyph og) { Color drawcol; - uint32_t cc; /* remove the old cursor */ if (selected(ox, oy)) @@ -1420,22 +1419,14 @@ xdrawcursor(int cx, int cy, Glyph g, int ox, int oy, Glyph og) g.mode &= ATTR_BOLD|ATTR_ITALIC|ATTR_UNDERLINE|ATTR_STRUCK|ATTR_WIDE; if (selected(cx, cy)) { - cc = g.bg; + drawcol = dc.col[g.bg]; } else { g.mode |= ATTR_REVERSE; - if (g.mode & ATTR_BOLD && BETWEEN(g.fg, 0, 7)) - cc = g.fg + 8; - else - cc = g.fg; - } - if (IS_TRUECOL(cc)) { - drawcol.color.alpha = 0xffff; - drawcol.color.red = TRUERED(cc); - drawcol.color.green = TRUEGREEN(cc); - drawcol.color.blue = TRUEBLUE(cc); - } else { - drawcol = dc.col[cc]; + if (g.mode & ATTR_BOLD && BETWEEN(g.fg, 0, 7)) + drawcol = dc.col[g.fg + 8]; + else + drawcol = dc.col[g.fg]; } if (IS_SET(MODE_REVERSE)) { From 8ed7a4b3b755407a7724a586ef224051bc306f4f Mon Sep 17 00:00:00 2001 From: Hiltjo Posthuma Date: Tue, 17 Jul 2018 20:01:57 +0200 Subject: [PATCH 083/185] Revert "Make cursor follow text color" This reverts commit b51bcd5553af3db394014efbd78acf7828fa48ff. --- config.def.h | 2 ++ x.c | 12 +++++------- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/config.def.h b/config.def.h index ca6b0db..82b1b09 100644 --- a/config.def.h +++ b/config.def.h @@ -118,6 +118,8 @@ static const char *colorname[] = { */ unsigned int defaultfg = 7; unsigned int defaultbg = 0; +static unsigned int defaultcs = 256; +static unsigned int defaultrcs = 257; /* * Default shape of cursor diff --git a/x.c b/x.c index b51821d..ffd005f 100644 --- a/x.c +++ b/x.c @@ -1419,15 +1419,13 @@ xdrawcursor(int cx, int cy, Glyph g, int ox, int oy, Glyph og) g.mode &= ATTR_BOLD|ATTR_ITALIC|ATTR_UNDERLINE|ATTR_STRUCK|ATTR_WIDE; if (selected(cx, cy)) { - drawcol = dc.col[g.bg]; + g.bg = defaultrcs; + g.fg = defaultfg; } else { - g.mode |= ATTR_REVERSE; - - if (g.mode & ATTR_BOLD && BETWEEN(g.fg, 0, 7)) - drawcol = dc.col[g.fg + 8]; - else - drawcol = dc.col[g.fg]; + g.bg = defaultcs; + g.fg = defaultbg; } + drawcol = dc.col[g.bg]; if (IS_SET(MODE_REVERSE)) { drawcol.color.red = ~drawcol.color.red; From 4f4bccd1627c845330235721f593d2e93418723d Mon Sep 17 00:00:00 2001 From: Hiltjo Posthuma Date: Tue, 17 Jul 2018 20:01:58 +0200 Subject: [PATCH 084/185] Revert "Simplify cursor color handling" This reverts commit 1911c9274d9b03f3d7999c6ce26e2d5169642d26. --- x.c | 30 ++++++++++++++++++------------ 1 file changed, 18 insertions(+), 12 deletions(-) diff --git a/x.c b/x.c index ffd005f..00cb6b1 100644 --- a/x.c +++ b/x.c @@ -1418,19 +1418,25 @@ xdrawcursor(int cx, int cy, Glyph g, int ox, int oy, Glyph og) */ g.mode &= ATTR_BOLD|ATTR_ITALIC|ATTR_UNDERLINE|ATTR_STRUCK|ATTR_WIDE; - if (selected(cx, cy)) { - g.bg = defaultrcs; - g.fg = defaultfg; - } else { - g.bg = defaultcs; - g.fg = defaultbg; - } - drawcol = dc.col[g.bg]; - if (IS_SET(MODE_REVERSE)) { - drawcol.color.red = ~drawcol.color.red; - drawcol.color.green = ~drawcol.color.green; - drawcol.color.blue = ~drawcol.color.blue; + g.mode |= ATTR_REVERSE; + g.bg = defaultfg; + if (selected(cx, cy)) { + drawcol = dc.col[defaultcs]; + g.fg = defaultrcs; + } else { + drawcol = dc.col[defaultrcs]; + g.fg = defaultcs; + } + } else { + if (selected(cx, cy)) { + g.fg = defaultfg; + g.bg = defaultrcs; + } else { + g.fg = defaultbg; + g.bg = defaultcs; + } + drawcol = dc.col[g.bg]; } /* draw the new one */ From 67d0cb65d0794e2d91e72e5fa1e3612172e5812e Mon Sep 17 00:00:00 2001 From: Quentin Rameau Date: Tue, 11 Sep 2018 13:11:28 +0200 Subject: [PATCH 085/185] Remove the ISO 14755 feature And move it to the patches section. Keeping it would force to add an exec pledge on OpenBSD, and some people think it's bloated, so bye! --- config.def.h | 1 - st.1 | 4 ---- st.c | 26 -------------------------- st.h | 1 - 4 files changed, 32 deletions(-) diff --git a/config.def.h b/config.def.h index 82b1b09..823e79f 100644 --- a/config.def.h +++ b/config.def.h @@ -177,7 +177,6 @@ static Shortcut shortcuts[] = { { TERMMOD, XK_V, clippaste, {.i = 0} }, { TERMMOD, XK_Y, selpaste, {.i = 0} }, { TERMMOD, XK_Num_Lock, numlock, {.i = 0} }, - { TERMMOD, XK_I, iso14755, {.i = 0} }, }; /* diff --git a/st.1 b/st.1 index 81bceff..e8d6059 100644 --- a/st.1 +++ b/st.1 @@ -159,10 +159,6 @@ Copy the selected text to the clipboard selection. .TP .B Ctrl-Shift-v Paste from the clipboard selection. -.TP -.B Ctrl-Shift-i -Launch dmenu to enter a unicode codepoint and send the corresponding glyph -to st. .SH CUSTOMIZATION .B st can be customized by creating a custom config.h and (re)compiling the source diff --git a/st.c b/st.c index 76bb3ea..574dbee 100644 --- a/st.c +++ b/st.c @@ -38,15 +38,11 @@ /* macros */ #define IS_SET(flag) ((term.mode & (flag)) != 0) -#define NUMMAXLEN(x) ((int)(sizeof(x) * 2.56 + 0.5) + 1) #define ISCONTROLC0(c) (BETWEEN(c, 0, 0x1f) || (c) == '\177') #define ISCONTROLC1(c) (BETWEEN(c, 0x80, 0x9f)) #define ISCONTROL(c) (ISCONTROLC0(c) || ISCONTROLC1(c)) #define ISDELIM(u) (utf8strchr(worddelimiters, u) != NULL) -/* constants */ -#define ISO14755CMD "dmenu -w \"$WINDOWID\" -p codepoint: 7) - return; - if ((utf32 = strtoul(us, &e, 16)) == ULONG_MAX || - (*e != '\n' && *e != '\0')) - return; - - ttywrite(uc, utf8encode(utf32, uc), 1); -} - void toggleprinter(const Arg *arg) { diff --git a/st.h b/st.h index dac64d8..38c61c4 100644 --- a/st.h +++ b/st.h @@ -80,7 +80,6 @@ void die(const char *, ...); void redraw(void); void draw(void); -void iso14755(const Arg *); void printscreen(const Arg *); void printsel(const Arg *); void sendbreak(const Arg *); From 30ec9a3dc354ab1a561c9edd808046bdf5dfd392 Mon Sep 17 00:00:00 2001 From: Hiltjo Posthuma Date: Tue, 11 Sep 2018 19:06:35 +0200 Subject: [PATCH 086/185] small code-style fix --- st.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/st.c b/st.c index 574dbee..46cf2da 100644 --- a/st.c +++ b/st.c @@ -1452,7 +1452,8 @@ tsetattr(int *attr, int l) } else { fprintf(stderr, "erresc(default): gfx attr %d unknown\n", - attr[i]), csidump(); + attr[i]); + csidump(); } break; } From b4d68d4daa2716c0064605a2a92082a7287ee54a Mon Sep 17 00:00:00 2001 From: Hiltjo Posthuma Date: Sun, 4 Nov 2018 14:30:56 +0100 Subject: [PATCH 087/185] st: small typofix in comment --- st.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/st.c b/st.c index 46cf2da..a51d19c 100644 --- a/st.c +++ b/st.c @@ -2262,7 +2262,7 @@ eschandle(uchar ascii) case 'Z': /* DECID -- Identify Terminal */ ttywrite(vtiden, strlen(vtiden), 0); break; - case 'c': /* RIS -- Reset to inital state */ + case 'c': /* RIS -- Reset to initial state */ treset(); resettitle(); xloadcols(); From d7bf023b2f2d41cb6983bb3ce2c6d1bf049150b3 Mon Sep 17 00:00:00 2001 From: Hiltjo Posthuma Date: Sun, 4 Nov 2018 14:35:07 +0100 Subject: [PATCH 088/185] fix memory leak in xloadcols() reported by Avi Halachmi (:avih)" patch slightly changed by me. --- x.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/x.c b/x.c index 00cb6b1..0422421 100644 --- a/x.c +++ b/x.c @@ -733,12 +733,12 @@ xloadcols(void) static int loaded; Color *cp; - dc.collen = MAX(LEN(colorname), 256); - dc.col = xmalloc(dc.collen * sizeof(Color)); - if (loaded) { for (cp = dc.col; cp < &dc.col[dc.collen]; ++cp) XftColorFree(xw.dpy, xw.vis, xw.cmap, cp); + } else { + dc.collen = MAX(LEN(colorname), 256); + dc.col = xmalloc(dc.collen * sizeof(Color)); } for (i = 0; i < dc.collen; i++) From 096b125db7ec254002a049ef3a5c1204bd7f0ad9 Mon Sep 17 00:00:00 2001 From: Lauri Tirkkonen Date: Tue, 11 Dec 2018 11:43:03 +0200 Subject: [PATCH 089/185] output child WEXITSTATUS/WTERMSIG on abnormal termination --- st.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/st.c b/st.c index a51d19c..b8e6077 100644 --- a/st.c +++ b/st.c @@ -731,8 +731,10 @@ sigchld(int a) if (pid != p) return; - if (!WIFEXITED(stat) || WEXITSTATUS(stat)) - die("child finished with error '%d'\n", stat); + if (WIFEXITED(stat) && WEXITSTATUS(stat)) + die("child exited with status %d\n", WEXITSTATUS(stat)); + else if (WIFSIGNALED(stat)) + die("child terminated due to signal %d\n", WTERMSIG(stat)); exit(0); } From 7e19e116764d2651721cb9062ccaac31b0c771f2 Mon Sep 17 00:00:00 2001 From: Hiltjo Posthuma Date: Fri, 4 Jan 2019 12:33:01 +0100 Subject: [PATCH 090/185] Makefile: fix dependencies on config.h patch by Younes Khoudli (changed slightly). Thanks --- Makefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Makefile b/Makefile index 0b3cecd..470ac86 100644 --- a/Makefile +++ b/Makefile @@ -22,7 +22,7 @@ config.h: $(CC) $(STCFLAGS) -c $< st.o: config.h st.h win.h -x.o: arg.h st.h win.h +x.o: arg.h config.h st.h win.h $(OBJ): config.h config.mk From e23acb9188b7bc7a7cfc418ac45671003abcc341 Mon Sep 17 00:00:00 2001 From: Paride Legovini Date: Fri, 4 Jan 2019 09:48:37 +0100 Subject: [PATCH 091/185] Set the path of pkg-config in a variable instead of hardcoding it In this way the path of pkg-config can be overridden from the command line. This is useful for example when cross-compiling. --- config.mk | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/config.mk b/config.mk index e676105..64a1da9 100644 --- a/config.mk +++ b/config.mk @@ -10,13 +10,15 @@ MANPREFIX = $(PREFIX)/share/man X11INC = /usr/X11R6/include X11LIB = /usr/X11R6/lib +PKG_CONFIG = pkg-config + # includes and libs INCS = -I$(X11INC) \ - `pkg-config --cflags fontconfig` \ - `pkg-config --cflags freetype2` + `$(PKG_CONFIG) --cflags fontconfig` \ + `$(PKG_CONFIG) --cflags freetype2` LIBS = -L$(X11LIB) -lm -lrt -lX11 -lutil -lXft \ - `pkg-config --libs fontconfig` \ - `pkg-config --libs freetype2` + `$(PKG_CONFIG) --libs fontconfig` \ + `$(PKG_CONFIG) --libs freetype2` # flags CPPFLAGS = -DVERSION=\"$(VERSION)\" -D_XOPEN_SOURCE=600 From 16d98738e74f189b3a00c23390ab5a9a22b056a1 Mon Sep 17 00:00:00 2001 From: Paride Legovini Date: Thu, 10 Jan 2019 13:36:09 +0100 Subject: [PATCH 092/185] Let the user specify CPPFLAGS This complements the work done in d4928ed, allowing the user to specify the preprocessor flags with the CPPFLAGS environment variable. This is useful for example to specify preprocessor macros with -D. CFLAGS could be used instead, but CPPFLAGS is more correct and is expected to be honored in some cases. For example, the helper scripts to build Debian packages make use of CPPFLAGS, but the variable is currently being ignored unless manually appended to CFLAGS. --- config.mk | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/config.mk b/config.mk index 64a1da9..5059632 100644 --- a/config.mk +++ b/config.mk @@ -21,8 +21,8 @@ LIBS = -L$(X11LIB) -lm -lrt -lX11 -lutil -lXft \ `$(PKG_CONFIG) --libs freetype2` # flags -CPPFLAGS = -DVERSION=\"$(VERSION)\" -D_XOPEN_SOURCE=600 -STCFLAGS = $(INCS) $(CPPFLAGS) $(CFLAGS) +STCPPFLAGS = -DVERSION=\"$(VERSION)\" -D_XOPEN_SOURCE=600 +STCFLAGS = $(INCS) $(STCPPFLAGS) $(CPPFLAGS) $(CFLAGS) STLDFLAGS = $(LIBS) $(LDFLAGS) # OpenBSD: From 3be4cf11d79ca87ff1fbbb57801913ec6f822429 Mon Sep 17 00:00:00 2001 From: Hiltjo Posthuma Date: Thu, 10 Jan 2019 18:16:17 +0100 Subject: [PATCH 093/185] config: add Shift+Insert as selpaste() again This was changed before in: commit 20f713548de451b67db3306cf8cf7b2f38fee05c on Wed Jan 25 19:17:38 2017 --- config.def.h | 1 + 1 file changed, 1 insertion(+) diff --git a/config.def.h b/config.def.h index 823e79f..0e01717 100644 --- a/config.def.h +++ b/config.def.h @@ -176,6 +176,7 @@ static Shortcut shortcuts[] = { { TERMMOD, XK_C, clipcopy, {.i = 0} }, { TERMMOD, XK_V, clippaste, {.i = 0} }, { TERMMOD, XK_Y, selpaste, {.i = 0} }, + { ShiftMask, XK_Insert, selpaste, {.i = 0} }, { TERMMOD, XK_Num_Lock, numlock, {.i = 0} }, }; From 75f92eb3489275bfcad901e5ca424134eda6e2f4 Mon Sep 17 00:00:00 2001 From: Hiltjo Posthuma Date: Sat, 9 Feb 2019 12:48:39 +0100 Subject: [PATCH 094/185] bump version to 0.8.2 --- config.mk | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/config.mk b/config.mk index 5059632..0cbb002 100644 --- a/config.mk +++ b/config.mk @@ -1,5 +1,5 @@ # st version -VERSION = 0.8.1 +VERSION = 0.8.2 # Customize below to fit your system From e85b6b64660214121164ea97fb098eaa4935f7db Mon Sep 17 00:00:00 2001 From: Ivan Tham Date: Tue, 12 Feb 2019 18:41:41 +0100 Subject: [PATCH 095/185] better Input Method Editor (IME) support Features: - Allow input methods swap with hotkey (E.g. left ctrl + left shift). - Over-the-spot pre-editing style, pre-edit data placed over insertion point. - Restart IME without segmentation fault. TODO: - Automatically pickup IME if st started before IME --- st.c | 1 + win.h | 1 + x.c | 69 ++++++++++++++++++++++++++++++++++++++++++++--------------- 3 files changed, 54 insertions(+), 17 deletions(-) diff --git a/st.c b/st.c index b8e6077..cf8687e 100644 --- a/st.c +++ b/st.c @@ -2594,6 +2594,7 @@ draw(void) term.ocx, term.ocy, term.line[term.ocy][term.ocx]); term.ocx = cx, term.ocy = term.c.y; xfinishdraw(); + xximspot(term.ocx, term.ocy); } void diff --git a/win.h b/win.h index 31f327d..a6ef1b9 100644 --- a/win.h +++ b/win.h @@ -36,3 +36,4 @@ void xsetmode(int, unsigned int); void xsetpointermotion(int); void xsetsel(char *); int xstartdraw(void); +void xximspot(int, int); diff --git a/x.c b/x.c index 0422421..865dacc 100644 --- a/x.c +++ b/x.c @@ -139,6 +139,9 @@ static void xdrawglyphfontspecs(const XftGlyphFontSpec *, Glyph, int, int, int); static void xdrawglyph(Glyph, int, int); static void xclear(int, int, int, int); static int xgeommasktogravity(int); +static void ximopen(Display *); +static void ximinstantiate(Display *, XPointer, XPointer); +static void ximdestroy(XIM, XPointer, XPointer); static void xinit(int, int); static void cresize(int, int); static void xresize(int, int); @@ -996,6 +999,43 @@ xunloadfonts(void) xunloadfont(&dc.ibfont); } +void +ximopen(Display *dpy) +{ + XIMCallback destroy = { .client_data = NULL, .callback = ximdestroy }; + + if ((xw.xim = XOpenIM(xw.dpy, NULL, NULL, NULL)) == NULL) { + XSetLocaleModifiers("@im=local"); + if ((xw.xim = XOpenIM(xw.dpy, NULL, NULL, NULL)) == NULL) { + XSetLocaleModifiers("@im="); + if ((xw.xim = XOpenIM(xw.dpy, NULL, NULL, NULL)) == NULL) + die("XOpenIM failed. Could not open input device.\n"); + } + } + if (XSetIMValues(xw.xim, XNDestroyCallback, &destroy, NULL) != NULL) + die("XSetIMValues failed. Could not set input method value.\n"); + xw.xic = XCreateIC(xw.xim, XNInputStyle, XIMPreeditNothing | XIMStatusNothing, + XNClientWindow, xw.win, XNFocusWindow, xw.win, NULL); + if (xw.xic == NULL) + die("XCreateIC failed. Could not obtain input method.\n"); +} + +void +ximinstantiate(Display *dpy, XPointer client, XPointer call) +{ + ximopen(dpy); + XUnregisterIMInstantiateCallback(xw.dpy, NULL, NULL, NULL, + ximinstantiate, NULL); +} + +void +ximdestroy(XIM xim, XPointer client, XPointer call) +{ + xw.xim = NULL; + XRegisterIMInstantiateCallback(xw.dpy, NULL, NULL, NULL, + ximinstantiate, NULL); +} + void xinit(int cols, int rows) { @@ -1033,7 +1073,7 @@ xinit(int cols, int rows) xw.attrs.background_pixel = dc.col[defaultbg].pixel; xw.attrs.border_pixel = dc.col[defaultbg].pixel; xw.attrs.bit_gravity = NorthWestGravity; - xw.attrs.event_mask = FocusChangeMask | KeyPressMask + xw.attrs.event_mask = FocusChangeMask | KeyPressMask | KeyReleaseMask | ExposureMask | VisibilityChangeMask | StructureNotifyMask | ButtonMotionMask | ButtonPressMask | ButtonReleaseMask; xw.attrs.colormap = xw.cmap; @@ -1061,22 +1101,7 @@ xinit(int cols, int rows) xw.draw = XftDrawCreate(xw.dpy, xw.buf, xw.vis, xw.cmap); /* input methods */ - if ((xw.xim = XOpenIM(xw.dpy, NULL, NULL, NULL)) == NULL) { - XSetLocaleModifiers("@im=local"); - if ((xw.xim = XOpenIM(xw.dpy, NULL, NULL, NULL)) == NULL) { - XSetLocaleModifiers("@im="); - if ((xw.xim = XOpenIM(xw.dpy, - NULL, NULL, NULL)) == NULL) { - die("XOpenIM failed. Could not open input" - " device.\n"); - } - } - } - xw.xic = XCreateIC(xw.xim, XNInputStyle, XIMPreeditNothing - | XIMStatusNothing, XNClientWindow, xw.win, - XNFocusWindow, xw.win, NULL); - if (xw.xic == NULL) - die("XCreateIC failed. Could not obtain input method.\n"); + ximopen(xw.dpy); /* white cursor, black outline */ cursor = XCreateFontCursor(xw.dpy, mouseshape); @@ -1554,6 +1579,16 @@ xfinishdraw(void) defaultfg : defaultbg].pixel); } +void +xximspot(int x, int y) +{ + XPoint spot = { borderpx + x * win.cw, borderpx + (y + 1) * win.ch }; + XVaNestedList attr = XVaCreateNestedList(0, XNSpotLocation, &spot, NULL); + + XSetICValues(xw.xic, XNPreeditAttributes, attr, NULL); + XFree(attr); +} + void expose(XEvent *ev) { From a8cb8e94547d7e31441d2444e8a196415e3e4c1f Mon Sep 17 00:00:00 2001 From: magras Date: Thu, 28 Feb 2019 04:56:01 +0300 Subject: [PATCH 096/185] fix use after free in font caching algorithm Current font caching algorithm contains a use after free error. A font removed from `frc` might be still listed in `wx.specbuf`. It will lead to a crash inside `XftDrawGlyphFontSpec()`. Steps to reproduce: $ st -f 'Misc Tamsyn:scalable=false' $ curl https://www.cl.cam.ac.uk/~mgk25/ucs/examples/UTF-8-demo.txt Of course, result depends on fonts installed on a system and fontconfig. In my case, I'm getting consistent segfaults with different fonts. I replaced a fixed array with a simple unbounded buffer with a constant growth rate. Cache starts with a capacity of 0, gets increments by 16, and never shrinks. On my machine after `cat UTF-8-demo.txt` buffer reaches a capacity of 192. During casual use capacity stays at 0. --- x.c | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/x.c b/x.c index 865dacc..2cd76d0 100644 --- a/x.c +++ b/x.c @@ -226,8 +226,9 @@ typedef struct { } Fontcache; /* Fontcache is an array now. A new font will be appended to the array. */ -static Fontcache frc[16]; +static Fontcache *frc = NULL; static int frclen = 0; +static int frccap = 0; static char *usedfont = NULL; static double usedfontsize = 0; static double defaultfontsize = 0; @@ -1244,12 +1245,14 @@ xmakeglyphfontspecs(XftGlyphFontSpec *specs, const Glyph *glyphs, int len, int x fcpattern, &fcres); /* - * Overwrite or create the new cache entry. + * Allocate memory for the new cache entry. */ - if (frclen >= LEN(frc)) { - frclen = LEN(frc) - 1; - XftFontClose(xw.dpy, frc[frclen].font); - frc[frclen].unicodep = 0; + if (frclen >= frccap) { + frccap += 16; + if (!frc) + frc = xmalloc(frccap * sizeof(Fontcache)); + else + frc = xrealloc(frc, frccap * sizeof(Fontcache)); } frc[frclen].font = XftFontOpenPattern(xw.dpy, From 4e0135afeca43f5affe13d7269cb98e7ac526074 Mon Sep 17 00:00:00 2001 From: Hiltjo Posthuma Date: Sun, 3 Mar 2019 11:23:54 +0100 Subject: [PATCH 097/185] style: remove double empty newlines --- st.c | 1 - x.c | 2 -- 2 files changed, 3 deletions(-) diff --git a/st.c b/st.c index cf8687e..379dd10 100644 --- a/st.c +++ b/st.c @@ -2335,7 +2335,6 @@ tputc(Rune u) goto check_control_code; } - if (IS_SET(MODE_SIXEL)) { /* TODO: implement sixel mode */ return; diff --git a/x.c b/x.c index 2cd76d0..aa86b31 100644 --- a/x.c +++ b/x.c @@ -763,7 +763,6 @@ xsetcolorname(int x, const char *name) if (!BETWEEN(x, 0, dc.collen)) return 1; - if (!xloadcolor(x, name, &ncolor)) return 1; @@ -1769,7 +1768,6 @@ kpress(XEvent *ev) ttywrite(buf, len, 1); } - void cmessage(XEvent *e) { From ed68fe7dce2b21b4e0e595b99d47790e76812cb7 Mon Sep 17 00:00:00 2001 From: Hiltjo Posthuma Date: Sun, 3 Mar 2019 11:29:43 +0100 Subject: [PATCH 098/185] simplify (greedy) font caching allocating a bit POSIX says: "If ptr is a null pointer, realloc() shall be equivalent to malloc() for the specified size." --- x.c | 9 ++------- 1 file changed, 2 insertions(+), 7 deletions(-) diff --git a/x.c b/x.c index aa86b31..5828a3b 100644 --- a/x.c +++ b/x.c @@ -1243,15 +1243,10 @@ xmakeglyphfontspecs(XftGlyphFontSpec *specs, const Glyph *glyphs, int len, int x fontpattern = FcFontSetMatch(0, fcsets, 1, fcpattern, &fcres); - /* - * Allocate memory for the new cache entry. - */ + /* Allocate memory for the new cache entry. */ if (frclen >= frccap) { frccap += 16; - if (!frc) - frc = xmalloc(frccap * sizeof(Fontcache)); - else - frc = xrealloc(frc, frccap * sizeof(Fontcache)); + frc = xrealloc(frc, frccap * sizeof(Fontcache)); } frc[frclen].font = XftFontOpenPattern(xw.dpy, From 75b4ba4b4be70a3ae429b1719d18b021839216d5 Mon Sep 17 00:00:00 2001 From: Lauri Tirkkonen Date: Wed, 13 Mar 2019 17:08:50 +0200 Subject: [PATCH 099/185] be silent about explicitly unhandled mouse modes --- st.c | 1 + 1 file changed, 1 insertion(+) diff --git a/st.c b/st.c index 379dd10..d35f89d 100644 --- a/st.c +++ b/st.c @@ -1575,6 +1575,7 @@ tsetmode(int priv, int set, int *args, int narg) case 1015: /* urxvt mangled mouse mode; incompatible and can be mistaken for other control codes. */ + break; default: fprintf(stderr, "erresc: unknown private set/reset mode %d\n", From d5efd256aa3840476579a27293ef1fb92a4b51e7 Mon Sep 17 00:00:00 2001 From: Lauri Tirkkonen Date: Wed, 13 Mar 2019 19:40:52 +0200 Subject: [PATCH 100/185] replace utf8strchr with wcschr --- config.def.h | 4 ++-- st.c | 20 +------------------- st.h | 2 +- 3 files changed, 4 insertions(+), 22 deletions(-) diff --git a/config.def.h b/config.def.h index 0e01717..482901e 100644 --- a/config.def.h +++ b/config.def.h @@ -30,9 +30,9 @@ static float chscale = 1.0; /* * word delimiter string * - * More advanced example: " `'\"()[]{}" + * More advanced example: L" `'\"()[]{}" */ -char *worddelimiters = " "; +wchar_t *worddelimiters = L" "; /* selection timeouts (in milliseconds) */ static unsigned int doubleclicktimeout = 300; diff --git a/st.c b/st.c index d35f89d..812f30c 100644 --- a/st.c +++ b/st.c @@ -41,7 +41,7 @@ #define ISCONTROLC0(c) (BETWEEN(c, 0, 0x1f) || (c) == '\177') #define ISCONTROLC1(c) (BETWEEN(c, 0x80, 0x9f)) #define ISCONTROL(c) (ISCONTROLC0(c) || ISCONTROLC1(c)) -#define ISDELIM(u) (utf8strchr(worddelimiters, u) != NULL) +#define ISDELIM(u) (u != 0 && wcschr(worddelimiters, u) != NULL) enum term_mode { MODE_WRAP = 1 << 0, @@ -210,7 +210,6 @@ static void selsnap(int *, int *, int); static size_t utf8decode(const char *, Rune *, size_t); static Rune utf8decodebyte(char, size_t *); static char utf8encodebyte(Rune, size_t); -static char *utf8strchr(char *, Rune); static size_t utf8validate(Rune *, size_t); static char *base64dec(const char *); @@ -337,23 +336,6 @@ utf8encodebyte(Rune u, size_t i) return utfbyte[i] | (u & ~utfmask[i]); } -char * -utf8strchr(char *s, Rune u) -{ - Rune r; - size_t i, j, len; - - len = strlen(s); - for (i = 0, j = 0; i < len; i += j) { - if (!(j = utf8decode(&s[i], &r, len - i))) - break; - if (r == u) - return &(s[i]); - } - - return NULL; -} - size_t utf8validate(Rune *u, size_t i) { diff --git a/st.h b/st.h index 38c61c4..4da3051 100644 --- a/st.h +++ b/st.h @@ -114,7 +114,7 @@ char *xstrdup(char *); extern char *utmp; extern char *stty_args; extern char *vtiden; -extern char *worddelimiters; +extern wchar_t *worddelimiters; extern int allowaltscreen; extern char *termname; extern unsigned int tabspaces; From add0211522737b79dad990ccd65c8af63b5cc1dd Mon Sep 17 00:00:00 2001 From: Lauri Tirkkonen Date: Wed, 13 Mar 2019 17:15:04 +0200 Subject: [PATCH 101/185] use iswspace()/iswpunct() to find word delimiters this inverts the configuration logic: you no longer provide a list of delimiters -- all space and punctuation characters are considered delimiters, unless listed in extrawordchars. --- config.def.h | 7 ++++--- st.c | 3 ++- st.h | 2 +- 3 files changed, 7 insertions(+), 5 deletions(-) diff --git a/config.def.h b/config.def.h index 482901e..9ce45a7 100644 --- a/config.def.h +++ b/config.def.h @@ -28,11 +28,12 @@ static float cwscale = 1.0; static float chscale = 1.0; /* - * word delimiter string + * all space and punctuation characters are considered word delimiters, unless + * listed here. * - * More advanced example: L" `'\"()[]{}" + * More advanced example: L"#$%&+,-./:=?_~" */ -wchar_t *worddelimiters = L" "; +wchar_t *extrawordchars = L"./:"; /* selection timeouts (in milliseconds) */ static unsigned int doubleclicktimeout = 300; diff --git a/st.c b/st.c index 812f30c..c383b43 100644 --- a/st.c +++ b/st.c @@ -16,6 +16,7 @@ #include #include #include +#include #include "st.h" #include "win.h" @@ -41,7 +42,7 @@ #define ISCONTROLC0(c) (BETWEEN(c, 0, 0x1f) || (c) == '\177') #define ISCONTROLC1(c) (BETWEEN(c, 0x80, 0x9f)) #define ISCONTROL(c) (ISCONTROLC0(c) || ISCONTROLC1(c)) -#define ISDELIM(u) (u != 0 && wcschr(worddelimiters, u) != NULL) +#define ISDELIM(u) ((iswspace(u) || iswpunct(u)) && wcschr(extrawordchars, u) == NULL) enum term_mode { MODE_WRAP = 1 << 0, diff --git a/st.h b/st.h index 4da3051..a3b19de 100644 --- a/st.h +++ b/st.h @@ -114,7 +114,7 @@ char *xstrdup(char *); extern char *utmp; extern char *stty_args; extern char *vtiden; -extern wchar_t *worddelimiters; +extern wchar_t *extrawordchars; extern int allowaltscreen; extern char *termname; extern unsigned int tabspaces; From 927621f6da015f51710c03279b00c6cc38057e32 Mon Sep 17 00:00:00 2001 From: Hiltjo Posthuma Date: Fri, 15 Mar 2019 12:31:54 +0100 Subject: [PATCH 102/185] config.def.h: tweak extra worddelimiters This changes the selection more like xterm. To test try: "find /" and select a path. --- config.def.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/config.def.h b/config.def.h index 9ce45a7..ac5e8ce 100644 --- a/config.def.h +++ b/config.def.h @@ -33,7 +33,7 @@ static float chscale = 1.0; * * More advanced example: L"#$%&+,-./:=?_~" */ -wchar_t *extrawordchars = L"./:"; +wchar_t *extrawordchars = L""; /* selection timeouts (in milliseconds) */ static unsigned int doubleclicktimeout = 300; From 9acec468fbeaa9f90578352b610431ca9b2d4ee4 Mon Sep 17 00:00:00 2001 From: Hiltjo Posthuma Date: Fri, 15 Mar 2019 14:42:50 +0100 Subject: [PATCH 103/185] minor code-style, initialize var at the top of function --- st.c | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/st.c b/st.c index c383b43..f342e5c 100644 --- a/st.c +++ b/st.c @@ -1830,7 +1830,7 @@ csireset(void) void strhandle(void) { - char *p = NULL; + char *p = NULL, *dec; int j, narg, par; term.esc &= ~(ESC_STR_END|ESC_STR); @@ -1848,8 +1848,6 @@ strhandle(void) return; case 52: if (narg > 2) { - char *dec; - dec = base64dec(strescseq.args[2]); if (dec) { xsetsel(dec); From b650256044f867851725f712fdac58d4ff294808 Mon Sep 17 00:00:00 2001 From: Hiltjo Posthuma Date: Fri, 15 Mar 2019 14:44:28 +0100 Subject: [PATCH 104/185] dont print color warning on color reset OSC 104 without parameter also print explicitly "(null)" when printf "%s" p=NULL. noticed when exiting mutt: printf '\x1b]104\x07' --- st.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/st.c b/st.c index f342e5c..48d65ca 100644 --- a/st.c +++ b/st.c @@ -1865,7 +1865,10 @@ strhandle(void) case 104: /* color reset, here p = NULL */ j = (narg > 1) ? atoi(strescseq.args[1]) : -1; if (xsetcolorname(j, p)) { - fprintf(stderr, "erresc: invalid color %s\n", p); + if (par == 104 && narg <= 1) + return; /* color reset without parameter */ + fprintf(stderr, "erresc: invalid color j=%d, p=%s\n", + j, p ? p : "(null)"); } else { /* * TODO if defaultbg color is changed, borders From 21367a040f056f6a207fafa066bd1cb2d9cae586 Mon Sep 17 00:00:00 2001 From: Hiltjo Posthuma Date: Fri, 15 Mar 2019 20:40:16 +0100 Subject: [PATCH 105/185] revert part of commit add0211522737b79dad990ccd65c8af63b5cc1dd "use iswspace()/iswpunct() to find word delimiters this inverts the configuration logic: you no longer provide a list of delimiters -- all space and punctuation characters are considered delimiters, unless listed in extrawordchars." Feedback from IRC and personal preference. --- config.def.h | 7 +++---- st.c | 3 +-- st.h | 2 +- 3 files changed, 5 insertions(+), 7 deletions(-) diff --git a/config.def.h b/config.def.h index ac5e8ce..482901e 100644 --- a/config.def.h +++ b/config.def.h @@ -28,12 +28,11 @@ static float cwscale = 1.0; static float chscale = 1.0; /* - * all space and punctuation characters are considered word delimiters, unless - * listed here. + * word delimiter string * - * More advanced example: L"#$%&+,-./:=?_~" + * More advanced example: L" `'\"()[]{}" */ -wchar_t *extrawordchars = L""; +wchar_t *worddelimiters = L" "; /* selection timeouts (in milliseconds) */ static unsigned int doubleclicktimeout = 300; diff --git a/st.c b/st.c index 48d65ca..8e6ccb5 100644 --- a/st.c +++ b/st.c @@ -16,7 +16,6 @@ #include #include #include -#include #include "st.h" #include "win.h" @@ -42,7 +41,7 @@ #define ISCONTROLC0(c) (BETWEEN(c, 0, 0x1f) || (c) == '\177') #define ISCONTROLC1(c) (BETWEEN(c, 0x80, 0x9f)) #define ISCONTROL(c) (ISCONTROLC0(c) || ISCONTROLC1(c)) -#define ISDELIM(u) ((iswspace(u) || iswpunct(u)) && wcschr(extrawordchars, u) == NULL) +#define ISDELIM(u) (u && wcschr(worddelimiters, u)) enum term_mode { MODE_WRAP = 1 << 0, diff --git a/st.h b/st.h index a3b19de..4da3051 100644 --- a/st.h +++ b/st.h @@ -114,7 +114,7 @@ char *xstrdup(char *); extern char *utmp; extern char *stty_args; extern char *vtiden; -extern wchar_t *extrawordchars; +extern wchar_t *worddelimiters; extern int allowaltscreen; extern char *termname; extern unsigned int tabspaces; From f1546cf9c1f9fc52d26dbbcf73210901e83c7ecf Mon Sep 17 00:00:00 2001 From: "Avi Halachmi (:avih)" Date: Wed, 10 Apr 2019 01:54:43 +0300 Subject: [PATCH 106/185] selection: fix view to match actual selection on first cell --- st.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/st.c b/st.c index 8e6ccb5..ede7ae6 100644 --- a/st.c +++ b/st.c @@ -458,7 +458,7 @@ selextend(int col, int row, int type, int done) selnormalize(); sel.type = type; - if (oldey != sel.oe.y || oldex != sel.oe.x || oldtype != sel.type) + if (oldey != sel.oe.y || oldex != sel.oe.x || oldtype != sel.type || sel.mode == SEL_EMPTY) tsetdirt(MIN(sel.nb.y, oldsby), MAX(sel.ne.y, oldsey)); sel.mode = done ? SEL_IDLE : SEL_READY; From caa1d8fbea2b92bca24652af0fee874bdbbbb3e5 Mon Sep 17 00:00:00 2001 From: Hiltjo Posthuma Date: Fri, 17 May 2019 13:00:10 +0200 Subject: [PATCH 107/185] FAQ: add entry about color emoji Xft bug This has been asked many times on IRC and the mailinglist. Make it easier to find information about this particular Xft issue by adding it to the FAQ. --- FAQ | 27 +++++++++++++++++++++++++++ 1 file changed, 27 insertions(+) diff --git a/FAQ b/FAQ index 921c493..ecf7af8 100644 --- a/FAQ +++ b/FAQ @@ -165,3 +165,30 @@ Apply [1]. [1] http://st.suckless.org/patches/delkey +## BadLength X error in Xft when trying to render emoji + +Xft makes st crash when rendering color emojis with the following error: + +"X Error of failed request: BadLength (poly request too large or internal Xlib length error)" + Major opcode of failed request: 139 (RENDER) + Minor opcode of failed request: 20 (RenderAddGlyphs) + Serial number of failed request: 1595 + Current serial number in output stream: 1818" + +This is a known bug in Xft (not st) which happens on some platforms and +combination of particular fonts and fontconfig settings. + +See also: +https://gitlab.freedesktop.org/xorg/lib/libxft/issues/6 +https://bugs.freedesktop.org/show_bug.cgi?id=107534 +https://bugzilla.redhat.com/show_bug.cgi?id=1498269 + +The solution is to remove color emoji fonts or disable this in the fontconfig +XML configuration. As an ugly workaround (which may work only on newer +fontconfig versions (FC_COLOR)), the following code can be used to mask color +fonts: + + FcPatternAddBool(fcpattern, FC_COLOR, FcFalse); + +Please don't bother reporting this bug to st, but notify the upstream Xft +developers about fixing this bug. From 2b8333f553c14c15398e810353e192eb05938580 Mon Sep 17 00:00:00 2001 From: Hiltjo Posthuma Date: Mon, 26 Aug 2019 17:58:47 +0200 Subject: [PATCH 108/185] config.def.h: remove crlf value section this is not used anymore. patch sent as an ed script using RFC2549 by k0ga. --- config.def.h | 4 ---- 1 file changed, 4 deletions(-) diff --git a/config.def.h b/config.def.h index 482901e..6ebea98 100644 --- a/config.def.h +++ b/config.def.h @@ -195,10 +195,6 @@ static Shortcut shortcuts[] = { * * 0: no value * * > 0: cursor application mode enabled * * < 0: cursor application mode disabled - * crlf value - * * 0: no value - * * > 0: crlf mode is enabled - * * < 0: crlf mode is disabled * * Be careful with the order of the definitions because st searches in * this table sequentially, so any XK_ANY_MOD must be in the last From ba7f4d69af62d20e13fea78a408095e017410651 Mon Sep 17 00:00:00 2001 From: "Avi Halachmi (:avih)" Date: Thu, 10 Oct 2019 23:02:26 +0300 Subject: [PATCH 109/185] mouse shortcuts: allow same functions as kb shortcuts Previously mouse shortcuts supported only ttywrite. This required adding an "Arg" function ttysend - which does what the original mouse shortcuts did. --- config.def.h | 6 +++--- st.h | 1 + x.c | 20 ++++++++++++++------ 3 files changed, 18 insertions(+), 9 deletions(-) diff --git a/config.def.h b/config.def.h index 6ebea98..36ff6ce 100644 --- a/config.def.h +++ b/config.def.h @@ -155,9 +155,9 @@ static unsigned int defaultattr = 11; * Beware that overloading Button1 will disable the selection. */ static MouseShortcut mshortcuts[] = { - /* button mask string */ - { Button4, XK_ANY_MOD, "\031" }, - { Button5, XK_ANY_MOD, "\005" }, + /* mask button function argument */ + { XK_ANY_MOD, Button4, ttysend, {.s = "\031"} }, + { XK_ANY_MOD, Button5, ttysend, {.s = "\005"} }, }; /* Internal keyboard shortcuts. */ diff --git a/st.h b/st.h index 4da3051..a1928ca 100644 --- a/st.h +++ b/st.h @@ -74,6 +74,7 @@ typedef union { uint ui; float f; const void *v; + const char *s; } Arg; void die(const char *, ...); diff --git a/x.c b/x.c index 5828a3b..2a05a81 100644 --- a/x.c +++ b/x.c @@ -29,9 +29,10 @@ typedef struct { } Shortcut; typedef struct { - uint b; - uint mask; - char *s; + uint mod; + uint button; + void (*func)(const Arg *); + const Arg arg; } MouseShortcut; typedef struct { @@ -56,6 +57,7 @@ static void selpaste(const Arg *); static void zoom(const Arg *); static void zoomabs(const Arg *); static void zoomreset(const Arg *); +static void ttysend(const Arg *); /* config.h for applying patches and the configuration. */ #include "config.h" @@ -312,6 +314,12 @@ zoomreset(const Arg *arg) } } +void +ttysend(const Arg *arg) +{ + ttywrite(arg->s, strlen(arg->s), 1); +} + int evcol(XEvent *e) { @@ -421,9 +429,9 @@ bpress(XEvent *e) } for (ms = mshortcuts; ms < mshortcuts + LEN(mshortcuts); ms++) { - if (e->xbutton.button == ms->b - && match(ms->mask, e->xbutton.state)) { - ttywrite(ms->s, strlen(ms->s), 1); + if (e->xbutton.button == ms->button + && match(ms->mod, e->xbutton.state)) { + ms->func(&(ms->arg)); return; } } From b6d280de6df30167ce9cf30fadefc362e77729e7 Mon Sep 17 00:00:00 2001 From: "Avi Halachmi (:avih)" Date: Thu, 10 Oct 2019 23:42:30 +0300 Subject: [PATCH 110/185] mouse shortcuts: allow override for all shortcuts Allow forceselmod to override all mouse shortcuts rather than only selection, and rename it to forcemousemod as it's now more appropriate. This will affect mouse shortcuts which use mask other than XK_ANY_MOD. This does not affect the default behavior because the default mouse shortcuts (wheel) use XK_ANY_MOD, where forceselmod already activated the override also before this change. Previously, if a mouse shortcut was configured with a specific mod and forceselmod was held, then the shortcut did not execute unless the configured mod included forceselmod. --- config.def.h | 14 +++++++------- x.c | 12 ++++++------ 2 files changed, 13 insertions(+), 13 deletions(-) diff --git a/config.def.h b/config.def.h index 36ff6ce..a0a0d2d 100644 --- a/config.def.h +++ b/config.def.h @@ -150,6 +150,13 @@ static unsigned int mousebg = 0; */ static unsigned int defaultattr = 11; +/* + * Force mouse select/shortcuts while mask is active (when MODE_MOUSE is set). + * Note that if you want to use ShiftMask with selmasks, set this to an other + * modifier, set to 0 to not use it. + */ +static uint forcemousemod = ShiftMask; + /* * Internal mouse shortcuts. * Beware that overloading Button1 will disable the selection. @@ -213,13 +220,6 @@ static KeySym mappedkeys[] = { -1 }; */ static uint ignoremod = Mod2Mask|XK_SWITCH_MOD; -/* - * Override mouse-select while mask is active (when MODE_MOUSE is set). - * Note that if you want to use ShiftMask with selmasks, set this to an other - * modifier, set to 0 to not use it. - */ -static uint forceselmod = ShiftMask; - /* * This is the huge key array which defines all compatibility to the Linux * world. Please decide about changes wisely. diff --git a/x.c b/x.c index 2a05a81..c967caf 100644 --- a/x.c +++ b/x.c @@ -340,7 +340,7 @@ void mousesel(XEvent *e, int done) { int type, seltype = SEL_REGULAR; - uint state = e->xbutton.state & ~(Button1Mask | forceselmod); + uint state = e->xbutton.state & ~(Button1Mask | forcemousemod); for (type = 1; type < LEN(selmasks); ++type) { if (match(selmasks[type], state)) { @@ -423,14 +423,14 @@ bpress(XEvent *e) MouseShortcut *ms; int snap; - if (IS_SET(MODE_MOUSE) && !(e->xbutton.state & forceselmod)) { + if (IS_SET(MODE_MOUSE) && !(e->xbutton.state & forcemousemod)) { mousereport(e); return; } for (ms = mshortcuts; ms < mshortcuts + LEN(mshortcuts); ms++) { - if (e->xbutton.button == ms->button - && match(ms->mod, e->xbutton.state)) { + if (e->xbutton.button == ms->button && + match(ms->mod, e->xbutton.state & ~forcemousemod)) { ms->func(&(ms->arg)); return; } @@ -650,7 +650,7 @@ xsetsel(char *str) void brelease(XEvent *e) { - if (IS_SET(MODE_MOUSE) && !(e->xbutton.state & forceselmod)) { + if (IS_SET(MODE_MOUSE) && !(e->xbutton.state & forcemousemod)) { mousereport(e); return; } @@ -664,7 +664,7 @@ brelease(XEvent *e) void bmotion(XEvent *e) { - if (IS_SET(MODE_MOUSE) && !(e->xbutton.state & forceselmod)) { + if (IS_SET(MODE_MOUSE) && !(e->xbutton.state & forcemousemod)) { mousereport(e); return; } From d2b75db8d7519a20af8bf09e9c205507f9ff828c Mon Sep 17 00:00:00 2001 From: "Avi Halachmi (:avih)" Date: Fri, 11 Oct 2019 02:26:10 +0300 Subject: [PATCH 111/185] mouse shortcuts: don't hardcode selpaste Because selpaste is activated on release, a release flag was added to mouse shortcuts which controls whether activation is on press/release, and selpaste binding to button2 was moved to config.h . button1 remains the only hardcoded mouse button - for selection + copy. --- config.def.h | 3 ++- x.c | 35 ++++++++++++++++++++++++----------- 2 files changed, 26 insertions(+), 12 deletions(-) diff --git a/config.def.h b/config.def.h index a0a0d2d..546edda 100644 --- a/config.def.h +++ b/config.def.h @@ -162,7 +162,8 @@ static uint forcemousemod = ShiftMask; * Beware that overloading Button1 will disable the selection. */ static MouseShortcut mshortcuts[] = { - /* mask button function argument */ + /* mask button function argument release */ + { XK_ANY_MOD, Button2, selpaste, {.i = 0}, 1 }, { XK_ANY_MOD, Button4, ttysend, {.s = "\031"} }, { XK_ANY_MOD, Button5, ttysend, {.s = "\005"} }, }; diff --git a/x.c b/x.c index c967caf..8f570c2 100644 --- a/x.c +++ b/x.c @@ -33,6 +33,7 @@ typedef struct { uint button; void (*func)(const Arg *); const Arg arg; + uint release; } MouseShortcut; typedef struct { @@ -165,6 +166,7 @@ static void kpress(XEvent *); static void cmessage(XEvent *); static void resize(XEvent *); static void focus(XEvent *); +static int mouseaction(XEvent *, uint); static void brelease(XEvent *); static void bpress(XEvent *); static void bmotion(XEvent *); @@ -416,11 +418,27 @@ mousereport(XEvent *e) ttywrite(buf, len, 0); } +int +mouseaction(XEvent *e, uint release) +{ + MouseShortcut *ms; + + for (ms = mshortcuts; ms < mshortcuts + LEN(mshortcuts); ms++) { + if (ms->release == release && + ms->button == e->xbutton.button && + match(ms->mod, e->xbutton.state & ~forcemousemod)) { + ms->func(&(ms->arg)); + return 1; + } + } + + return 0; +} + void bpress(XEvent *e) { struct timespec now; - MouseShortcut *ms; int snap; if (IS_SET(MODE_MOUSE) && !(e->xbutton.state & forcemousemod)) { @@ -428,13 +446,8 @@ bpress(XEvent *e) return; } - for (ms = mshortcuts; ms < mshortcuts + LEN(mshortcuts); ms++) { - if (e->xbutton.button == ms->button && - match(ms->mod, e->xbutton.state & ~forcemousemod)) { - ms->func(&(ms->arg)); - return; - } - } + if (mouseaction(e, 0)) + return; if (e->xbutton.button == Button1) { /* @@ -655,9 +668,9 @@ brelease(XEvent *e) return; } - if (e->xbutton.button == Button2) - selpaste(NULL); - else if (e->xbutton.button == Button1) + if (mouseaction(e, 1)) + return; + if (e->xbutton.button == Button1) mousesel(e, 1); } From a2c479c4c8d035c11a91e4b954a9f161bf4c7150 Mon Sep 17 00:00:00 2001 From: "Avi Halachmi (:avih)" Date: Thu, 24 Oct 2019 15:42:07 +0300 Subject: [PATCH 112/185] mouse shortcuts: allow using forcemousemod (e.g. shift) The recent mouse shurtcuts commits allow customization, but ignore forcemousemod mask (default: shift) as a modifier, for no good reason other than following the behavior of the KB shortcuts. Allow using forcemousemod too, which now can be used to invoke different shortcuts, though the automatic effect of forcemousemod will be lost for buttons which use mask with forcemousemod. E.g. the default is: static uint forcemousemod = ShiftMask; ... { XK_ANY_MOD, Button4, ttysend, {.s = "\031"} }, ... where ttysend will be invoked for button4 with any mod when not in mouse mode, and with shift when in mouse mode. Now it's possible to do this: { ShiftMask, Button4, ttysend, {.s = "foo"} }, { XK_ANY_MOD, Button4, ttysend, {.s = "\031"} }, Which will invoke ttysend("foo") while shift is held and ttysend("\031") otherwise. Shift still overrides mouse mode, but will now send "foo". Previously with this setup the second binding was always invoked because the forceousemod mask was always removed from the event. Buttons which don't use forcemousemod behave the same as before. This is useful e.g. for the scrollback mouse patch, which wants to configure shift+wheel for scrollback, while keeping the normal behavior without shift. --- x.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/x.c b/x.c index 8f570c2..6406925 100644 --- a/x.c +++ b/x.c @@ -426,7 +426,8 @@ mouseaction(XEvent *e, uint release) for (ms = mshortcuts; ms < mshortcuts + LEN(mshortcuts); ms++) { if (ms->release == release && ms->button == e->xbutton.button && - match(ms->mod, e->xbutton.state & ~forcemousemod)) { + (match(ms->mod, e->xbutton.state) || /* exact or forced */ + match(ms->mod, e->xbutton.state & ~forcemousemod))) { ms->func(&(ms->arg)); return 1; } From 1f09f0b0bbba29ceed9b4e6d558b6ad5b0843cfe Mon Sep 17 00:00:00 2001 From: Ingo Lohmar Date: Fri, 31 May 2019 22:25:35 +0200 Subject: [PATCH 113/185] apply hints before initial mapping (ICCCM) For WM_CLASS this is mentioned in the ICCCM docs https://tronche.com/gui/x/icccm/sec-4.html#s-4.1.2.5 (third sentence). When changing the WM_CLASS from the command line, this is necessary for window managers to pick it up before applying class-based rules. --- x.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/x.c b/x.c index 6406925..bc3ad5a 100644 --- a/x.c +++ b/x.c @@ -1154,8 +1154,8 @@ xinit(int cols, int rows) win.mode = MODE_NUMLOCK; resettitle(); - XMapWindow(xw.dpy, xw.win); xhints(); + XMapWindow(xw.dpy, xw.win); XSync(xw.dpy, False); clock_gettime(CLOCK_MONOTONIC, &xsel.tclick1); From 83866428de031300eab03fbb116bcf7d2b1d4f60 Mon Sep 17 00:00:00 2001 From: "Sebastian J. Bronner" Date: Tue, 5 Nov 2019 18:16:39 +0100 Subject: [PATCH 114/185] Fix tmux terminfo extensions Se and Ss The tmux terminfo extensions Ss and Se are currently specified as booleans in `st.info`. They should be strings. See https://github.com/tmux/tmux/blob/eeedb43ae847a0a692ceea965f7556e84bca4fd0/tty-term.c lines 254 and 265. I have used the values from https://invisible-island.net/ncurses/terminfo.src.html#toc-_S_I_M_P_L_E_T_E_R_M for this patch. --- st.info | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/st.info b/st.info index 52fc617..78ffd30 100644 --- a/st.info +++ b/st.info @@ -189,10 +189,10 @@ st| simpleterm, rmxx=\E[29m, smxx=\E[9m, # tmux extensions, see TERMINFO EXTENSIONS in tmux(1) - Se, - Ss, Tc, Ms=\E]52;%p1%s;%p2%s\007, + Se=\E[2 q, + Ss=\E[%p1%d q, st-256color| simpleterm with 256 colors, use=st, From ea4d933ed9d8ce16699c84892a29e070c70b2eb9 Mon Sep 17 00:00:00 2001 From: "Avi Halachmi (:avih)" Date: Wed, 16 Oct 2019 11:17:23 +0300 Subject: [PATCH 115/185] base64dec: don't read out of bounds Previously, base64dec checked terminating input '\0' every 4 calls to base64dec_getc, where the latter progressed one or more chars on each call, and could read past '\0' in the way it was used. The input to base64dec currently comes only from OSC 52 escape seq (copy to clipboard), and reading past '\0' or even past the buffer boundary was easy to trigger. Also, even if we could trust external input to be valid base64, there are different base64 standards, and not all of them require padding to 4 bytes blocks (using trailing '=' chars). It didn't affect short OSC 52 strings because the buffer is initialized to 0's, so typically it did stop within the buffer, but if the string was trimmed to fit (the buffer is 512 bytes) then it did also read past the end of the buffer, and the decoded suffix ended up arbitrary. This patch makes base64dec_getc not progress past '\0', and instead produce fake trailing padding of '='. Additionally, at base64dec, if padding is detected at the first or second byte of a quartet, then we identify it as invalid and abort (a valid quartet has at least two leading non-padding bytes). --- st.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/st.c b/st.c index ede7ae6..a8f8232 100644 --- a/st.c +++ b/st.c @@ -366,7 +366,7 @@ char base64dec_getc(const char **src) { while (**src && !isprint(**src)) (*src)++; - return *((*src)++); + return **src ? *((*src)++) : '='; /* emulate padding if string ends */ } char * @@ -384,6 +384,10 @@ base64dec(const char *src) int c = base64_digits[(unsigned char) base64dec_getc(&src)]; int d = base64_digits[(unsigned char) base64dec_getc(&src)]; + /* invalid input. 'a' can be -1, e.g. if src is "\n" (c-str) */ + if (a == -1 || b == -1) + break; + *dst++ = (a << 2) | ((b & 0x30) >> 4); if (c == -1) break; From 7ceb3d1f72eabfa678e5cfae176c57630ad98c43 Mon Sep 17 00:00:00 2001 From: "Avi Halachmi (:avih)" Date: Wed, 16 Oct 2019 12:19:49 +0300 Subject: [PATCH 116/185] STREscape: don't trim prematurely STRescape holds strings in escape sequences such as OSC and DCS, and its buffer is 512 bytes. If the input is too big then trailing chars are ignored, but the test was off-by-1 such that it took 510 chars instead of 511 (before a terminating NULL is added). Now the full size can be utilized. --- st.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/st.c b/st.c index a8f8232..50226d1 100644 --- a/st.c +++ b/st.c @@ -2330,7 +2330,7 @@ tputc(Rune u) if (term.esc&ESC_DCS && strescseq.len == 0 && u == 'q') term.mode |= MODE_SIXEL; - if (strescseq.len+len >= sizeof(strescseq.buf)-1) { + if (strescseq.len+len >= sizeof(strescseq.buf)) { /* * Here is a bug in terminals. If the user never sends * some code to stop the str or esc command, then st From 289c52b7aa9b0e826bbea6f956755b3199b3ccac Mon Sep 17 00:00:00 2001 From: Hiltjo Posthuma Date: Wed, 16 Oct 2019 12:38:43 +0300 Subject: [PATCH 117/185] CSIEscape, STREscape: use size_t for buffer length --- st.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/st.c b/st.c index 50226d1..0c1acd4 100644 --- a/st.c +++ b/st.c @@ -135,7 +135,7 @@ typedef struct { /* ESC '[' [[ [] [;]] []] */ typedef struct { char buf[ESC_BUF_SIZ]; /* raw string */ - int len; /* raw string length */ + size_t len; /* raw string length */ char priv; int arg[ESC_ARG_SIZ]; int narg; /* nb of args */ @@ -147,7 +147,7 @@ typedef struct { typedef struct { char type; /* ESC type ... */ char buf[STR_BUF_SIZ]; /* raw string */ - int len; /* raw string length */ + size_t len; /* raw string length */ char *args[STR_ARG_SIZ]; int narg; /* nb of args */ } STREscape; @@ -1803,7 +1803,7 @@ csihandle(void) void csidump(void) { - int i; + size_t i; uint c; fprintf(stderr, "ESC["); @@ -1921,7 +1921,7 @@ strparse(void) void strdump(void) { - int i; + size_t i; uint c; fprintf(stderr, "ESC%c", strescseq.type); From 2e54a21b5ae249a6bcedab9db611ea86037a018b Mon Sep 17 00:00:00 2001 From: "Avi Halachmi (:avih)" Date: Wed, 16 Oct 2019 12:55:53 +0300 Subject: [PATCH 118/185] OSC 52 - copy to clipboard: don't limit to 382 bytes Strings which an application sends to the terminal in OSC, DCS, etc are typically small (title, colors, etc) but one exception is OSC 52 which copies text to the clipboard, and is used for instance by tmux. Previously st cropped these strings at 512 bytes, which for OSC 52 limited the copied text to 382 bytes (remaining buffer space before base64). This made it less useful than it can be. Now it's a dynamic growing buffer. It remains allocated after use, resets to 512 when a new string starts, or leaked on exit. Resetting/deallocating the buffer right after use (at strhandle) is possible with some more code, however, it doesn't always end up used, and to cover those cases too will require even more code, so resetting only on new string is good enough for now. --- st.c | 15 +++++++++++---- 1 file changed, 11 insertions(+), 4 deletions(-) diff --git a/st.c b/st.c index 0c1acd4..3e48410 100644 --- a/st.c +++ b/st.c @@ -146,7 +146,8 @@ typedef struct { /* ESC type [[ [] [;]] ] ESC '\' */ typedef struct { char type; /* ESC type ... */ - char buf[STR_BUF_SIZ]; /* raw string */ + char *buf; /* allocated raw string */ + size_t siz; /* allocation size */ size_t len; /* raw string length */ char *args[STR_ARG_SIZ]; int narg; /* nb of args */ @@ -1948,7 +1949,10 @@ strdump(void) void strreset(void) { - memset(&strescseq, 0, sizeof(strescseq)); + strescseq = (STREscape){ + .buf = xrealloc(strescseq.buf, STR_BUF_SIZ), + .siz = STR_BUF_SIZ, + }; } void @@ -2330,7 +2334,7 @@ tputc(Rune u) if (term.esc&ESC_DCS && strescseq.len == 0 && u == 'q') term.mode |= MODE_SIXEL; - if (strescseq.len+len >= sizeof(strescseq.buf)) { + if (strescseq.len+len >= strescseq.siz) { /* * Here is a bug in terminals. If the user never sends * some code to stop the str or esc command, then st @@ -2344,7 +2348,10 @@ tputc(Rune u) * term.esc = 0; * strhandle(); */ - return; + if (strescseq.siz > (SIZE_MAX - UTF_SIZ) / 2) + return; + strescseq.siz *= 2; + strescseq.buf = xrealloc(strescseq.buf, strescseq.siz); } memmove(&strescseq.buf[strescseq.len], c, len); From 384830110bddcebed00b6530a5336f07ad7c405f Mon Sep 17 00:00:00 2001 From: Hiltjo Posthuma Date: Sun, 17 Nov 2019 20:04:52 +0100 Subject: [PATCH 119/185] update FAQ - add common question about the w3m image drawing hack. - remove some bad advise about $TERM. - change some links to https. --- FAQ | 21 +++++++++++---------- 1 file changed, 11 insertions(+), 10 deletions(-) diff --git a/FAQ b/FAQ index ecf7af8..78c769a 100644 --- a/FAQ +++ b/FAQ @@ -1,6 +1,6 @@ ## Why does st not handle utmp entries? -Use the excellent tool of [utmp](http://git.suckless.org/utmp/) for this task. +Use the excellent tool of [utmp](https://git.suckless.org/utmp/) for this task. ## Some _random program_ complains that st is unknown/not recognised/unsupported/whatever! @@ -15,13 +15,6 @@ you can manually run `tic -sx st.info`. * Some programs don’t complain about the lacking st description and default to another terminal. In that case see the question about terminfo. -## I get some weird glitches/visual bug on _random program_! - -Try launching it with a different TERM: $ TERM=xterm myapp. toe(1) will give -you a list of available terminals, but you’ll most likely switch between xterm, -st or st-256color. The default value for TERM can be changed in config.h -(TNAME). - ## How do I scroll back up? Using a terminal multiplexer. @@ -104,7 +97,7 @@ St is emulating the Linux way of handling backspace being delete and delete bein backspace. This is an issue that was discussed in suckless mailing list -. Here is why some old grumpy +. Here is why some old grumpy terminal users wants its backspace to be how he feels it: Well, I am going to comment why I want to change the behaviour @@ -163,7 +156,15 @@ terminal users wants its backspace to be how he feels it: Apply [1]. -[1] http://st.suckless.org/patches/delkey +[1] https://st.suckless.org/patches/delkey + +## Why do images not work in st (in programs such as w3m)? + +This is a terrible hack that overdraws an image on top of the terminal emulator +window. It also relies on a very specific way the terminal draws it's contents. + +A more proper (but limited way) would be using sixels. Which st doesn't +support. ## BadLength X error in Xft when trying to render emoji From 895e5b50a8cc835c19a45e1e328eb4dc78f5fd0c Mon Sep 17 00:00:00 2001 From: Ivan Tham Date: Sat, 18 Jan 2020 15:52:25 +0800 Subject: [PATCH 120/185] Increase XmbLookupString buffer MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Current buffer is too short to input medium to long sentences from IME. Input with longer text will show the wrong input, taking 64 instead of 32 bytes should be enough for most of the cases. Broken cases before, Chinese (taken from song 也可以) 可不可以轻轻的松开自己 Japanese (taken from bootleggers rom quote) あなたは家のように感じる --- x.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/x.c b/x.c index bc3ad5a..e000894 100644 --- a/x.c +++ b/x.c @@ -1743,7 +1743,7 @@ kpress(XEvent *ev) { XKeyEvent *e = &ev->xkey; KeySym ksym; - char buf[32], *customkey; + char buf[64], *customkey; int len; Rune c; Status status; From 99de33395126fc9708f442d155e737b9182f6ef4 Mon Sep 17 00:00:00 2001 From: Quentin Rameau Date: Sun, 2 Feb 2020 15:37:29 +0100 Subject: [PATCH 121/185] x: move IME variables into XWindow ime embedded struct --- x.c | 26 ++++++++++++++------------ 1 file changed, 14 insertions(+), 12 deletions(-) diff --git a/x.c b/x.c index e000894..60eeee1 100644 --- a/x.c +++ b/x.c @@ -94,8 +94,10 @@ typedef struct { Drawable buf; GlyphFontSpec *specbuf; /* font spec buffer used for rendering */ Atom xembed, wmdeletewin, netwmname, netwmpid; - XIM xim; - XIC xic; + struct { + XIM xim; + XIC xic; + } ime; Draw draw; Visual *vis; XSetWindowAttributes attrs; @@ -1026,18 +1028,18 @@ ximopen(Display *dpy) { XIMCallback destroy = { .client_data = NULL, .callback = ximdestroy }; - if ((xw.xim = XOpenIM(xw.dpy, NULL, NULL, NULL)) == NULL) { + if ((xw.ime.xim = XOpenIM(xw.dpy, NULL, NULL, NULL)) == NULL) { XSetLocaleModifiers("@im=local"); - if ((xw.xim = XOpenIM(xw.dpy, NULL, NULL, NULL)) == NULL) { + if ((xw.ime.xim = XOpenIM(xw.dpy, NULL, NULL, NULL)) == NULL) { XSetLocaleModifiers("@im="); - if ((xw.xim = XOpenIM(xw.dpy, NULL, NULL, NULL)) == NULL) + if ((xw.ime.xim = XOpenIM(xw.dpy, NULL, NULL, NULL)) == NULL) die("XOpenIM failed. Could not open input device.\n"); } } - if (XSetIMValues(xw.xim, XNDestroyCallback, &destroy, NULL) != NULL) + if (XSetIMValues(xw.ime.xim, XNDestroyCallback, &destroy, NULL) != NULL) die("XSetIMValues failed. Could not set input method value.\n"); - xw.xic = XCreateIC(xw.xim, XNInputStyle, XIMPreeditNothing | XIMStatusNothing, - XNClientWindow, xw.win, XNFocusWindow, xw.win, NULL); + xw.xic = XCreateIC(xw.ime.xim, XNInputStyle, XIMPreeditNothing | XIMStatusNothing, + XNClientWindow, xw.win, XNFocusWindow, xw.win, NULL); if (xw.xic == NULL) die("XCreateIC failed. Could not obtain input method.\n"); } @@ -1053,7 +1055,7 @@ ximinstantiate(Display *dpy, XPointer client, XPointer call) void ximdestroy(XIM xim, XPointer client, XPointer call) { - xw.xim = NULL; + xw.ime.xim = NULL; XRegisterIMInstantiateCallback(xw.dpy, NULL, NULL, NULL, ximinstantiate, NULL); } @@ -1682,13 +1684,13 @@ focus(XEvent *ev) return; if (ev->type == FocusIn) { - XSetICFocus(xw.xic); + XSetICFocus(xw.ime.xic); win.mode |= MODE_FOCUSED; xseturgency(0); if (IS_SET(MODE_FOCUS)) ttywrite("\033[I", 3, 0); } else { - XUnsetICFocus(xw.xic); + XUnsetICFocus(xw.ime.xic); win.mode &= ~MODE_FOCUSED; if (IS_SET(MODE_FOCUS)) ttywrite("\033[O", 3, 0); @@ -1752,7 +1754,7 @@ kpress(XEvent *ev) if (IS_SET(MODE_KBDLOCK)) return; - len = XmbLookupString(xw.xic, e, buf, sizeof buf, &ksym, &status); + len = XmbLookupString(xw.ime.xic, e, buf, sizeof buf, &ksym, &status); /* 1. shortcuts */ for (bp = shortcuts; bp < shortcuts + LEN(shortcuts); bp++) { if (ksym == bp->keysym && match(bp->mod, e->state)) { From 2cb539142b97bd2a5c1a322fd7c063c6afb67c9b Mon Sep 17 00:00:00 2001 From: Quentin Rameau Date: Sun, 2 Feb 2020 15:38:08 +0100 Subject: [PATCH 122/185] x: do not instantiate a new nested list on each cursor move --- x.c | 16 ++++++++++++---- 1 file changed, 12 insertions(+), 4 deletions(-) diff --git a/x.c b/x.c index 60eeee1..5af6e4d 100644 --- a/x.c +++ b/x.c @@ -97,6 +97,8 @@ typedef struct { struct { XIM xim; XIC xic; + XPoint spot; + XVaNestedList spotlist; } ime; Draw draw; Visual *vis; @@ -1042,6 +1044,9 @@ ximopen(Display *dpy) XNClientWindow, xw.win, XNFocusWindow, xw.win, NULL); if (xw.xic == NULL) die("XCreateIC failed. Could not obtain input method.\n"); + + xw.ime.spotlist = XVaCreateNestedList(0, XNSpotLocation, &xw.ime.spot, + NULL); } void @@ -1058,6 +1063,7 @@ ximdestroy(XIM xim, XPointer client, XPointer call) xw.ime.xim = NULL; XRegisterIMInstantiateCallback(xw.dpy, NULL, NULL, NULL, ximinstantiate, NULL); + XFree(xw.ime.spotlist); } void @@ -1603,11 +1609,13 @@ xfinishdraw(void) void xximspot(int x, int y) { - XPoint spot = { borderpx + x * win.cw, borderpx + (y + 1) * win.ch }; - XVaNestedList attr = XVaCreateNestedList(0, XNSpotLocation, &spot, NULL); + if (xw.ime.xic == NULL) + return; - XSetICValues(xw.xic, XNPreeditAttributes, attr, NULL); - XFree(attr); + xw.ime.spot.x = borderpx + x * win.cw; + xw.ime.spot.y = borderpx + (y + 1) * win.ch; + + XSetICValues(xw.ime.xic, XNPreeditAttributes, xw.ime.spotlist, NULL); } void From cd785755f2e3e3305c7d2556a04423a40bce060a Mon Sep 17 00:00:00 2001 From: Quentin Rameau Date: Sun, 2 Feb 2020 17:38:36 +0100 Subject: [PATCH 123/185] x: check we still have an XIC context before accessing it --- x.c | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/x.c b/x.c index 5af6e4d..b488617 100644 --- a/x.c +++ b/x.c @@ -1061,6 +1061,7 @@ void ximdestroy(XIM xim, XPointer client, XPointer call) { xw.ime.xim = NULL; + xw.ime.xic = NULL; XRegisterIMInstantiateCallback(xw.dpy, NULL, NULL, NULL, ximinstantiate, NULL); XFree(xw.ime.spotlist); @@ -1692,13 +1693,15 @@ focus(XEvent *ev) return; if (ev->type == FocusIn) { - XSetICFocus(xw.ime.xic); + if (xw.ime.xic) + XSetICFocus(xw.ime.xic); win.mode |= MODE_FOCUSED; xseturgency(0); if (IS_SET(MODE_FOCUS)) ttywrite("\033[I", 3, 0); } else { - XUnsetICFocus(xw.ime.xic); + if (xw.ime.xic) + XUnsetICFocus(xw.ime.xic); win.mode &= ~MODE_FOCUSED; if (IS_SET(MODE_FOCUS)) ttywrite("\033[O", 3, 0); From 26cdfebf31f024e331429e482b1ee342708888e3 Mon Sep 17 00:00:00 2001 From: Quentin Rameau Date: Sun, 2 Feb 2020 21:47:19 +0100 Subject: [PATCH 124/185] x: fix XIM handling Do not try to set specific IM method, let the user specify it with XMODIFIERS. If the requested method is not available or opening fails, fallback to the default input handler and register a handler on the new IM server availability signal. Do the same when the input server is closed and (re)started. --- x.c | 68 +++++++++++++++++++++++++++++++++++++++---------------------- 1 file changed, 44 insertions(+), 24 deletions(-) diff --git a/x.c b/x.c index b488617..1f62129 100644 --- a/x.c +++ b/x.c @@ -146,9 +146,10 @@ static void xdrawglyphfontspecs(const XftGlyphFontSpec *, Glyph, int, int, int); static void xdrawglyph(Glyph, int, int); static void xclear(int, int, int, int); static int xgeommasktogravity(int); -static void ximopen(Display *); +static int ximopen(Display *); static void ximinstantiate(Display *, XPointer, XPointer); static void ximdestroy(XIM, XPointer, XPointer); +static int xicdestroy(XIC, XPointer, XPointer); static void xinit(int, int); static void cresize(int, int); static void xresize(int, int); @@ -1025,48 +1026,61 @@ xunloadfonts(void) xunloadfont(&dc.ibfont); } -void +int ximopen(Display *dpy) { - XIMCallback destroy = { .client_data = NULL, .callback = ximdestroy }; + XIMCallback imdestroy = { .client_data = NULL, .callback = ximdestroy }; + XICCallback icdestroy = { .client_data = NULL, .callback = xicdestroy }; - if ((xw.ime.xim = XOpenIM(xw.dpy, NULL, NULL, NULL)) == NULL) { - XSetLocaleModifiers("@im=local"); - if ((xw.ime.xim = XOpenIM(xw.dpy, NULL, NULL, NULL)) == NULL) { - XSetLocaleModifiers("@im="); - if ((xw.ime.xim = XOpenIM(xw.dpy, NULL, NULL, NULL)) == NULL) - die("XOpenIM failed. Could not open input device.\n"); - } - } - if (XSetIMValues(xw.ime.xim, XNDestroyCallback, &destroy, NULL) != NULL) - die("XSetIMValues failed. Could not set input method value.\n"); - xw.xic = XCreateIC(xw.ime.xim, XNInputStyle, XIMPreeditNothing | XIMStatusNothing, - XNClientWindow, xw.win, XNFocusWindow, xw.win, NULL); - if (xw.xic == NULL) - die("XCreateIC failed. Could not obtain input method.\n"); + xw.ime.xim = XOpenIM(xw.dpy, NULL, NULL, NULL); + if (xw.ime.xim == NULL) + return 0; + + if (XSetIMValues(xw.ime.xim, XNDestroyCallback, &imdestroy, NULL)) + fprintf(stderr, "XSetIMValues: " + "Could not set XNDestroyCallback.\n"); xw.ime.spotlist = XVaCreateNestedList(0, XNSpotLocation, &xw.ime.spot, NULL); + + if (xw.ime.xic == NULL) { + xw.ime.xic = XCreateIC(xw.ime.xim, XNInputStyle, + XIMPreeditNothing | XIMStatusNothing, + XNClientWindow, xw.win, + XNFocusWindow, xw.win, + XNDestroyCallback, &icdestroy, + NULL); + } + if (xw.ime.xic == NULL) + fprintf(stderr, "XCreateIC: Could not create input context.\n"); + + return 1; } void ximinstantiate(Display *dpy, XPointer client, XPointer call) { - ximopen(dpy); - XUnregisterIMInstantiateCallback(xw.dpy, NULL, NULL, NULL, - ximinstantiate, NULL); + if (ximopen(dpy)) + XUnregisterIMInstantiateCallback(xw.dpy, NULL, NULL, NULL, + ximinstantiate, NULL); } void ximdestroy(XIM xim, XPointer client, XPointer call) { xw.ime.xim = NULL; - xw.ime.xic = NULL; XRegisterIMInstantiateCallback(xw.dpy, NULL, NULL, NULL, - ximinstantiate, NULL); + ximinstantiate, NULL); XFree(xw.ime.spotlist); } +int +xicdestroy(XIC xim, XPointer client, XPointer call) +{ + xw.ime.xic = NULL; + return 1; +} + void xinit(int cols, int rows) { @@ -1132,7 +1146,10 @@ xinit(int cols, int rows) xw.draw = XftDrawCreate(xw.dpy, xw.buf, xw.vis, xw.cmap); /* input methods */ - ximopen(xw.dpy); + if (!ximopen(xw.dpy)) { + XRegisterIMInstantiateCallback(xw.dpy, NULL, NULL, NULL, + ximinstantiate, NULL); + } /* white cursor, black outline */ cursor = XCreateFontCursor(xw.dpy, mouseshape); @@ -1765,7 +1782,10 @@ kpress(XEvent *ev) if (IS_SET(MODE_KBDLOCK)) return; - len = XmbLookupString(xw.ime.xic, e, buf, sizeof buf, &ksym, &status); + if (xw.ime.xic) + len = XmbLookupString(xw.ime.xic, e, buf, sizeof buf, &ksym, &status); + else + len = XLookupString(e, buf, sizeof buf, &ksym, NULL); /* 1. shortcuts */ for (bp = shortcuts; bp < shortcuts + LEN(shortcuts); bp++) { if (ksym == bp->keysym && match(bp->mod, e->state)) { From 51e19ea11dd42eefed1ca136ee3f6be975f618b1 Mon Sep 17 00:00:00 2001 From: Ivan Tham Date: Tue, 18 Feb 2020 23:28:47 +0800 Subject: [PATCH 125/185] Remove explicit XNFocusWindow XCreateIC ICValues default XNFocusWindow to XNClientWindow if not specified, it can be omitted since it is the same. From the documentation https://www.x.org/releases/current/doc/libX11/libX11/libX11.html > Focus Window > > The XNFocusWindow argument specifies the focus window. The primary > purpose of the XNFocusWindow is to identify the window that will receive > the key event when input is composed. > > When this XIC value is left unspecified, the input method will use the > client window as the default focus window. --- x.c | 1 - 1 file changed, 1 deletion(-) diff --git a/x.c b/x.c index 1f62129..48a6676 100644 --- a/x.c +++ b/x.c @@ -1047,7 +1047,6 @@ ximopen(Display *dpy) xw.ime.xic = XCreateIC(xw.ime.xim, XNInputStyle, XIMPreeditNothing | XIMStatusNothing, XNClientWindow, xw.win, - XNFocusWindow, xw.win, XNDestroyCallback, &icdestroy, NULL); } From 28ad28839985e965c9ca06a9a202523414c84ac4 Mon Sep 17 00:00:00 2001 From: "Avi Halachmi (:avih)" Date: Thu, 2 Apr 2020 11:43:22 +0300 Subject: [PATCH 126/185] mouseshortcuts: fix custom modifier on release This line didn't work at mshortcuts at config.h: /* mask button function arg release */ { ShiftMask, Button2, selpaste, {.i = 0}, 1 }, and now it does work. The issue was that XButtonEvent.state is "the logical state ... just prior to the event", which means that on release the state has the Button2Mask bit set because button2 was down just before it was released. The issue didn't manifest with the default shift + middle-click on release (to override mouse mode) because its specified modifier is XK_ANY_MOD, at which case match(...) ignores any specific bits and simply returns true. The issue also doesn't manifest on press, because prior to the event Button was not down and its mask bit is not set. Fix by filtering out the mask of the button which we're currently matching. We could have said "well, that's how button events behave, you should use ShiftMask|Button2Mask for release", but this both not obvious to figure out, and specifically here always filtering does not prevent configuring any useful modifiers combination. So it's a win-win. --- x.c | 19 +++++++++++++++++-- 1 file changed, 17 insertions(+), 2 deletions(-) diff --git a/x.c b/x.c index 48a6676..4cf6b21 100644 --- a/x.c +++ b/x.c @@ -171,6 +171,7 @@ static void kpress(XEvent *); static void cmessage(XEvent *); static void resize(XEvent *); static void focus(XEvent *); +static uint buttonmask(uint); static int mouseaction(XEvent *, uint); static void brelease(XEvent *); static void bpress(XEvent *); @@ -423,16 +424,30 @@ mousereport(XEvent *e) ttywrite(buf, len, 0); } +uint +buttonmask(uint button) +{ + return button == Button1 ? Button1Mask + : button == Button2 ? Button2Mask + : button == Button3 ? Button3Mask + : button == Button4 ? Button4Mask + : button == Button5 ? Button5Mask + : 0; +} + int mouseaction(XEvent *e, uint release) { MouseShortcut *ms; + /* ignore Buttonmask for Button - it's set on release */ + uint state = e->xbutton.state & ~buttonmask(e->xbutton.button); + for (ms = mshortcuts; ms < mshortcuts + LEN(mshortcuts); ms++) { if (ms->release == release && ms->button == e->xbutton.button && - (match(ms->mod, e->xbutton.state) || /* exact or forced */ - match(ms->mod, e->xbutton.state & ~forcemousemod))) { + (match(ms->mod, state) || /* exact or forced */ + match(ms->mod, state & ~forcemousemod))) { ms->func(&(ms->arg)); return 1; } From 5703aa0390484dd7da4bd9c388c85708d8fcd339 Mon Sep 17 00:00:00 2001 From: Hiltjo Posthuma Date: Fri, 10 Apr 2020 12:12:43 +0200 Subject: [PATCH 127/185] make argv0 not static, fixes a warning with tcc Reported by Aajonus, thanks! --- x.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/x.c b/x.c index 4cf6b21..e5f1737 100644 --- a/x.c +++ b/x.c @@ -15,7 +15,7 @@ #include #include -static char *argv0; +char *argv0; #include "arg.h" #include "st.h" #include "win.h" From 21e0d6e8b8d20903494386e7e6f43201b3761154 Mon Sep 17 00:00:00 2001 From: "Roberto E. Vargas Caballero" Date: Fri, 10 Apr 2020 22:06:32 +0200 Subject: [PATCH 128/185] Add support for scroll(1) Scroll is a program that stores all the lines of its child and be used in st as a way of implementing scrollback. This solution is much better than implementing the scrollback in st itself because having a different program allows to use it in any other program without doing modifications to those programs. --- config.def.h | 3 ++- st.1 | 3 ++- st.c | 16 ++++++++++------ st.h | 1 + 4 files changed, 15 insertions(+), 8 deletions(-) diff --git a/config.def.h b/config.def.h index 546edda..dfcbda9 100644 --- a/config.def.h +++ b/config.def.h @@ -11,13 +11,14 @@ static int borderpx = 2; /* * What program is execed by st depends of these precedence rules: * 1: program passed with -e - * 2: utmp option + * 2: scroll and/or utmp * 3: SHELL environment variable * 4: value of shell in /etc/passwd * 5: value of shell in config.h */ static char *shell = "/bin/sh"; char *utmp = NULL; +char *scroll = NULL; char *stty_args = "stty raw pass8 nl -echo -iexten -cstopb 38400"; /* identification sequence returned in DA and DECID */ diff --git a/st.1 b/st.1 index e8d6059..39120b4 100644 --- a/st.1 +++ b/st.1 @@ -170,7 +170,8 @@ See the LICENSE file for the terms of redistribution. .SH SEE ALSO .BR tabbed (1), .BR utmp (1), -.BR stty (1) +.BR stty (1), +.BR scroll (1) .SH BUGS See the TODO file in the distribution. diff --git a/st.c b/st.c index 3e48410..5f2352a 100644 --- a/st.c +++ b/st.c @@ -664,7 +664,7 @@ die(const char *errstr, ...) void execsh(char *cmd, char **args) { - char *sh, *prog; + char *sh, *prog, *arg; const struct passwd *pw; errno = 0; @@ -678,13 +678,17 @@ execsh(char *cmd, char **args) if ((sh = getenv("SHELL")) == NULL) sh = (pw->pw_shell[0]) ? pw->pw_shell : cmd; - if (args) + if (args) { prog = args[0]; - else if (utmp) - prog = utmp; - else + arg = NULL; + } else if (scroll || utmp) { + prog = scroll ? scroll : utmp; + arg = scroll ? utmp : NULL; + } else { prog = sh; - DEFAULT(args, ((char *[]) {prog, NULL})); + arg = NULL; + } + DEFAULT(args, ((char *[]) {prog, arg, NULL})); unsetenv("COLUMNS"); unsetenv("LINES"); diff --git a/st.h b/st.h index a1928ca..d978458 100644 --- a/st.h +++ b/st.h @@ -113,6 +113,7 @@ char *xstrdup(char *); /* config.h globals */ extern char *utmp; +extern char *scroll; extern char *stty_args; extern char *vtiden; extern wchar_t *worddelimiters; From e52319cc7d153e4f59b38c4fb4c0556e118d4775 Mon Sep 17 00:00:00 2001 From: "Roberto E. Vargas Caballero" Date: Fri, 10 Apr 2020 22:25:46 +0200 Subject: [PATCH 129/185] ttyread: test for EOF while reading tty When a read operation returns 0 then it means that we arrived to the end of the file, and new reads will return 0 unless you do some other operation such as lseek(). This case happens with USB-232 adapters when they are unplugged. --- st.c | 25 ++++++++++++++++--------- 1 file changed, 16 insertions(+), 9 deletions(-) diff --git a/st.c b/st.c index 5f2352a..81973ee 100644 --- a/st.c +++ b/st.c @@ -823,17 +823,24 @@ ttyread(void) int ret; /* append read bytes to unprocessed bytes */ - if ((ret = read(cmdfd, buf+buflen, LEN(buf)-buflen)) < 0) + ret = read(cmdfd, buf+buflen, LEN(buf)-buflen); + + switch (ret) { + case 0: + fputs("Found EOF in input\n", stderr); + exit(0); + case -1: die("couldn't read from shell: %s\n", strerror(errno)); - buflen += ret; + default: + buflen += ret; + written = twrite(buf, buflen, 0); + buflen -= written; + /* keep any uncomplete utf8 char for the next call */ + if (buflen > 0) + memmove(buf, buf + written, buflen); + return ret; - written = twrite(buf, buflen, 0); - buflen -= written; - /* keep any uncomplete utf8 char for the next call */ - if (buflen > 0) - memmove(buf, buf + written, buflen); - - return ret; + } } void From fbae700a3f32db76106b0ff6f49a73ecf0c2b4fe Mon Sep 17 00:00:00 2001 From: "Roberto E. Vargas Caballero" Date: Fri, 10 Apr 2020 22:26:12 +0200 Subject: [PATCH 130/185] Fix style issue --- st.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/st.c b/st.c index 81973ee..2ecf8f3 100644 --- a/st.c +++ b/st.c @@ -366,7 +366,8 @@ static const char base64_digits[] = { char base64dec_getc(const char **src) { - while (**src && !isprint(**src)) (*src)++; + while (**src && !isprint(**src)) + (*src)++; return **src ? *((*src)++) : '='; /* emulate padding if string ends */ } From 019449a7e64a881be8cc5d715fe9de32726ba190 Mon Sep 17 00:00:00 2001 From: "Roberto E. Vargas Caballero" Date: Fri, 10 Apr 2020 22:50:23 +0200 Subject: [PATCH 131/185] Add terminfo entries for backspace mode St used to use backspace as BS until the commit 230d0c8, but due to general lack of knowledge of lusers, we moved to the most common configuration in linux to avoid answering the same question 3 times per month. With the most common configuration we have a backspace that returns a DEL, and we have a Delete key that doesn't return a DEL character neither a BS. When dealing with devices connected using a serial line (or even with Plan9) it is more common Backspace as BS and Delete as DEL. For this reason, st is not always the best tool when you talk with a serial device. This patch adds new terminfo entries for Backspace as BS and Delete as DEL. A patch for confg.h is also added, to make easier switch between both configurations. --- st.info | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/st.info b/st.info index 78ffd30..1df490b 100644 --- a/st.info +++ b/st.info @@ -220,3 +220,13 @@ st-meta-256color| simpleterm with meta key and 256 colors, smm=\E[?1034h, rs2=\E[4l\E>\E[?1034h, is2=\E[4l\E>\E[?1034h, + +st-bs| simpleterm with backspace as backspace, + use=st, + kbs=\010, + kdch1=\177, + +st-bs-256color| simpleterm with backspace as backspace and 256colors, + use=st-256color, + kbs=\010, + kdch1=\177, From 0b73612c0dc51dbec1717e5da94bc94559c37246 Mon Sep 17 00:00:00 2001 From: "Roberto E. Vargas Caballero" Date: Sat, 11 Apr 2020 11:52:58 +0200 Subject: [PATCH 132/185] Update FAQ with the last modifications --- FAQ | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/FAQ b/FAQ index 78c769a..85534a4 100644 --- a/FAQ +++ b/FAQ @@ -17,10 +17,16 @@ you can manually run `tic -sx st.info`. ## How do I scroll back up? -Using a terminal multiplexer. +* Using a terminal multiplexer. + * `st -e tmux` using C-b [ + * `st -e screen` using C-a ESC +* Using the excellent tool of [scroll](https://git.suckless.org/scroll/). +* Using the scrollback [patch](https://st.suckless.org/patches/scrollback/). -* `st -e tmux` using C-b [ -* `st -e screen` using C-a ESC +## I would like to have utmp and/or scroll functionality by default + +You can add the absolute patch of both programs in your config.h +file. You only have to modify the value of utmp and scroll variables. ## Why doesn't the Del key work in some programs? From c1145268f6b6c6f03a8bec1c09d356d6a4eba77e Mon Sep 17 00:00:00 2001 From: Quentin Rameau Date: Sat, 11 Apr 2020 12:09:20 +0200 Subject: [PATCH 133/185] Launch scroll program with the default shell --- st.c | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/st.c b/st.c index 2ecf8f3..59db144 100644 --- a/st.c +++ b/st.c @@ -682,9 +682,12 @@ execsh(char *cmd, char **args) if (args) { prog = args[0]; arg = NULL; - } else if (scroll || utmp) { - prog = scroll ? scroll : utmp; - arg = scroll ? utmp : NULL; + } else if (scroll) { + prog = scroll; + arg = utmp ? utmp : sh; + } else if (utmp) { + prog = utmp; + arg = NULL; } else { prog = sh; arg = NULL; From e997303502ddd5c26cfc41af0ff5356bffc04359 Mon Sep 17 00:00:00 2001 From: Hiltjo Posthuma Date: Sat, 11 Apr 2020 13:29:48 +0200 Subject: [PATCH 134/185] Fix small typos --- st.c | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/st.c b/st.c index 59db144..a545724 100644 --- a/st.c +++ b/st.c @@ -823,15 +823,14 @@ ttyread(void) { static char buf[BUFSIZ]; static int buflen = 0; - int written; - int ret; + int ret, written; /* append read bytes to unprocessed bytes */ ret = read(cmdfd, buf+buflen, LEN(buf)-buflen); switch (ret) { case 0: - fputs("Found EOF in input\n", stderr); + fputs("found EOF in input\n", stderr); exit(0); case -1: die("couldn't read from shell: %s\n", strerror(errno)); @@ -839,7 +838,7 @@ ttyread(void) buflen += ret; written = twrite(buf, buflen, 0); buflen -= written; - /* keep any uncomplete utf8 char for the next call */ + /* keep any incomplete UTF-8 byte sequence for the next call */ if (buflen > 0) memmove(buf, buf + written, buflen); return ret; From d66bd405c0d0f29beff89683a04a10297e962cb9 Mon Sep 17 00:00:00 2001 From: Hiltjo Posthuma Date: Sat, 11 Apr 2020 13:56:31 +0200 Subject: [PATCH 135/185] config.def.h: add a comment for the scroll variable --- config.def.h | 1 + 1 file changed, 1 insertion(+) diff --git a/config.def.h b/config.def.h index dfcbda9..0895a1f 100644 --- a/config.def.h +++ b/config.def.h @@ -18,6 +18,7 @@ static int borderpx = 2; */ static char *shell = "/bin/sh"; char *utmp = NULL; +/* scroll program: to enable use a string like "scroll" */ char *scroll = NULL; char *stty_args = "stty raw pass8 nl -echo -iexten -cstopb 38400"; From 771bc401f76b329f78a285ca65355d4b43415172 Mon Sep 17 00:00:00 2001 From: "Roberto E. Vargas Caballero" Date: Sat, 11 Apr 2020 14:46:17 +0200 Subject: [PATCH 136/185] Add st-mono terminfo entry This entry is intended for monocolor display and it is very helpful for color haters. --- st.info | 20 ++++++++++++-------- 1 file changed, 12 insertions(+), 8 deletions(-) diff --git a/st.info b/st.info index 1df490b..e2abc98 100644 --- a/st.info +++ b/st.info @@ -1,4 +1,4 @@ -st| simpleterm, +st-mono| simpleterm monocolor, acsc=+C\,D-A.B0E``aaffgghFiGjjkkllmmnnooppqqrrssttuuvvwwxxyyzz{{||}}~~, am, bce, @@ -10,7 +10,7 @@ st| simpleterm, civis=\E[?25l, clear=\E[H\E[2J, cnorm=\E[?12l\E[?25h, - colors#8, + colors#2, cols#80, cr=^M, csr=\E[%i%p1%d;%p2%dr, @@ -168,13 +168,8 @@ st| simpleterm, rs1=\Ec, rs2=\E[4l\E>\E[?1034l, sc=\E7, - setab=\E[4%p1%dm, - setaf=\E[3%p1%dm, - setb=\E[4%?%p1%{1}%=%t4%e%p1%{3}%=%t6%e%p1%{4}%=%t1%e%p1%{6}%=%t3%e%p1%d%;m, - setf=\E[3%?%p1%{1}%=%t4%e%p1%{3}%=%t6%e%p1%{4}%=%t1%e%p1%{6}%=%t3%e%p1%d%;m, - sgr0=\E[0m, - sgr=%?%p9%t\E(0%e\E(B%;\E[0%?%p6%t;1%;%?%p2%t;4%;%?%p1%p3%|%t;7%;%?%p4%t;5%;%?%p7%t;8%;m, sitm=\E[3m, + sgr0=\E[0m, smacs=\E(0, smcup=\E[?1049h, smir=\E[4h, @@ -194,6 +189,15 @@ st| simpleterm, Se=\E[2 q, Ss=\E[%p1%d q, +st| simpleterm, + use=st-mono, + colors#8, + setab=\E[4%p1%dm, + setaf=\E[3%p1%dm, + setb=\E[4%?%p1%{1}%=%t4%e%p1%{3}%=%t6%e%p1%{4}%=%t1%e%p1%{6}%=%t3%e%p1%d%;m, + setf=\E[3%?%p1%{1}%=%t4%e%p1%{3}%=%t6%e%p1%{4}%=%t1%e%p1%{6}%=%t3%e%p1%d%;m, + sgr=%?%p9%t\E(0%e\E(B%;\E[0%?%p6%t;1%;%?%p2%t;4%;%?%p1%p3%|%t;7%;%?%p4%t;5%;%?%p7%t;8%;m, + st-256color| simpleterm with 256 colors, use=st, ccc, From 33a9a456644ceb235ea6ce61282f3bdce7a8b547 Mon Sep 17 00:00:00 2001 From: Hiltjo Posthuma Date: Sat, 11 Apr 2020 15:45:06 +0200 Subject: [PATCH 137/185] just remove the EOF message --- st.c | 1 - 1 file changed, 1 deletion(-) diff --git a/st.c b/st.c index a545724..9e568c6 100644 --- a/st.c +++ b/st.c @@ -830,7 +830,6 @@ ttyread(void) switch (ret) { case 0: - fputs("found EOF in input\n", stderr); exit(0); case -1: die("couldn't read from shell: %s\n", strerror(errno)); From 72e3f6c7c05b4d5b56388508bb20a863aec279f5 Mon Sep 17 00:00:00 2001 From: Ivan Tham Date: Sun, 19 Apr 2020 19:38:39 +0200 Subject: [PATCH 138/185] Update XIM cursor position only if changed Updating XIM cursor position is expensive, so only update it when cursor position changed. --- st.c | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/st.c b/st.c index 9e568c6..0ce6ac2 100644 --- a/st.c +++ b/st.c @@ -2571,6 +2571,7 @@ void drawregion(int x1, int y1, int x2, int y2) { int y; + for (y = y1; y < y2; y++) { if (!term.dirty[y]) continue; @@ -2583,7 +2584,7 @@ drawregion(int x1, int y1, int x2, int y2) void draw(void) { - int cx = term.c.x; + int cx = term.c.x, ocx = term.ocx, ocy = term.ocy; if (!xstartdraw()) return; @@ -2599,9 +2600,11 @@ draw(void) drawregion(0, 0, term.col, term.row); xdrawcursor(cx, term.c.y, term.line[term.c.y][cx], term.ocx, term.ocy, term.line[term.ocy][term.ocx]); - term.ocx = cx, term.ocy = term.c.y; + term.ocx = cx; + term.ocy = term.c.y; xfinishdraw(); - xximspot(term.ocx, term.ocy); + if (ocx != term.ocx || ocy != term.ocy) + xximspot(term.ocx, term.ocy); } void From 43a395ae91f7d67ce694e65edeaa7bbc720dd027 Mon Sep 17 00:00:00 2001 From: Hiltjo Posthuma Date: Mon, 27 Apr 2020 13:56:25 +0200 Subject: [PATCH 139/185] bump version to 0.8.3 --- config.mk | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/config.mk b/config.mk index 0cbb002..beafc35 100644 --- a/config.mk +++ b/config.mk @@ -1,5 +1,5 @@ # st version -VERSION = 0.8.2 +VERSION = 0.8.3 # Customize below to fit your system From d6ea0a1a61853dd892029a7126e7fdb70c371878 Mon Sep 17 00:00:00 2001 From: Jan Klemkow Date: Thu, 30 Apr 2020 00:10:02 +0200 Subject: [PATCH 140/185] replace exit(3) by _exit(2) in signal handler sigchld() exit(3) is not async-signal-safe but, _exit(2) is. This change prevents st to crash and dump core. --- st.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/st.c b/st.c index 0ce6ac2..2bf133f 100644 --- a/st.c +++ b/st.c @@ -730,7 +730,7 @@ sigchld(int a) die("child exited with status %d\n", WEXITSTATUS(stat)); else if (WIFSIGNALED(stat)) die("child terminated due to signal %d\n", WTERMSIG(stat)); - exit(0); + _exit(0); } void From 1d590910652519268152eae6b97cf30ace4e90c0 Mon Sep 17 00:00:00 2001 From: "Avi Halachmi (:avih)" Date: Tue, 26 Feb 2019 22:37:49 +0200 Subject: [PATCH 141/185] auto-sync: draw on idle to avoid flicker/tearing st could easily tear/flicker with animation or other unattended output. This commit eliminates most of the tear/flicker. Before this commit, the display timing had two "modes": - Interactively, st was waiting fixed `1000/xfps` ms after forwarding the kb/mouse event to the application and before drawing. - Unattended, and specifically with animations, the draw frequency was throttled to `actionfps`. Animation at a higher rate would throttle and likely tear, and at lower rates it was tearing big frames (specifically, when one `read` didn't get a full "frame"). The interactive behavior was decent, but it was impossible to get good unattended-draw behavior even with carefully chosen configuration. This commit changes the behavior such that it draws on idle instead of using fixed latency/frequency. This means that it tries to draw only when it's very likely that the application has completed its output (or after some duration without idle), so it mostly succeeds to avoid tear, flicker, and partial drawing. The config values minlatency/maxlatency replace xfps/actionfps and define the range which the algorithm is allowed to wait from the initial draw-trigger until the actual draw. The range enables the flexibility to choose when to draw - when least likely to flicker. It also unifies the interactive and unattended behavior and config values, which makes the code simpler as well - without sacrificing latency during interactive use, because typically interactively idle arrives very quickly, so the wait is typically minlatency. While it only slighly improves interactive behavior, for animations and other unattended-drawing it improves greatly, as it effectively adapts to any [animation] output rate without tearing, throttling, redundant drawing, or unnecessary delays (sounds impossible, but it works). --- config.def.h | 11 +++-- x.c | 120 ++++++++++++++++++++++++--------------------------- 2 files changed, 65 insertions(+), 66 deletions(-) diff --git a/config.def.h b/config.def.h index 0895a1f..fdbacfd 100644 --- a/config.def.h +++ b/config.def.h @@ -43,9 +43,14 @@ static unsigned int tripleclicktimeout = 600; /* alt screens */ int allowaltscreen = 1; -/* frames per second st should at maximum draw to the screen */ -static unsigned int xfps = 120; -static unsigned int actionfps = 30; +/* + * draw latency range in ms - from new content/keypress/etc until drawing. + * within this range, st draws when content stops arriving (idle). mostly it's + * near minlatency, but it waits longer for slow updates to avoid partial draw. + * low minlatency will tear/flicker more, as it can "detect" idle too early. + */ +static double minlatency = 8; +static double maxlatency = 33; /* * blinking timeout (set to 0 to disable blinking) for the terminal blinking diff --git a/x.c b/x.c index e5f1737..cbbd11f 100644 --- a/x.c +++ b/x.c @@ -1867,10 +1867,9 @@ run(void) XEvent ev; int w = win.w, h = win.h; fd_set rfd; - int xfd = XConnectionNumber(xw.dpy), xev, blinkset = 0, dodraw = 0; - int ttyfd; - struct timespec drawtimeout, *tv = NULL, now, last, lastblink; - long deltatime; + int xfd = XConnectionNumber(xw.dpy), ttyfd, xev, drawing; + struct timespec seltv, *tv, now, lastblink, trigger; + double timeout; /* Waiting for window mapping */ do { @@ -1891,82 +1890,77 @@ run(void) ttyfd = ttynew(opt_line, shell, opt_io, opt_cmd); cresize(w, h); - clock_gettime(CLOCK_MONOTONIC, &last); - lastblink = last; - - for (xev = actionfps;;) { + for (timeout = -1, drawing = 0, lastblink = (struct timespec){0};;) { FD_ZERO(&rfd); FD_SET(ttyfd, &rfd); FD_SET(xfd, &rfd); + if (XPending(xw.dpy)) + timeout = 0; /* existing events might not set xfd */ + + seltv.tv_sec = timeout / 1E3; + seltv.tv_nsec = 1E6 * (timeout - 1E3 * seltv.tv_sec); + tv = timeout >= 0 ? &seltv : NULL; + if (pselect(MAX(xfd, ttyfd)+1, &rfd, NULL, NULL, tv, NULL) < 0) { if (errno == EINTR) continue; die("select failed: %s\n", strerror(errno)); } - if (FD_ISSET(ttyfd, &rfd)) { - ttyread(); - if (blinktimeout) { - blinkset = tattrset(ATTR_BLINK); - if (!blinkset) - MODBIT(win.mode, 0, MODE_BLINK); - } - } - - if (FD_ISSET(xfd, &rfd)) - xev = actionfps; - clock_gettime(CLOCK_MONOTONIC, &now); - drawtimeout.tv_sec = 0; - drawtimeout.tv_nsec = (1000 * 1E6)/ xfps; - tv = &drawtimeout; - dodraw = 0; - if (blinktimeout && TIMEDIFF(now, lastblink) > blinktimeout) { - tsetdirtattr(ATTR_BLINK); - win.mode ^= MODE_BLINK; - lastblink = now; - dodraw = 1; - } - deltatime = TIMEDIFF(now, last); - if (deltatime > 1000 / (xev ? xfps : actionfps)) { - dodraw = 1; - last = now; + if (FD_ISSET(ttyfd, &rfd)) + ttyread(); + + xev = 0; + while (XPending(xw.dpy)) { + xev = 1; + XNextEvent(xw.dpy, &ev); + if (XFilterEvent(&ev, None)) + continue; + if (handler[ev.type]) + (handler[ev.type])(&ev); } - if (dodraw) { - while (XPending(xw.dpy)) { - XNextEvent(xw.dpy, &ev); - if (XFilterEvent(&ev, None)) - continue; - if (handler[ev.type]) - (handler[ev.type])(&ev); + /* + * To reduce flicker and tearing, when new content or event + * triggers drawing, we first wait a bit to ensure we got + * everything, and if nothing new arrives - we draw. + * We start with trying to wait minlatency ms. If more content + * arrives sooner, we retry with shorter and shorter preiods, + * and eventually draw even without idle after maxlatency ms. + * Typically this results in low latency while interacting, + * maximum latency intervals during `cat huge.txt`, and perfect + * sync with periodic updates from animations/key-repeats/etc. + */ + if (FD_ISSET(ttyfd, &rfd) || xev) { + if (!drawing) { + trigger = now; + drawing = 1; } + timeout = (maxlatency - TIMEDIFF(now, trigger)) \ + / maxlatency * minlatency; + if (timeout > 0) + continue; /* we have time, try to find idle */ + } - draw(); - XFlush(xw.dpy); - - if (xev && !FD_ISSET(xfd, &rfd)) - xev--; - if (!FD_ISSET(ttyfd, &rfd) && !FD_ISSET(xfd, &rfd)) { - if (blinkset) { - if (TIMEDIFF(now, lastblink) \ - > blinktimeout) { - drawtimeout.tv_nsec = 1000; - } else { - drawtimeout.tv_nsec = (1E6 * \ - (blinktimeout - \ - TIMEDIFF(now, - lastblink))); - } - drawtimeout.tv_sec = \ - drawtimeout.tv_nsec / 1E9; - drawtimeout.tv_nsec %= (long)1E9; - } else { - tv = NULL; - } + /* idle detected or maxlatency exhausted -> draw */ + timeout = -1; + if (blinktimeout && tattrset(ATTR_BLINK)) { + timeout = blinktimeout - TIMEDIFF(now, lastblink); + if (timeout <= 0) { + if (-timeout > blinktimeout) /* start visible */ + win.mode |= MODE_BLINK; + win.mode ^= MODE_BLINK; + tsetdirtattr(ATTR_BLINK); + lastblink = now; + timeout = blinktimeout; } } + + draw(); + XFlush(xw.dpy); + drawing = 0; } } From 87545c612e8ab6e7cd1ef38e2355d0cb86df79f2 Mon Sep 17 00:00:00 2001 From: Hiltjo Posthuma Date: Sat, 9 May 2020 13:55:34 +0200 Subject: [PATCH 142/185] tiny code-style and typo-fix in comment --- st.c | 6 +++--- x.c | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/st.c b/st.c index 2bf133f..c161497 100644 --- a/st.c +++ b/st.c @@ -38,7 +38,7 @@ /* macros */ #define IS_SET(flag) ((term.mode & (flag)) != 0) -#define ISCONTROLC0(c) (BETWEEN(c, 0, 0x1f) || (c) == '\177') +#define ISCONTROLC0(c) (BETWEEN(c, 0, 0x1f) || (c) == 0x7f) #define ISCONTROLC1(c) (BETWEEN(c, 0x80, 0x9f)) #define ISCONTROL(c) (ISCONTROLC0(c) || ISCONTROLC1(c)) #define ISDELIM(u) (u && wcschr(worddelimiters, u)) @@ -2023,7 +2023,7 @@ tdumpline(int n) bp = &term.line[n][0]; end = &bp[MIN(tlinelen(n), term.col) - 1]; if (bp != end || bp->u != ' ') { - for ( ;bp <= end; ++bp) + for ( ; bp <= end; ++bp) tprinter(buf, utf8encode(bp->u, buf)); } tprinter("\n", 1); @@ -2307,7 +2307,7 @@ tputc(Rune u) Glyph *gp; control = ISCONTROL(u); - if (!IS_SET(MODE_UTF8) && !IS_SET(MODE_SIXEL)) { + if (!IS_SET(MODE_UTF8 | MODE_SIXEL)) { c[0] = u; width = len = 1; } else { diff --git a/x.c b/x.c index cbbd11f..7bfc36f 100644 --- a/x.c +++ b/x.c @@ -1927,7 +1927,7 @@ run(void) * triggers drawing, we first wait a bit to ensure we got * everything, and if nothing new arrives - we draw. * We start with trying to wait minlatency ms. If more content - * arrives sooner, we retry with shorter and shorter preiods, + * arrives sooner, we retry with shorter and shorter periods, * and eventually draw even without idle after maxlatency ms. * Typically this results in low latency while interacting, * maximum latency intervals during `cat huge.txt`, and perfect From 8211e36d281990a39db1853bcd454ac59e53d521 Mon Sep 17 00:00:00 2001 From: Hiltjo Posthuma Date: Sat, 9 May 2020 13:56:28 +0200 Subject: [PATCH 143/185] fix for incorrect (partial) written sequences when libc wcwidth() == -1 Fix an issue with incorrect (partial) written sequences when libc wcwidth() == -1. The sequence is updated to on wcwidth(u) == -1: c = "\357\277\275" but len isn't. A way to reproduce in practise: * st -o dump.txt * In the terminal: printf '\xcd\xb8' - This is codepoint 888, on OpenBSD it reports wcwidth() == -1. - Quit the terminal. - Look in dump.txt (partial written sequence of "UTF_INVALID"). This was introduced in: " commit 11625c7166b7e4dad414606227acec2de1c36464 Author: czarkoff@gmail.com Date: Tue Oct 28 12:55:28 2014 +0100 Replace character with U+FFFD if wcwidth() is -1 Helpful when new Unicode codepoints are not recognized by libc." Change: Remove setting the sequence. If this happens to break something, another solution could be setting len = 3 for the sequence. --- st.c | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/st.c b/st.c index c161497..5d4054f 100644 --- a/st.c +++ b/st.c @@ -2312,10 +2312,8 @@ tputc(Rune u) width = len = 1; } else { len = utf8encode(u, c); - if (!control && (width = wcwidth(u)) == -1) { - memcpy(c, "\357\277\275", 4); /* UTF_INVALID */ + if (!control && (width = wcwidth(u)) == -1) width = 1; - } } if (IS_SET(MODE_PRINT)) From cde480c6939e62771ba3b60ef4eb848031aee9f9 Mon Sep 17 00:00:00 2001 From: Hiltjo Posthuma Date: Sat, 9 May 2020 14:03:14 +0200 Subject: [PATCH 144/185] optimize column width calculation and utf-8 encode for ASCII In particular on OpenBSD and on glibc wcwidth() is quite expensive. On musl there is little difference. --- st.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/st.c b/st.c index 5d4054f..1414f98 100644 --- a/st.c +++ b/st.c @@ -2307,7 +2307,7 @@ tputc(Rune u) Glyph *gp; control = ISCONTROL(u); - if (!IS_SET(MODE_UTF8 | MODE_SIXEL)) { + if (u < 127 || !IS_SET(MODE_UTF8 | MODE_SIXEL)) { c[0] = u; width = len = 1; } else { From 914fb825df3bde7abdd7947e54f8bf4d2b55e34e Mon Sep 17 00:00:00 2001 From: Hiltjo Posthuma Date: Sat, 9 May 2020 14:43:31 +0200 Subject: [PATCH 145/185] code-style: add fallthrough comment Patch by Steve Ward, thanks. --- st.c | 1 + x.c | 1 + 2 files changed, 2 insertions(+) diff --git a/st.c b/st.c index 1414f98..a8de17d 100644 --- a/st.c +++ b/st.c @@ -2153,6 +2153,7 @@ tcontrolcode(uchar ascii) return; case '\032': /* SUB */ tsetchar('?', &term.c.attr, term.c.x, term.c.y); + /* FALLTHROUGH */ case '\030': /* CAN */ csireset(); break; diff --git a/x.c b/x.c index 7bfc36f..1dc44d6 100644 --- a/x.c +++ b/x.c @@ -1528,6 +1528,7 @@ xdrawcursor(int cx, int cy, Glyph g, int ox, int oy, Glyph og) switch (win.cursor) { case 7: /* st extension: snowman (U+2603) */ g.u = 0x2603; + /* FALLTHROUGH */ case 0: /* Blinking Block */ case 1: /* Blinking Block (Default) */ case 2: /* Steady Block */ From 8304d4f0599b1be2226c28c553547070658d4af3 Mon Sep 17 00:00:00 2001 From: Jakub Leszczak Date: Wed, 6 May 2020 13:35:06 +0200 Subject: [PATCH 146/185] Fix selection: selclear in tputc --- st.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/st.c b/st.c index a8de17d..8c42a9c 100644 --- a/st.c +++ b/st.c @@ -2412,7 +2412,7 @@ check_control_code: */ return; } - if (sel.ob.x != -1 && BETWEEN(term.c.y, sel.ob.y, sel.oe.y)) + if (selected(term.c.x, term.c.y)) selclear(); gp = &term.line[term.c.y][term.c.x]; From 9c30066e73f0105c3fccb7582c8172d5117857b3 Mon Sep 17 00:00:00 2001 From: Jakub Leszczak Date: Wed, 6 May 2020 13:35:53 +0200 Subject: [PATCH 147/185] Fix selection: ignore ATTR_WRAP when rectangular selection in getsel --- st.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/st.c b/st.c index 8c42a9c..7c15d5f 100644 --- a/st.c +++ b/st.c @@ -634,7 +634,8 @@ getsel(void) * st. * FIXME: Fix the computer world. */ - if ((y < sel.ne.y || lastx >= linelen) && !(last->mode & ATTR_WRAP)) + if ((y < sel.ne.y || lastx >= linelen) && + (!(last->mode & ATTR_WRAP) || sel.type == SEL_RECTANGULAR)) *ptr++ = '\n'; } *ptr = 0; From 045a0fab4f80b57f4a982ae6bc5f33fe21d66111 Mon Sep 17 00:00:00 2001 From: Jakub Leszczak Date: Wed, 6 May 2020 13:36:59 +0200 Subject: [PATCH 148/185] Fix selection: selscroll --- st.c | 26 ++++++++------------------ 1 file changed, 8 insertions(+), 18 deletions(-) diff --git a/st.c b/st.c index 7c15d5f..0d35613 100644 --- a/st.c +++ b/st.c @@ -1106,27 +1106,17 @@ selscroll(int orig, int n) if (sel.ob.x == -1) return; - if (BETWEEN(sel.ob.y, orig, term.bot) || BETWEEN(sel.oe.y, orig, term.bot)) { - if ((sel.ob.y += n) > term.bot || (sel.oe.y += n) < term.top) { + if (BETWEEN(sel.nb.y, orig, term.bot) != BETWEEN(sel.ne.y, orig, term.bot)) { + selclear(); + } else if (BETWEEN(sel.nb.y, orig, term.bot)) { + sel.ob.y += n; + sel.oe.y += n; + if (sel.ob.y < term.top || sel.ob.y > term.bot || + sel.oe.y < term.top || sel.oe.y > term.bot) { selclear(); - return; - } - if (sel.type == SEL_RECTANGULAR) { - if (sel.ob.y < term.top) - sel.ob.y = term.top; - if (sel.oe.y > term.bot) - sel.oe.y = term.bot; } else { - if (sel.ob.y < term.top) { - sel.ob.y = term.top; - sel.ob.x = 0; - } - if (sel.oe.y > term.bot) { - sel.oe.y = term.bot; - sel.oe.x = term.col; - } + selnormalize(); } - selnormalize(); } } From bda9c9ffa645ee5e4b2507474ebfa1c5efb889b2 Mon Sep 17 00:00:00 2001 From: k0ga Date: Sat, 16 May 2020 09:48:18 +0000 Subject: [PATCH 149/185] Make shift+wheel behaves as shift+Prev/Next St uses a very good hack where mouse wheel genereates ^Y and ^E, that are the same keys that less and vi uses for backward and fordward scrolling. Scroll, as many terminal emulators, use shift+Prev/Next for scrolling, but it is also using ^E and ^Y for scroling, characters that are reserved in the POSIX shell in emacs mode for end of line and yanking, making scroll unsable in st. This patch adds a new hack, making shift+wheel returning the same sequences than shift+Prev/Next, meaning that scroll or any other similar program will not be able to differentiate between them. --- config.def.h | 2 ++ 1 file changed, 2 insertions(+) diff --git a/config.def.h b/config.def.h index fdbacfd..293e00c 100644 --- a/config.def.h +++ b/config.def.h @@ -171,7 +171,9 @@ static uint forcemousemod = ShiftMask; static MouseShortcut mshortcuts[] = { /* mask button function argument release */ { XK_ANY_MOD, Button2, selpaste, {.i = 0}, 1 }, + { ShiftMask, Button4, ttysend, {.s = "\033[5;2~"} }, { XK_ANY_MOD, Button4, ttysend, {.s = "\031"} }, + { ShiftMask, Button5, ttysend, {.s = "\033[6;2~"} }, { XK_ANY_MOD, Button5, ttysend, {.s = "\005"} }, }; From f8afebdfa0cc9a57b22c39c47e9b585f69453eb7 Mon Sep 17 00:00:00 2001 From: "Roberto E. Vargas" Date: Sat, 16 May 2020 10:42:51 +0000 Subject: [PATCH 150/185] Add rin terminfo capability Tianlin Qu discovered that st is missing rin (scroll back #1 lines). --- st.info | 1 + 1 file changed, 1 insertion(+) diff --git a/st.info b/st.info index e2abc98..d0694e2 100644 --- a/st.info +++ b/st.info @@ -158,6 +158,7 @@ st-mono| simpleterm monocolor, rc=\E8, rev=\E[7m, ri=\EM, + rin=\E[%p1%dT, ritm=\E[23m, rmacs=\E(B, rmcup=\E[?1049l, From e8392b282c2eaa28725241a9612804fb55113da4 Mon Sep 17 00:00:00 2001 From: "Avi Halachmi (:avih)" Date: Thu, 14 May 2020 18:18:07 +0300 Subject: [PATCH 151/185] support REP (repeat) escape sequence The sequence \e[Nb prints the last printed char N (more) times if it's printable, and it's ignored after newline or other control chars. This is Ecma-048/ANSI-X3.6 sequence and not DEC VT. It's supported by xterm, and ncurses uses it when possible, e.g. when TERM is xterm* (and with this commit also st*). xterm supports only codepoints<=255, possibly due to internal limits. We support any value/codepoint which was placed in a cell. To test: - tput rep 65 4 -> prints 'AAAA' - printf "\342\225\246\033[4b" -> prints U+2566 1+4 times. --- st.c | 10 ++++++++++ st.info | 1 + 2 files changed, 11 insertions(+) diff --git a/st.c b/st.c index 0d35613..54af098 100644 --- a/st.c +++ b/st.c @@ -129,6 +129,7 @@ typedef struct { int charset; /* current charset */ int icharset; /* selected charset for sequence */ int *tabs; + Rune lastc; /* last printed char outside of sequence, 0 if control */ } Term; /* CSI Escape sequence structs */ @@ -1648,6 +1649,12 @@ csihandle(void) if (csiescseq.arg[0] == 0) ttywrite(vtiden, strlen(vtiden), 0); break; + case 'b': /* REP -- if last char is printable print it more times */ + DEFAULT(csiescseq.arg[0], 1); + if (term.lastc) + while (csiescseq.arg[0]-- > 0) + tputc(term.lastc); + break; case 'C': /* CUF -- Cursor Forward */ case 'a': /* HPR -- Cursor Forward */ DEFAULT(csiescseq.arg[0], 1); @@ -2373,6 +2380,8 @@ check_control_code: /* * control codes are not shown ever */ + if (!term.esc) + term.lastc = 0; return; } else if (term.esc & ESC_START) { if (term.esc & ESC_CSI) { @@ -2422,6 +2431,7 @@ check_control_code: } tsetchar(u, &term.c.attr, term.c.x, term.c.y); + term.lastc = u; if (width == 2) { gp->mode |= ATTR_WIDE; diff --git a/st.info b/st.info index d0694e2..e5393db 100644 --- a/st.info +++ b/st.info @@ -184,6 +184,7 @@ st-mono| simpleterm monocolor, # XTerm extensions rmxx=\E[29m, smxx=\E[9m, + rep=%p1%c\E[%p2%{1}%-%db, # tmux extensions, see TERMINFO EXTENSIONS in tmux(1) Tc, Ms=\E]52;%p1%s;%p2%s\007, From 475a0a36cb4fda1da30f014da65988e99b222876 Mon Sep 17 00:00:00 2001 From: Hiltjo Posthuma Date: Sat, 16 May 2020 18:06:42 +0200 Subject: [PATCH 152/185] Revert "support REP (repeat) escape sequence" This reverts commit e8392b282c2eaa28725241a9612804fb55113da4. There is currently a bug in older ncurses versions (like on OpenBSD) where a fix for a bug with REP is not backported yet. Most likely in tty/tty_update.c: Noticed while using lynx (which uses ncurses/curses). To reproduce using lynx: echo "Z0000000" | lynx -stdin or using the program: int main(void) { WINDOW *win; win = initscr(); printw("Z0000000"); refresh(); sleep(5); return 0; } This prints "ZZZZZZZ" (incorrectly). --- st.c | 10 ---------- st.info | 1 - 2 files changed, 11 deletions(-) diff --git a/st.c b/st.c index 54af098..0d35613 100644 --- a/st.c +++ b/st.c @@ -129,7 +129,6 @@ typedef struct { int charset; /* current charset */ int icharset; /* selected charset for sequence */ int *tabs; - Rune lastc; /* last printed char outside of sequence, 0 if control */ } Term; /* CSI Escape sequence structs */ @@ -1649,12 +1648,6 @@ csihandle(void) if (csiescseq.arg[0] == 0) ttywrite(vtiden, strlen(vtiden), 0); break; - case 'b': /* REP -- if last char is printable print it more times */ - DEFAULT(csiescseq.arg[0], 1); - if (term.lastc) - while (csiescseq.arg[0]-- > 0) - tputc(term.lastc); - break; case 'C': /* CUF -- Cursor Forward */ case 'a': /* HPR -- Cursor Forward */ DEFAULT(csiescseq.arg[0], 1); @@ -2380,8 +2373,6 @@ check_control_code: /* * control codes are not shown ever */ - if (!term.esc) - term.lastc = 0; return; } else if (term.esc & ESC_START) { if (term.esc & ESC_CSI) { @@ -2431,7 +2422,6 @@ check_control_code: } tsetchar(u, &term.c.attr, term.c.x, term.c.y); - term.lastc = u; if (width == 2) { gp->mode |= ATTR_WIDE; diff --git a/st.info b/st.info index e5393db..d0694e2 100644 --- a/st.info +++ b/st.info @@ -184,7 +184,6 @@ st-mono| simpleterm monocolor, # XTerm extensions rmxx=\E[29m, smxx=\E[9m, - rep=%p1%c\E[%p2%{1}%-%db, # tmux extensions, see TERMINFO EXTENSIONS in tmux(1) Tc, Ms=\E]52;%p1%s;%p2%s\007, From dec6b530a4fddf405c1822b2cac6e2036d3c8b75 Mon Sep 17 00:00:00 2001 From: Steve Ward Date: Wed, 20 May 2020 22:24:55 -0400 Subject: [PATCH 153/185] Call xsetcursor to set win.cursor in main In xsetcursor, remove "DEFAULT(cursor, 1)" because 0 is a valid value. Increase max allowed value of cursor from 6 to 7 (st extension). --- x.c | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/x.c b/x.c index 1dc44d6..210f184 100644 --- a/x.c +++ b/x.c @@ -1526,8 +1526,8 @@ xdrawcursor(int cx, int cy, Glyph g, int ox, int oy, Glyph og) /* draw the new one */ if (IS_SET(MODE_FOCUSED)) { switch (win.cursor) { - case 7: /* st extension: snowman (U+2603) */ - g.u = 0x2603; + case 7: /* st extension */ + g.u = 0x2603; /* snowman (U+2603) */ /* FALLTHROUGH */ case 0: /* Blinking Block */ case 1: /* Blinking Block (Default) */ @@ -1690,8 +1690,7 @@ xsetmode(int set, unsigned int flags) int xsetcursor(int cursor) { - DEFAULT(cursor, 1); - if (!BETWEEN(cursor, 0, 6)) + if (!BETWEEN(cursor, 0, 7)) /* 7: st extension */ return 1; win.cursor = cursor; return 0; @@ -1983,7 +1982,7 @@ main(int argc, char *argv[]) { xw.l = xw.t = 0; xw.isfixed = False; - win.cursor = cursorshape; + xsetcursor(cursorshape); ARGBEGIN { case 'a': From 94b8ec002101a5e8f52a342e53431eea71aa0631 Mon Sep 17 00:00:00 2001 From: Hiltjo Posthuma Date: Sat, 30 May 2020 21:34:57 +0200 Subject: [PATCH 154/185] Partially add back in "support REP (repeat) escape sequence" Add the functionality back in for xterm compatibility, but do not expose the capability in st.info (yet). Some notes: It was reverted because it caused some issues with ncurses in some configurations, namely when using BSD padding (--enable-bsdpad, BSD_TPUTS) in ncurses it caused issues with repeating digits. A fix has been upstreamed in ncurses since snapshot 20200523. The fix is also backported to OpenBSD -current. --- st.c | 10 ++++++++++ st.info | 2 ++ 2 files changed, 12 insertions(+) diff --git a/st.c b/st.c index 0d35613..54af098 100644 --- a/st.c +++ b/st.c @@ -129,6 +129,7 @@ typedef struct { int charset; /* current charset */ int icharset; /* selected charset for sequence */ int *tabs; + Rune lastc; /* last printed char outside of sequence, 0 if control */ } Term; /* CSI Escape sequence structs */ @@ -1648,6 +1649,12 @@ csihandle(void) if (csiescseq.arg[0] == 0) ttywrite(vtiden, strlen(vtiden), 0); break; + case 'b': /* REP -- if last char is printable print it more times */ + DEFAULT(csiescseq.arg[0], 1); + if (term.lastc) + while (csiescseq.arg[0]-- > 0) + tputc(term.lastc); + break; case 'C': /* CUF -- Cursor Forward */ case 'a': /* HPR -- Cursor Forward */ DEFAULT(csiescseq.arg[0], 1); @@ -2373,6 +2380,8 @@ check_control_code: /* * control codes are not shown ever */ + if (!term.esc) + term.lastc = 0; return; } else if (term.esc & ESC_START) { if (term.esc & ESC_CSI) { @@ -2422,6 +2431,7 @@ check_control_code: } tsetchar(u, &term.c.attr, term.c.x, term.c.y); + term.lastc = u; if (width == 2) { gp->mode |= ATTR_WIDE; diff --git a/st.info b/st.info index d0694e2..8201ad6 100644 --- a/st.info +++ b/st.info @@ -184,6 +184,8 @@ st-mono| simpleterm monocolor, # XTerm extensions rmxx=\E[29m, smxx=\E[9m, +# disabled rep for now: causes some issues with older ncurses versions. +# rep=%p1%c\E[%p2%{1}%-%db, # tmux extensions, see TERMINFO EXTENSIONS in tmux(1) Tc, Ms=\E]52;%p1%s;%p2%s\007, From e6e2c6199f102f1459b53717050eee27832f4f87 Mon Sep 17 00:00:00 2001 From: Hiltjo Posthuma Date: Sat, 30 May 2020 21:39:49 +0200 Subject: [PATCH 155/185] tiny style fix --- st.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/st.c b/st.c index 54af098..2d901ab 100644 --- a/st.c +++ b/st.c @@ -843,7 +843,6 @@ ttyread(void) if (buflen > 0) memmove(buf, buf + written, buflen); return ret; - } } @@ -1778,7 +1777,7 @@ csihandle(void) break; case 'n': /* DSR – Device Status Report (cursor position) */ if (csiescseq.arg[0] == 6) { - len = snprintf(buf, sizeof(buf),"\033[%i;%iR", + len = snprintf(buf, sizeof(buf), "\033[%i;%iR", term.c.y+1, term.c.x+1); ttywrite(buf, len, 0); } From 0f8b40652bca0670f1f0bda069bbc55f8b5e364d Mon Sep 17 00:00:00 2001 From: Hiltjo Posthuma Date: Sat, 30 May 2020 21:50:54 +0200 Subject: [PATCH 156/185] FAQ: add some details about the w3m img hack ... and an example patch to switch from double-buffering to a single buffer. --- FAQ | 59 ++++++++++++++++++++++++++++++++++++++++++++++++++++++----- 1 file changed, 54 insertions(+), 5 deletions(-) diff --git a/FAQ b/FAQ index 85534a4..fb40264 100644 --- a/FAQ +++ b/FAQ @@ -2,12 +2,14 @@ Use the excellent tool of [utmp](https://git.suckless.org/utmp/) for this task. + ## Some _random program_ complains that st is unknown/not recognised/unsupported/whatever! It means that st doesn’t have any terminfo entry on your system. Chances are you did not `make install`. If you just want to test it without installing it, you can manually run `tic -sx st.info`. + ## Nothing works, and nothing is said about an unknown terminal! * Some programs just assume they’re running in xterm i.e. they don’t rely on @@ -15,6 +17,7 @@ you can manually run `tic -sx st.info`. * Some programs don’t complain about the lacking st description and default to another terminal. In that case see the question about terminfo. + ## How do I scroll back up? * Using a terminal multiplexer. @@ -23,11 +26,13 @@ you can manually run `tic -sx st.info`. * Using the excellent tool of [scroll](https://git.suckless.org/scroll/). * Using the scrollback [patch](https://st.suckless.org/patches/scrollback/). + ## I would like to have utmp and/or scroll functionality by default You can add the absolute patch of both programs in your config.h file. You only have to modify the value of utmp and scroll variables. + ## Why doesn't the Del key work in some programs? Taken from the terminfo manpage: @@ -83,12 +88,14 @@ If you are using zsh, then read the zsh FAQ Putting these lines into your .zshrc will fix the problems. + ## How can I use meta in 8bit mode? St supports meta in 8bit mode, but the default terminfo entry doesn't use this capability. If you want it, you have to use the 'st-meta' value in TERM. + ## I cannot compile st in OpenBSD OpenBSD lacks librt, despite it being mandatory in POSIX @@ -97,6 +104,7 @@ If you want to compile st for OpenBSD you have to remove -lrt from config.mk, an st will compile without any loss of functionality, because all the functions are included in libc on this platform. + ## The Backspace Case St is emulating the Linux way of handling backspace being delete and delete being @@ -158,19 +166,60 @@ terminal users wants its backspace to be how he feels it: [1] http://www.ibb.net/~anne/keyboard.html [2] http://www.tldp.org/HOWTO/Keyboard-and-Console-HOWTO-5.html + ## But I really want the old grumpy behaviour of my terminal Apply [1]. [1] https://st.suckless.org/patches/delkey -## Why do images not work in st (in programs such as w3m)? -This is a terrible hack that overdraws an image on top of the terminal emulator -window. It also relies on a very specific way the terminal draws it's contents. +## Why do images not work in st using the w3m image hack? + +w3mimg uses a hack that draws an image on top of the terminal emulator Drawable +window. The hack relies on the terminal to use a single buffer to draw its +contents directly. + +st uses double-buffered drawing so the image is quickly replaced and may show a +short flicker effect. + +Below is a patch example to change st double-buffering to a single Drawable +buffer. + +diff --git a/x.c b/x.c +--- a/x.c ++++ b/x.c +@@ -561,10 +561,6 @@ xresize(int col, int row) + win.tw = MAX(1, col * win.cw); + win.th = MAX(1, row * win.ch); + +- XFreePixmap(xw.dpy, xw.buf); +- xw.buf = XCreatePixmap(xw.dpy, xw.win, win.w, win.h, +- DefaultDepth(xw.dpy, xw.scr)); +- XftDrawChange(xw.draw, xw.buf); + xclear(0, 0, win.w, win.h); + } + +@@ -921,8 +917,7 @@ xinit(void) + gcvalues.graphics_exposures = False; + dc.gc = XCreateGC(xw.dpy, parent, GCGraphicsExposures, + &gcvalues); +- xw.buf = XCreatePixmap(xw.dpy, xw.win, win.w, win.h, +- DefaultDepth(xw.dpy, xw.scr)); ++ xw.buf = xw.win; + XSetForeground(xw.dpy, dc.gc, dc.col[defaultbg].pixel); + XFillRectangle(xw.dpy, xw.buf, dc.gc, 0, 0, win.w, win.h); + +@@ -1386,8 +1381,6 @@ void + draw(void) + { + drawregion(0, 0, term.col, term.row); +- XCopyArea(xw.dpy, xw.buf, xw.win, dc.gc, 0, 0, win.w, +- win.h, 0, 0); + XSetForeground(xw.dpy, dc.gc, + dc.col[IS_SET(MODE_REVERSE)? + defaultfg : defaultbg].pixel); -A more proper (but limited way) would be using sixels. Which st doesn't -support. ## BadLength X error in Xft when trying to render emoji From a2a704492b9f4d2408d180f7aeeacf4c789a1d67 Mon Sep 17 00:00:00 2001 From: Hiltjo Posthuma Date: Sat, 30 May 2020 21:56:18 +0200 Subject: [PATCH 157/185] config.def.h: add an option allowwindowops, by default off (secure) Similar to the xterm AllowWindowOps option, this is an option to allow or disallow certain (non-interactive) operations that can be insecure or exploited. NOTE: xsettitle() is not guarded by this because st does not support printing the window title. Else this could be exploitable (arbitrary code execution). Similar problems have been found in the past in other terminal emulators. The sequence for base64-encoded clipboard copy is now guarded because it allows a sequence written to the terminal to manipulate the clipboard of the running user non-interactively, for example: printf '\x1b]52;0;ZWNobyBoaQ0=\a' --- config.def.h | 4 ++++ st.c | 2 +- st.h | 1 + 3 files changed, 6 insertions(+), 1 deletion(-) diff --git a/config.def.h b/config.def.h index 293e00c..6f05dce 100644 --- a/config.def.h +++ b/config.def.h @@ -43,6 +43,10 @@ static unsigned int tripleclicktimeout = 600; /* alt screens */ int allowaltscreen = 1; +/* allow certain non-interactive (insecure) window operations such as: + setting the clipboard text */ +int allowwindowops = 0; + /* * draw latency range in ms - from new content/keypress/etc until drawing. * within this range, st draws when content stops arriving (idle). mostly it's diff --git a/st.c b/st.c index 2d901ab..ef8abd5 100644 --- a/st.c +++ b/st.c @@ -1861,7 +1861,7 @@ strhandle(void) xsettitle(strescseq.args[1]); return; case 52: - if (narg > 2) { + if (narg > 2 && allowwindowops) { dec = base64dec(strescseq.args[2]); if (dec) { xsetsel(dec); diff --git a/st.h b/st.h index d978458..3d351b6 100644 --- a/st.h +++ b/st.h @@ -118,6 +118,7 @@ extern char *stty_args; extern char *vtiden; extern wchar_t *worddelimiters; extern int allowaltscreen; +extern int allowwindowops; extern char *termname; extern unsigned int tabspaces; extern unsigned int defaultfg; From 9ba7ecf7b15ec2986c6142036706aa353b249ef9 Mon Sep 17 00:00:00 2001 From: Hiltjo Posthuma Date: Mon, 1 Jun 2020 14:09:46 +0200 Subject: [PATCH 158/185] FAQ: fix single-buffer patch rebase against master --- FAQ | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/FAQ b/FAQ index fb40264..0f9609d 100644 --- a/FAQ +++ b/FAQ @@ -189,18 +189,18 @@ buffer. diff --git a/x.c b/x.c --- a/x.c +++ b/x.c -@@ -561,10 +561,6 @@ xresize(int col, int row) - win.tw = MAX(1, col * win.cw); - win.th = MAX(1, row * win.ch); +@@ -732,10 +732,6 @@ xresize(int col, int row) + win.tw = col * win.cw; + win.th = row * win.ch; - XFreePixmap(xw.dpy, xw.buf); - xw.buf = XCreatePixmap(xw.dpy, xw.win, win.w, win.h, - DefaultDepth(xw.dpy, xw.scr)); - XftDrawChange(xw.draw, xw.buf); xclear(0, 0, win.w, win.h); - } -@@ -921,8 +917,7 @@ xinit(void) + /* resize to new width */ +@@ -1148,8 +1144,7 @@ xinit(int cols, int rows) gcvalues.graphics_exposures = False; dc.gc = XCreateGC(xw.dpy, parent, GCGraphicsExposures, &gcvalues); @@ -210,10 +210,10 @@ diff --git a/x.c b/x.c XSetForeground(xw.dpy, dc.gc, dc.col[defaultbg].pixel); XFillRectangle(xw.dpy, xw.buf, dc.gc, 0, 0, win.w, win.h); -@@ -1386,8 +1381,6 @@ void - draw(void) +@@ -1632,8 +1627,6 @@ xdrawline(Line line, int x1, int y1, int x2) + void + xfinishdraw(void) { - drawregion(0, 0, term.col, term.row); - XCopyArea(xw.dpy, xw.buf, xw.win, dc.gc, 0, 0, win.w, - win.h, 0, 0); XSetForeground(xw.dpy, dc.gc, From 818ec746f4caae453d09368b101c3e841cf39870 Mon Sep 17 00:00:00 2001 From: Hiltjo Posthuma Date: Wed, 17 Jun 2020 21:35:39 +0200 Subject: [PATCH 159/185] fix unicode glitch in DCS strings, patch by Tim Allen MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Reported on the mailinglist: " I discovered recently that if an application running inside st tries to send a DCS string, subsequent Unicode characters get messed up. For example, consider the following test-case: printf '\303\277\033P\033\\\303\277' ...where: - \303\277 is the UTF-8 encoding of U+00FF LATIN SMALL LETTER Y WITH DIAERESIS (ÿ). - \033P is ESC P, the token that begins a DCS string. - \033\\ is ESC \, a token that ends a DCS string. - \303\277 is the same ÿ character again. If I run the above command in a VTE-based terminal, or xterm, or QTerminal, or pterm (PuTTY), I get the output: ÿÿ ...which is to say, the empty DCS string is ignored. However, if I run that command inside st (as of commit 9ba7ecf), I get: ÿÿ ...where those last two characters are \303\277 interpreted as ISO8859-1 characters, instead of UTF-8. I spent some time tracing through the state machines in st.c, and so far as I can tell, this is how it works currently: - ESC P sets the "ESC_DCS" and "ESC_STR" flags, indicating that incoming bytes should be collected into the strescseq buffer, rather than being interpreted. - ESC \ sets the "ESC_STR_END" flag (when ESC is received), and then calls strhandle() (when \ is received) to interpret the collected bytes. - If the collected bytes begin with 'P' (i.e. if this was a DCS string) strhandle() sets the "ESC_DCS" flag again, confusing the state machine. If my understanding is correct, fixing the problem should be as easy as removing the line that sets ESC_DCS from strhandle(): diff --git a/st.c b/st.c index ef8abd5..b5b805a 100644 --- a/st.c +++ b/st.c @@ -1897,7 +1897,6 @@ strhandle(void) xsettitle(strescseq.args[0]); return; case 'P': /* DCS -- Device Control String */ - term.mode |= ESC_DCS; case '_': /* APC -- Application Program Command */ case '^': /* PM -- Privacy Message */ return; I've tried the above patch and it fixes my problem, but I don't know if it introduces any others. " --- st.c | 1 - 1 file changed, 1 deletion(-) diff --git a/st.c b/st.c index ef8abd5..b5b805a 100644 --- a/st.c +++ b/st.c @@ -1897,7 +1897,6 @@ strhandle(void) xsettitle(strescseq.args[0]); return; case 'P': /* DCS -- Device Control String */ - term.mode |= ESC_DCS; case '_': /* APC -- Application Program Command */ case '^': /* PM -- Privacy Message */ return; From f74a9df6e1fc88eebe6d673d888b61fd83cf6fc4 Mon Sep 17 00:00:00 2001 From: Hiltjo Posthuma Date: Wed, 17 Jun 2020 22:05:48 +0200 Subject: [PATCH 160/185] remove sixel stub code Remove stub code that was used for an experiment of adding sixel code to st from the commit f7398434. --- st.c | 26 +++++--------------------- 1 file changed, 5 insertions(+), 21 deletions(-) diff --git a/st.c b/st.c index b5b805a..76b7e0d 100644 --- a/st.c +++ b/st.c @@ -51,7 +51,6 @@ enum term_mode { MODE_ECHO = 1 << 4, MODE_PRINT = 1 << 5, MODE_UTF8 = 1 << 6, - MODE_SIXEL = 1 << 7, }; enum cursor_movement { @@ -78,12 +77,11 @@ enum charset { enum escape_state { ESC_START = 1, ESC_CSI = 2, - ESC_STR = 4, /* OSC, PM, APC */ + ESC_STR = 4, /* DCS, OSC, PM, APC */ ESC_ALTCHARSET = 8, ESC_STR_END = 16, /* a final string was encountered */ ESC_TEST = 32, /* Enter in test mode */ ESC_UTF8 = 64, - ESC_DCS =128, }; typedef struct { @@ -2090,12 +2088,9 @@ tdectest(char c) void tstrsequence(uchar c) { - strreset(); - switch (c) { case 0x90: /* DCS -- Device Control String */ c = 'P'; - term.esc |= ESC_DCS; break; case 0x9f: /* APC -- Application Program Command */ c = '_'; @@ -2107,6 +2102,7 @@ tstrsequence(uchar c) c = ']'; break; } + strreset(); strescseq.type = c; term.esc |= ESC_STR; } @@ -2304,7 +2300,7 @@ tputc(Rune u) Glyph *gp; control = ISCONTROL(u); - if (u < 127 || !IS_SET(MODE_UTF8 | MODE_SIXEL)) { + if (u < 127 || !IS_SET(MODE_UTF8)) { c[0] = u; width = len = 1; } else { @@ -2325,23 +2321,11 @@ tputc(Rune u) if (term.esc & ESC_STR) { if (u == '\a' || u == 030 || u == 032 || u == 033 || ISCONTROLC1(u)) { - term.esc &= ~(ESC_START|ESC_STR|ESC_DCS); - if (IS_SET(MODE_SIXEL)) { - /* TODO: render sixel */; - term.mode &= ~MODE_SIXEL; - return; - } + term.esc &= ~(ESC_START|ESC_STR); term.esc |= ESC_STR_END; goto check_control_code; } - if (IS_SET(MODE_SIXEL)) { - /* TODO: implement sixel mode */ - return; - } - if (term.esc&ESC_DCS && strescseq.len == 0 && u == 'q') - term.mode |= MODE_SIXEL; - if (strescseq.len+len >= strescseq.siz) { /* * Here is a bug in terminals. If the user never sends @@ -2453,7 +2437,7 @@ twrite(const char *buf, int buflen, int show_ctrl) int n; for (n = 0; n < buflen; n += charsize) { - if (IS_SET(MODE_UTF8) && !IS_SET(MODE_SIXEL)) { + if (IS_SET(MODE_UTF8)) { /* process a complete utf8 char */ charsize = utf8decode(buf + n, &u, buflen - n); if (charsize == 0) From 81067c65ea4dd80e8eb34755a4f50a4a8c7df06b Mon Sep 17 00:00:00 2001 From: Hiltjo Posthuma Date: Wed, 17 Jun 2020 23:44:34 +0200 Subject: [PATCH 161/185] LICENSE: bump years --- LICENSE | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/LICENSE b/LICENSE index c356c39..d80eb47 100644 --- a/LICENSE +++ b/LICENSE @@ -1,6 +1,6 @@ MIT/X Consortium License -© 2014-2018 Hiltjo Posthuma +© 2014-2020 Hiltjo Posthuma © 2018 Devin J. Pohly © 2014-2017 Quentin Rameau © 2009-2012 Aurélien APTEL From b27a383a3acc7decf00e6e889fca265430b5d329 Mon Sep 17 00:00:00 2001 From: Hiltjo Posthuma Date: Wed, 17 Jun 2020 23:47:00 +0200 Subject: [PATCH 162/185] config.mk: use PKG_CONFIG in commented OpenBSD section --- config.mk | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/config.mk b/config.mk index beafc35..7b1c3db 100644 --- a/config.mk +++ b/config.mk @@ -28,8 +28,8 @@ 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` +# `$(PKG_CONFIG) --libs fontconfig` \ +# `$(PKG_CONFIG) --libs freetype2` # compiler and linker # CC = c99 From fa253f077f19b3220c7655b81bd91e52f4367803 Mon Sep 17 00:00:00 2001 From: Hiltjo Posthuma Date: Fri, 19 Jun 2020 11:27:17 +0200 Subject: [PATCH 163/185] bump version to 0.8.4 --- config.mk | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/config.mk b/config.mk index 7b1c3db..c070a4a 100644 --- a/config.mk +++ b/config.mk @@ -1,5 +1,5 @@ # st version -VERSION = 0.8.3 +VERSION = 0.8.4 # Customize below to fit your system From 28b4c822c5c0acec300fdf15c6e3ede9f5e2335d Mon Sep 17 00:00:00 2001 From: John Collis Date: Sun, 6 Sep 2020 17:53:41 +1200 Subject: [PATCH 164/185] ST: Add WM_ICON_NAME property support Also added _NET_WM_ICON_NAME. --- st.c | 9 +++++++++ win.h | 1 + x.c | 16 +++++++++++++++- 3 files changed, 25 insertions(+), 1 deletion(-) diff --git a/st.c b/st.c index 76b7e0d..ae7fa63 100644 --- a/st.c +++ b/st.c @@ -1844,6 +1844,7 @@ strhandle(void) { char *p = NULL, *dec; int j, narg, par; + static int winname = 0; term.esc &= ~(ESC_STR_END|ESC_STR); strparse(); @@ -1853,7 +1854,15 @@ strhandle(void) case ']': /* OSC -- Operating System Command */ switch (par) { case 0: + if (narg > 1) { + xsettitle(strescseq.args[1]); + xseticontitle(strescseq.args[1]); + } + return; case 1: + if (narg > 1) + xseticontitle(strescseq.args[1]); + return; case 2: if (narg > 1) xsettitle(strescseq.args[1]); diff --git a/win.h b/win.h index a6ef1b9..e6e4369 100644 --- a/win.h +++ b/win.h @@ -30,6 +30,7 @@ void xdrawline(Line, int, int, int); void xfinishdraw(void); void xloadcols(void); int xsetcolorname(int, const char *); +void xseticontitle(char *); void xsettitle(char *); int xsetcursor(int); void xsetmode(int, unsigned int); diff --git a/x.c b/x.c index 210f184..120e495 100644 --- a/x.c +++ b/x.c @@ -93,7 +93,7 @@ typedef struct { Window win; Drawable buf; GlyphFontSpec *specbuf; /* font spec buffer used for rendering */ - Atom xembed, wmdeletewin, netwmname, netwmpid; + Atom xembed, wmdeletewin, netwmname, netwmiconname, netwmpid; struct { XIM xim; XIC xic; @@ -1186,6 +1186,7 @@ xinit(int cols, int rows) 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); + xw.netwmiconname = XInternAtom(xw.dpy, "_NET_WM_ICON_NAME", False); XSetWMProtocols(xw.dpy, xw.win, &xw.wmdeletewin, 1); xw.netwmpid = XInternAtom(xw.dpy, "_NET_WM_PID", False); @@ -1579,6 +1580,19 @@ xsetenv(void) setenv("WINDOWID", buf, 1); } +void +xseticontitle(char *p) +{ + XTextProperty prop; + DEFAULT(p, opt_title); + + Xutf8TextListToTextProperty(xw.dpy, &p, 1, XUTF8StringStyle, + &prop); + XSetWMIconName(xw.dpy, xw.win, &prop); + XSetTextProperty(xw.dpy, xw.win, &prop, xw.netwmiconname); + XFree(prop.value); +} + void xsettitle(char *p) { From 4ef0cbd8b9371f37f7d02ef37b5378b879e6b8bf Mon Sep 17 00:00:00 2001 From: Hiltjo Posthuma Date: Sun, 18 Oct 2020 11:18:03 +0200 Subject: [PATCH 165/185] remove unused variable from previous patch --- st.c | 1 - 1 file changed, 1 deletion(-) diff --git a/st.c b/st.c index ae7fa63..abbbe4b 100644 --- a/st.c +++ b/st.c @@ -1844,7 +1844,6 @@ strhandle(void) { char *p = NULL, *dec; int j, narg, par; - static int winname = 0; term.esc &= ~(ESC_STR_END|ESC_STR); strparse(); From 9e68fdbcdb06dfa3d23fe3a7a7f7b59e40e1ea2f Mon Sep 17 00:00:00 2001 From: Hiltjo Posthuma Date: Fri, 19 Mar 2021 11:54:36 +0100 Subject: [PATCH 166/185] fix: correctly encode mouse buttons >= 8 in X10 and SGR mode These are typically mapped in X11 to the side-buttons (backward/forwards) on the mouse. A comparison of the button numbers in SGR mode (first field): st old: 0 1 2 64 65 66 67 68 69 70 st new (it is the same as xterm now): 0 1 2 64 65 66 67 128 129 130 A script to test and reproduce it, first argument is "h" (on) or "l" (off): #!/bin/sh printf '\x1b[?1000%s\x1b[?1006%s' "$1" "$1" for n in 1 2 3 4 5 6 7 8 9 10; do printf 'button %d\n' "$n" xdotool click "$n" printf '\n\n' done --- x.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/x.c b/x.c index 120e495..8bf998e 100644 --- a/x.c +++ b/x.c @@ -387,7 +387,9 @@ mousereport(XEvent *e) button = 3; } else { button -= Button1; - if (button >= 3) + if (button >= 7) + button += 128 - 7; + else if (button >= 3) button += 64 - 3; } if (e->xbutton.type == ButtonPress) { From 4536f46cfff50c66a115755def0155d8e246b02f Mon Sep 17 00:00:00 2001 From: "Markus F.X.J. Oberhumer" Date: Sun, 28 Mar 2021 21:16:59 +0200 Subject: [PATCH 167/185] Mild const-correctness improvements. Only touch a few things, the main focus is to improve code readability. --- st.c | 44 +++++++++++++++++++++++--------------------- st.h | 4 ++-- x.c | 6 +++--- 3 files changed, 28 insertions(+), 26 deletions(-) diff --git a/st.c b/st.c index abbbe4b..ebdf360 100644 --- a/st.c +++ b/st.c @@ -186,18 +186,18 @@ static void tputc(Rune); static void treset(void); static void tscrollup(int, int); static void tscrolldown(int, int); -static void tsetattr(int *, int); -static void tsetchar(Rune, Glyph *, int, int); +static void tsetattr(const int *, int); +static void tsetchar(Rune, const Glyph *, int, int); static void tsetdirt(int, int); static void tsetscroll(int, int); static void tswapscreen(void); -static void tsetmode(int, int, int *, int); +static void tsetmode(int, int, const int *, int); static int twrite(const char *, int, int); static void tfulldirt(void); static void tcontrolcode(uchar ); static void tdectest(char ); static void tdefutf8(char); -static int32_t tdefcolor(int *, int *, int); +static int32_t tdefcolor(const int *, int *, int); static void tdeftran(char); static void tstrsequence(uchar); @@ -226,10 +226,10 @@ static int iofd = 1; static int cmdfd; static pid_t pid; -static uchar utfbyte[UTF_SIZ + 1] = {0x80, 0, 0xC0, 0xE0, 0xF0}; -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}; +static const uchar utfbyte[UTF_SIZ + 1] = {0x80, 0, 0xC0, 0xE0, 0xF0}; +static const uchar utfmask[UTF_SIZ + 1] = {0xC0, 0x80, 0xE0, 0xF0, 0xF8}; +static const Rune utfmin[UTF_SIZ + 1] = { 0, 0, 0x80, 0x800, 0x10000}; +static const Rune utfmax[UTF_SIZ + 1] = {0x10FFFF, 0x7F, 0x7FF, 0xFFFF, 0x10FFFF}; ssize_t xwrite(int fd, const char *s, size_t len) @@ -269,12 +269,14 @@ xrealloc(void *p, size_t len) } char * -xstrdup(char *s) +xstrdup(const char *s) { - if ((s = strdup(s)) == NULL) + char *p; + + if ((p = strdup(s)) == NULL) die("strdup: %s\n", strerror(errno)); - return s; + return p; } size_t @@ -518,7 +520,7 @@ selsnap(int *x, int *y, int direction) { int newx, newy, xt, yt; int delim, prevdelim; - Glyph *gp, *prevgp; + const Glyph *gp, *prevgp; switch (sel.snap) { case SNAP_WORD: @@ -591,7 +593,7 @@ getsel(void) { char *str, *ptr; int y, bufsize, lastx, linelen; - Glyph *gp, *last; + const Glyph *gp, *last; if (sel.ob.x == -1) return NULL; @@ -758,7 +760,7 @@ stty(char **args) } int -ttynew(char *line, char *cmd, char *out, char **args) +ttynew(const char *line, char *cmd, const char *out, char **args) { int m, s; @@ -1186,9 +1188,9 @@ tmoveto(int x, int y) } void -tsetchar(Rune u, Glyph *attr, int x, int y) +tsetchar(Rune u, const Glyph *attr, int x, int y) { - static char *vt100_0[62] = { /* 0x41 - 0x7e */ + static const char *vt100_0[62] = { /* 0x41 - 0x7e */ "↑", "↓", "→", "←", "█", "▚", "☃", /* A - G */ 0, 0, 0, 0, 0, 0, 0, 0, /* H - O */ 0, 0, 0, 0, 0, 0, 0, 0, /* P - W */ @@ -1300,7 +1302,7 @@ tdeleteline(int n) } int32_t -tdefcolor(int *attr, int *npar, int l) +tdefcolor(const int *attr, int *npar, int l) { int32_t idx = -1; uint r, g, b; @@ -1350,7 +1352,7 @@ tdefcolor(int *attr, int *npar, int l) } void -tsetattr(int *attr, int l) +tsetattr(const int *attr, int l) { int i; int32_t idx; @@ -1468,9 +1470,9 @@ tsetscroll(int t, int b) } void -tsetmode(int priv, int set, int *args, int narg) +tsetmode(int priv, int set, const int *args, int narg) { - int alt, *lim; + int alt; const int *lim; for (lim = args + narg; args < lim; ++args) { if (priv) { @@ -2020,7 +2022,7 @@ void tdumpline(int n) { char buf[UTF_SIZ]; - Glyph *bp, *end; + const Glyph *bp, *end; bp = &term.line[n][0]; end = &bp[MIN(tlinelen(n), term.col) - 1]; diff --git a/st.h b/st.h index 3d351b6..fa2eddf 100644 --- a/st.h +++ b/st.h @@ -91,7 +91,7 @@ void tnew(int, int); void tresize(int, int); void tsetdirtattr(int); void ttyhangup(void); -int ttynew(char *, char *, char *, char **); +int ttynew(const char *, char *, const char *, char **); size_t ttyread(void); void ttyresize(int, int); void ttywrite(const char *, size_t, int); @@ -109,7 +109,7 @@ size_t utf8encode(Rune, char *); void *xmalloc(size_t); void *xrealloc(void *, size_t); -char *xstrdup(char *); +char *xstrdup(const char *); /* config.h globals */ extern char *utmp; diff --git a/x.c b/x.c index 8bf998e..7186040 100644 --- a/x.c +++ b/x.c @@ -156,7 +156,7 @@ static void xresize(int, int); static void xhints(void); static int xloadcolor(int, const char *, Color *); static int xloadfont(Font *, FcPattern *); -static void xloadfonts(char *, double); +static void xloadfonts(const char *, double); static void xunloadfont(Font *); static void xunloadfonts(void); static void xsetenv(void); @@ -952,7 +952,7 @@ xloadfont(Font *f, FcPattern *pattern) } void -xloadfonts(char *fontstr, double fontsize) +xloadfonts(const char *fontstr, double fontsize) { FcPattern *pattern; double fontval; @@ -960,7 +960,7 @@ xloadfonts(char *fontstr, double fontsize) if (fontstr[0] == '-') pattern = XftXlfdParse(fontstr, False, False); else - pattern = FcNameParse((FcChar8 *)fontstr); + pattern = FcNameParse((const FcChar8 *)fontstr); if (!pattern) die("can't open font %s\n", fontstr); From 2ec571a30c0c3b1a17f6b3631c80d573582f59a1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Petar=20Kapri=C5=A1?= Date: Sun, 18 Jul 2021 00:14:00 +0200 Subject: [PATCH 168/185] Add 14th bit to XK_SWITCH_MOD bitmask The bits of uint signal in an XKeyEvent which concern the key group (keyboard layout) are bits 13 and 14, as documented here: https://www.x.org/releases/X11R7.7/doc/libX11/XKB/xkblib.html#Groups_and_Shift_Levels In the older version, only bit 13 was marked as part of XK_SWITCH_MOD, this causes issues for users who have more than two keymaps. the 14th bit is not in ignoremod, key sequences are not caught by match(), if they switch to a third or fourth keyboard. --- x.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/x.c b/x.c index 7186040..248d505 100644 --- a/x.c +++ b/x.c @@ -48,7 +48,7 @@ typedef struct { /* X modifiers */ #define XK_ANY_MOD UINT_MAX #define XK_NO_MOD 0 -#define XK_SWITCH_MOD (1<<13) +#define XK_SWITCH_MOD (1<<13|1<<14) /* function definitions used in config.h */ static void clipcopy(const Arg *); From 1d3142da968da7f6f61f1c1708f39ca233eda150 Mon Sep 17 00:00:00 2001 From: Koichi Murase Date: Tue, 24 Aug 2021 06:25:05 +0900 Subject: [PATCH 169/185] fix a problem that the standard streams are unexpectedly closed In the current implementation, the slave PTY (assigned to the variable `s') is always closed after duplicating it to file descriptors of standard streams (0, 1, and 2). However, when the allocated slave PTY `s' is already one of 0, 1, or 2, this causes unexpected closing of a standard stream. The same problem occurs when the file descriptor of the master PTY (the variable `m') is one of 0, 1, or 2. In this patch, the original master PTY (m) is closed before it would be overwritten by duplicated slave PTYs. The original slave PTY (s) is closed only when it is not one of the stanrad streams. --- st.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/st.c b/st.c index ebdf360..a9338e1 100644 --- a/st.c +++ b/st.c @@ -793,14 +793,15 @@ ttynew(const char *line, char *cmd, const char *out, char **args) break; case 0: close(iofd); + close(m); setsid(); /* create a new process group */ dup2(s, 0); dup2(s, 1); dup2(s, 2); if (ioctl(s, TIOCSCTTY, NULL) < 0) die("ioctl TIOCSCTTY failed: %s\n", strerror(errno)); - close(s); - close(m); + if (s > 2) + close(s); #ifdef __OpenBSD__ if (pledge("stdio getpw proc exec", NULL) == -1) die("pledge\n"); From 2f6e597ed871cff91c627850d03152cae5f45779 Mon Sep 17 00:00:00 2001 From: Hiltjo Posthuma Date: Tue, 24 Aug 2021 13:44:35 +0200 Subject: [PATCH 170/185] fix possible rare crash when Xutf8TextPropertyToTextList fails from the XmbTextListToTextProperty(3) man page: "If insufficient memory is available for the new value string, the functions return XNoMemory. If the current locale is not supported, the functions return XLocaleNotSupported. In both of these error cases, the functions do not set text_prop_return." Reported by Steffen Nurpmeso , thanks! --- x.c | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/x.c b/x.c index 248d505..89786b8 100644 --- a/x.c +++ b/x.c @@ -1588,8 +1588,9 @@ xseticontitle(char *p) XTextProperty prop; DEFAULT(p, opt_title); - Xutf8TextListToTextProperty(xw.dpy, &p, 1, XUTF8StringStyle, - &prop); + if (Xutf8TextListToTextProperty(xw.dpy, &p, 1, XUTF8StringStyle, + &prop) != Success) + return; XSetWMIconName(xw.dpy, xw.win, &prop); XSetTextProperty(xw.dpy, xw.win, &prop, xw.netwmiconname); XFree(prop.value); @@ -1601,8 +1602,9 @@ xsettitle(char *p) XTextProperty prop; DEFAULT(p, opt_title); - Xutf8TextListToTextProperty(xw.dpy, &p, 1, XUTF8StringStyle, - &prop); + if (Xutf8TextListToTextProperty(xw.dpy, &p, 1, XUTF8StringStyle, + &prop) != Success) + return; XSetWMName(xw.dpy, xw.win, &prop); XSetTextProperty(xw.dpy, xw.win, &prop, xw.netwmname); XFree(prop.value); From 8e310303903792c010d03c046ba75f8b18f7d3a7 Mon Sep 17 00:00:00 2001 From: Raheman Vaiya Date: Sun, 26 Dec 2021 18:57:04 +0100 Subject: [PATCH 171/185] Add support for OSC color sequences --- config.def.h | 8 ++++-- st.c | 80 +++++++++++++++++++++++++++++++++++++++++++++++++++- st.h | 3 ++ x.c | 13 +++++++++ 4 files changed, 100 insertions(+), 4 deletions(-) diff --git a/config.def.h b/config.def.h index 6f05dce..91ab8ca 100644 --- a/config.def.h +++ b/config.def.h @@ -120,6 +120,8 @@ static const char *colorname[] = { /* more colors can be added after 255 to use with DefaultXX */ "#cccccc", "#555555", + "gray90", /* default foreground colour */ + "black", /* default background colour */ }; @@ -127,9 +129,9 @@ static const char *colorname[] = { * Default colors (colorname index) * foreground, background, cursor, reverse cursor */ -unsigned int defaultfg = 7; -unsigned int defaultbg = 0; -static unsigned int defaultcs = 256; +unsigned int defaultfg = 258; +unsigned int defaultbg = 259; +unsigned int defaultcs = 256; static unsigned int defaultrcs = 257; /* diff --git a/st.c b/st.c index a9338e1..781dbf2 100644 --- a/st.c +++ b/st.c @@ -1842,6 +1842,42 @@ csireset(void) memset(&csiescseq, 0, sizeof(csiescseq)); } +void +osc4_color_response(int num) +{ + int n; + char buf[32]; + unsigned char r, g, b; + + if (xgetcolor(num, &r, &g, &b)) { + fprintf(stderr, "erresc: failed to fetch osc4 color %d\n", num); + return; + } + + n = snprintf(buf, sizeof buf, "\033]4;%d;rgb:%02x%02x/%02x%02x/%02x%02x\007", + num, r, r, g, g, b, b); + + ttywrite(buf, n, 1); +} + +void +osc_color_response(int index, int num) +{ + int n; + char buf[32]; + unsigned char r, g, b; + + if (xgetcolor(index, &r, &g, &b)) { + fprintf(stderr, "erresc: failed to fetch osc color %d\n", index); + return; + } + + n = snprintf(buf, sizeof buf, "\033]%d;rgb:%02x%02x/%02x%02x/%02x%02x\007", + num, r, r, g, g, b, b); + + ttywrite(buf, n, 1); +} + void strhandle(void) { @@ -1880,6 +1916,45 @@ strhandle(void) } } return; + case 10: + if (narg < 2) + break; + + p = strescseq.args[1]; + + if (!strcmp(p, "?")) + osc_color_response(defaultfg, 10); + else if (xsetcolorname(defaultfg, p)) + fprintf(stderr, "erresc: invalid foreground color: %s\n", p); + else + redraw(); + break; + case 11: + if (narg < 2) + break; + + p = strescseq.args[1]; + + if (!strcmp(p, "?")) + osc_color_response(defaultbg, 11); + else if (xsetcolorname(defaultbg, p)) + fprintf(stderr, "erresc: invalid background color: %s\n", p); + else + redraw(); + break; + case 12: + if (narg < 2) + break; + + p = strescseq.args[1]; + + if (!strcmp(p, "?")) + osc_color_response(defaultcs, 12); + else if (xsetcolorname(defaultcs, p)) + fprintf(stderr, "erresc: invalid cursor color: %s\n", p); + else + redraw(); + break; case 4: /* color set */ if (narg < 3) break; @@ -1887,7 +1962,10 @@ strhandle(void) /* FALLTHROUGH */ case 104: /* color reset, here p = NULL */ j = (narg > 1) ? atoi(strescseq.args[1]) : -1; - if (xsetcolorname(j, p)) { + + if (!strcmp(p, "?")) + osc4_color_response(j); + else if (xsetcolorname(j, p)) { if (par == 104 && narg <= 1) return; /* color reset without parameter */ fprintf(stderr, "erresc: invalid color j=%d, p=%s\n", diff --git a/st.h b/st.h index fa2eddf..519b9bd 100644 --- a/st.h +++ b/st.h @@ -111,6 +111,8 @@ void *xmalloc(size_t); void *xrealloc(void *, size_t); char *xstrdup(const char *); +int xgetcolor(int x, unsigned char *r, unsigned char *g, unsigned char *b); + /* config.h globals */ extern char *utmp; extern char *scroll; @@ -123,3 +125,4 @@ extern char *termname; extern unsigned int tabspaces; extern unsigned int defaultfg; extern unsigned int defaultbg; +extern unsigned int defaultcs; diff --git a/x.c b/x.c index 89786b8..8a16faa 100644 --- a/x.c +++ b/x.c @@ -799,6 +799,19 @@ xloadcols(void) loaded = 1; } +int +xgetcolor(int x, unsigned char *r, unsigned char *g, unsigned char *b) +{ + if (!BETWEEN(x, 0, dc.collen)) + return 1; + + *r = dc.col[x].color.red >> 8; + *g = dc.col[x].color.green >> 8; + *b = dc.col[x].color.blue >> 8; + + return 0; +} + int xsetcolorname(int x, const char *name) { From 273db5ceaf392e68c2faf8f7dec14ea2e25e980d Mon Sep 17 00:00:00 2001 From: Hiltjo Posthuma Date: Sun, 26 Dec 2021 19:00:41 +0100 Subject: [PATCH 172/185] follow-up fix for OSC color sequences, return Otherwise the message "erresc: unknown str" is printed. --- st.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/st.c b/st.c index 781dbf2..6783c2b 100644 --- a/st.c +++ b/st.c @@ -1928,7 +1928,7 @@ strhandle(void) fprintf(stderr, "erresc: invalid foreground color: %s\n", p); else redraw(); - break; + return; case 11: if (narg < 2) break; @@ -1941,7 +1941,7 @@ strhandle(void) fprintf(stderr, "erresc: invalid background color: %s\n", p); else redraw(); - break; + return; case 12: if (narg < 2) break; @@ -1954,7 +1954,7 @@ strhandle(void) fprintf(stderr, "erresc: invalid cursor color: %s\n", p); else redraw(); - break; + return; case 4: /* color set */ if (narg < 3) break; From a0467c802d4f86ed162486e3453dd61181423902 Mon Sep 17 00:00:00 2001 From: Jochen Sprickerhof Date: Mon, 27 Dec 2021 11:41:42 +0100 Subject: [PATCH 173/185] Fix null pointer access in strhandle According to the spec the argument is optional for 104, so p can be NULL as can be tested with printf '\x1b]104\x07'. This is a regression of 8e31030. --- st.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/st.c b/st.c index 6783c2b..de2dd0e 100644 --- a/st.c +++ b/st.c @@ -1960,10 +1960,10 @@ strhandle(void) break; p = strescseq.args[2]; /* FALLTHROUGH */ - case 104: /* color reset, here p = NULL */ + case 104: /* color reset */ j = (narg > 1) ? atoi(strescseq.args[1]) : -1; - if (!strcmp(p, "?")) + if (p && !strcmp(p, "?")) osc4_color_response(j); else if (xsetcolorname(j, p)) { if (par == 104 && narg <= 1) From 65f1dc428315ae9d7f362e10c668557c1379e7af Mon Sep 17 00:00:00 2001 From: jamin Date: Wed, 29 Dec 2021 09:07:17 -0800 Subject: [PATCH 174/185] Fix overtyping wide characters. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Overtyping the first half of a wide character with the second half of a wide character results in display garbage. This is because the trailing dummy is not cleaned up. i.e. ATTR_WIDE, ATTR_WDUMMY, ATTR_WDUMMY Here is a short script for demonstrating the behavior: #!/bin/sh alias printf=/usr/bin/printf printf こんにちは!; sleep 2 printf '\x1b[5D'; sleep 2 printf へ; sleep 2 printf ' '; sleep 2 echo --- st.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/st.c b/st.c index de2dd0e..51049ba 100644 --- a/st.c +++ b/st.c @@ -2507,6 +2507,10 @@ check_control_code: if (width == 2) { gp->mode |= ATTR_WIDE; if (term.c.x+1 < term.col) { + if (gp[1].mode == ATTR_WIDE && term.c.x+2 < term.col) { + gp[2].u = ' '; + gp[2].mode &= ~ATTR_WDUMMY; + } gp[1].u = '\0'; gp[1].mode = ATTR_WDUMMY; } From 7e1c68f25d9f08687a94eeef8d7f8ffd0d14b911 Mon Sep 17 00:00:00 2001 From: Hiltjo Posthuma Date: Sun, 2 Jan 2022 12:15:45 +0100 Subject: [PATCH 175/185] FAQ: fix a typo, patch -> path --- FAQ | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/FAQ b/FAQ index 0f9609d..969b195 100644 --- a/FAQ +++ b/FAQ @@ -29,8 +29,8 @@ you can manually run `tic -sx st.info`. ## I would like to have utmp and/or scroll functionality by default -You can add the absolute patch of both programs in your config.h -file. You only have to modify the value of utmp and scroll variables. +You can add the absolute path of both programs in your config.h file. You only +have to modify the value of utmp and scroll variables. ## Why doesn't the Del key work in some programs? From 7fb0c0cc681f36be2ad12091ef93a41671f32738 Mon Sep 17 00:00:00 2001 From: Hiltjo Posthuma Date: Fri, 7 Jan 2022 12:39:57 +0100 Subject: [PATCH 176/185] bump version to 0.8.5 --- config.mk | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/config.mk b/config.mk index c070a4a..4c4c5d5 100644 --- a/config.mk +++ b/config.mk @@ -1,5 +1,5 @@ # st version -VERSION = 0.8.4 +VERSION = 0.8.5 # Customize below to fit your system From ea7cd7b62fdfa6a1fbd882d1565d557577f2cf32 Mon Sep 17 00:00:00 2001 From: robert Date: Sat, 8 Jan 2022 11:40:34 -0800 Subject: [PATCH 177/185] Fix mousereport This patch replaces the previous one I sent. The following changes are made in this patch: - Fix tracking of pressed buttons. Previously, pressing two buttons and then releasing one would make st think no buttons are pressed, which in particular broke MODE_MOUSEMOTION. - Always send the lowest-numbered pressed button on motion events; when no button is pressed for a motion event in MODE_MOUSEMANY, then send a release. This matches the behaviour of xterm. (Previously, st sent the most recently pressed button in the motion report.) - Remove UB (?) access to potentially inactive struct member e->xbutton.button of XEvent union. - Fix (unlikely) possibility of overflow for large button numbers. The one discrepancy I found between st and xterm is that xterm sometimes encodes buttons with large numbers (>5) strangely. E.g., xterm reports presses of buttons 8 and 9 as releases, whereas st properly (?) encodes them as presses. --- x.c | 86 ++++++++++++++++++++++++++++++++++++------------------------- 1 file changed, 51 insertions(+), 35 deletions(-) diff --git a/x.c b/x.c index 8a16faa..5f4ea73 100644 --- a/x.c +++ b/x.c @@ -252,7 +252,7 @@ static char *opt_line = NULL; static char *opt_name = NULL; static char *opt_title = NULL; -static int oldbutton = 3; /* button event on startup: 3 = release */ +static uint buttons; /* bit field of pressed buttons */ void clipcopy(const Arg *dummy) @@ -364,61 +364,68 @@ mousesel(XEvent *e, int done) void mousereport(XEvent *e) { - int len, x = evcol(e), y = evrow(e), - button = e->xbutton.button, state = e->xbutton.state; + int len, btn, code; + int x = evcol(e), y = evrow(e); + int state = e->xbutton.state; char buf[40]; static int ox, oy; - /* from urxvt */ - if (e->xbutton.type == MotionNotify) { + if (e->type == MotionNotify) { if (x == ox && y == oy) return; if (!IS_SET(MODE_MOUSEMOTION) && !IS_SET(MODE_MOUSEMANY)) return; - /* MOUSE_MOTION: no reporting if no button is pressed */ - if (IS_SET(MODE_MOUSEMOTION) && oldbutton == 3) + /* MODE_MOUSEMOTION: no reporting if no button is pressed */ + if (IS_SET(MODE_MOUSEMOTION) && buttons == 0) return; - - button = oldbutton + 32; - ox = x; - oy = y; + /* Set btn to lowest-numbered pressed button, or 12 if no + * buttons are pressed. */ + for (btn = 1; btn <= 11 && !(buttons & (1<<(btn-1))); btn++) + ; + code = 32; } else { - if (!IS_SET(MODE_MOUSESGR) && e->xbutton.type == ButtonRelease) { - button = 3; - } else { - button -= Button1; - if (button >= 7) - button += 128 - 7; - else if (button >= 3) - button += 64 - 3; - } - if (e->xbutton.type == ButtonPress) { - oldbutton = button; - ox = x; - oy = y; - } else if (e->xbutton.type == ButtonRelease) { - oldbutton = 3; + btn = e->xbutton.button; + /* Only buttons 1 through 11 can be encoded */ + if (btn < 1 || btn > 11) + return; + if (e->type == ButtonRelease) { /* MODE_MOUSEX10: no button release reporting */ if (IS_SET(MODE_MOUSEX10)) return; - if (button == 64 || button == 65) + /* Don't send release events for the scroll wheel */ + if (btn == 4 || btn == 5) return; } + code = 0; } + ox = x; + oy = y; + + /* Encode btn into code. If no button is pressed for a motion event in + * MODE_MOUSEMANY, then encode it as a release. */ + if ((!IS_SET(MODE_MOUSESGR) && e->type == ButtonRelease) || btn == 12) + code += 3; + else if (btn >= 8) + code += 128 + btn - 8; + else if (btn >= 4) + code += 64 + btn - 4; + else + code += btn - 1; + if (!IS_SET(MODE_MOUSEX10)) { - button += ((state & ShiftMask ) ? 4 : 0) - + ((state & Mod4Mask ) ? 8 : 0) - + ((state & ControlMask) ? 16 : 0); + code += ((state & ShiftMask ) ? 4 : 0) + + ((state & Mod4Mask ) ? 8 : 0) + + ((state & ControlMask) ? 16 : 0); } if (IS_SET(MODE_MOUSESGR)) { len = snprintf(buf, sizeof(buf), "\033[<%d;%d;%d%c", - button, x+1, y+1, - e->xbutton.type == ButtonRelease ? 'm' : 'M'); + code, x+1, y+1, + e->type == ButtonRelease ? 'm' : 'M'); } else if (x < 223 && y < 223) { len = snprintf(buf, sizeof(buf), "\033[M%c%c%c", - 32+button, 32+x+1, 32+y+1); + 32+code, 32+x+1, 32+y+1); } else { return; } @@ -461,9 +468,13 @@ mouseaction(XEvent *e, uint release) void bpress(XEvent *e) { + int btn = e->xbutton.button; struct timespec now; int snap; + if (1 <= btn && btn <= 11) + buttons |= 1 << (btn-1); + if (IS_SET(MODE_MOUSE) && !(e->xbutton.state & forcemousemod)) { mousereport(e); return; @@ -472,7 +483,7 @@ bpress(XEvent *e) if (mouseaction(e, 0)) return; - if (e->xbutton.button == Button1) { + if (btn == Button1) { /* * If the user clicks below predefined timeouts specific * snapping behaviour is exposed. @@ -686,6 +697,11 @@ xsetsel(char *str) void brelease(XEvent *e) { + int btn = e->xbutton.button; + + if (1 <= btn && btn <= 11) + buttons &= ~(1 << (btn-1)); + if (IS_SET(MODE_MOUSE) && !(e->xbutton.state & forcemousemod)) { mousereport(e); return; @@ -693,7 +709,7 @@ brelease(XEvent *e) if (mouseaction(e, 1)) return; - if (e->xbutton.button == Button1) + if (btn == Button1) mousesel(e, 1); } From b1d97fec47b0d6633addb848131388ec40154ebc Mon Sep 17 00:00:00 2001 From: Hiltjo Posthuma Date: Mon, 10 Jan 2022 17:11:17 +0100 Subject: [PATCH 178/185] LICENSE: bump year --- LICENSE | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/LICENSE b/LICENSE index d80eb47..3cbf420 100644 --- a/LICENSE +++ b/LICENSE @@ -1,6 +1,6 @@ MIT/X Consortium License -© 2014-2020 Hiltjo Posthuma +© 2014-2022 Hiltjo Posthuma © 2018 Devin J. Pohly © 2014-2017 Quentin Rameau © 2009-2012 Aurélien APTEL From 2c5edf28ec851907305d73c6218ce75d39f1767f Mon Sep 17 00:00:00 2001 From: Hiltjo Posthuma Date: Wed, 12 Jan 2022 09:44:27 +0100 Subject: [PATCH 179/185] X10/SGR mouse: use alt as meta key instead of super/windows key --- x.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/x.c b/x.c index 5f4ea73..cd96575 100644 --- a/x.c +++ b/x.c @@ -415,7 +415,7 @@ mousereport(XEvent *e) if (!IS_SET(MODE_MOUSEX10)) { code += ((state & ShiftMask ) ? 4 : 0) - + ((state & Mod4Mask ) ? 8 : 0) + + ((state & Mod1Mask ) ? 8 : 0) /* meta key: alt */ + ((state & ControlMask) ? 16 : 0); } From e823e2308f2a99023032a3966ebb7036a31d305f Mon Sep 17 00:00:00 2001 From: Santtu Lakkala Date: Thu, 17 Feb 2022 16:00:47 +0200 Subject: [PATCH 180/185] Delay redrawals on palette changes Build on auto-sync and only mark window dirty on palette changes and let the event handler do the actual draw. --- st.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/st.c b/st.c index 51049ba..c71fa06 100644 --- a/st.c +++ b/st.c @@ -1927,7 +1927,7 @@ strhandle(void) else if (xsetcolorname(defaultfg, p)) fprintf(stderr, "erresc: invalid foreground color: %s\n", p); else - redraw(); + tfulldirt(); return; case 11: if (narg < 2) @@ -1940,7 +1940,7 @@ strhandle(void) else if (xsetcolorname(defaultbg, p)) fprintf(stderr, "erresc: invalid background color: %s\n", p); else - redraw(); + tfulldirt(); return; case 12: if (narg < 2) @@ -1953,7 +1953,7 @@ strhandle(void) else if (xsetcolorname(defaultcs, p)) fprintf(stderr, "erresc: invalid cursor color: %s\n", p); else - redraw(); + tfulldirt(); return; case 4: /* color set */ if (narg < 3) @@ -1975,7 +1975,7 @@ strhandle(void) * TODO if defaultbg color is changed, borders * are dirty */ - redraw(); + tfulldirt(); } return; } From 2aefa348baf4b702fdce98eb105bcba175d8283f Mon Sep 17 00:00:00 2001 From: Zacchary Dempsey-Plante Date: Sun, 13 Mar 2022 10:44:08 +0100 Subject: [PATCH 181/185] make underlines and strikethroughs respect `chscale` --- x.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/x.c b/x.c index cd96575..2a3bd38 100644 --- a/x.c +++ b/x.c @@ -1493,12 +1493,12 @@ xdrawglyphfontspecs(const XftGlyphFontSpec *specs, Glyph base, int len, int x, i /* Render underline and strikethrough. */ if (base.mode & ATTR_UNDERLINE) { - XftDrawRect(xw.draw, fg, winx, winy + dc.font.ascent + 1, + XftDrawRect(xw.draw, fg, winx, winy + dc.font.ascent * chscale + 1, width, 1); } if (base.mode & ATTR_STRUCK) { - XftDrawRect(xw.draw, fg, winx, winy + 2 * dc.font.ascent / 3, + XftDrawRect(xw.draw, fg, winx, winy + 2 * dc.font.ascent * chscale / 3, width, 1); } From af3bb68add1c40d19d0dee382009e21b0870a38f Mon Sep 17 00:00:00 2001 From: NRK Date: Fri, 18 Mar 2022 16:20:54 +0600 Subject: [PATCH 182/185] avoid potential UB when using isprint() all the ctype.h functions' argument must be representable as an unsigned char or as EOF, otherwise the behavior is undefined. --- st.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/st.c b/st.c index c71fa06..1307fdf 100644 --- a/st.c +++ b/st.c @@ -367,7 +367,7 @@ static const char base64_digits[] = { char base64dec_getc(const char **src) { - while (**src && !isprint(**src)) + while (**src && !isprint((unsigned char)**src)) (*src)++; return **src ? *((*src)++) : '='; /* emulate padding if string ends */ } From ef0551932fb162f907b40185d2f48c3b497708ee Mon Sep 17 00:00:00 2001 From: NRK Date: Fri, 18 Mar 2022 17:03:34 +0600 Subject: [PATCH 183/185] base64_digits: reduce scope, implicit zero, +1 size the array is not accessed outside of base64dec() so it makes sense to limit it's scope to the related function. the static-storage duration of the array is kept intact. this also removes unnecessary explicit zeroing from the start and end of the array. anything that wasn't explicitly zero-ed will now be implicitly zero-ed instead. the validity of the new array can be easily confirmed via running this trivial loop: for (int i = 0; i < 255; ++i) assert(base64_digits[i] == base64_digits_old[i]); lastly, as pointed out by Roberto, the array needs to have 256 elements in order to able access it as any unsigned char as an index; the previous array had 255. however, this array will only be accessed at indexes which are isprint() || '=' (see `base64dec_getc()`), so reducing the size of the array to the highest printable ascii char (127 AFAIK) + 1 might also be a valid strategy. --- st.c | 22 +++++++--------------- 1 file changed, 7 insertions(+), 15 deletions(-) diff --git a/st.c b/st.c index 1307fdf..f43cfd3 100644 --- a/st.c +++ b/st.c @@ -349,21 +349,6 @@ utf8validate(Rune *u, size_t i) return i; } -static const char base64_digits[] = { - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 62, 0, 0, 0, - 63, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 0, 0, 0, -1, 0, 0, 0, 0, 1, - 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, - 22, 23, 24, 25, 0, 0, 0, 0, 0, 0, 26, 27, 28, 29, 30, 31, 32, 33, 34, - 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 -}; - char base64dec_getc(const char **src) { @@ -377,6 +362,13 @@ base64dec(const char *src) { size_t in_len = strlen(src); char *result, *dst; + static const char base64_digits[256] = { + [43] = 62, 0, 0, 0, 63, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, + 0, 0, 0, -1, 0, 0, 0, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, + 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 0, 0, 0, 0, + 0, 0, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, + 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51 + }; if (in_len % 4) in_len += 4 - (in_len % 4); From 8629d9a1da72cc18568a8f146307b0e939b77ebf Mon Sep 17 00:00:00 2001 From: NRK Date: Fri, 7 Jan 2022 23:21:04 +0600 Subject: [PATCH 184/185] code-golfing: cleanup osc color related code * adds missing function prototype * move xgetcolor() prototype to win.h (that's where all the other x.c func prototype seems to be declared at) * check for snprintf error/truncation * reduces code duplication for osc 10/11/12 * unify osc_color_response() and osc4_color_response() into a single function the latter two was suggested by Quentin Rameau in his patch review on the hackers list. --- st.c | 88 +++++++++++++++++++++-------------------------------------- st.h | 2 -- win.h | 1 + 3 files changed, 32 insertions(+), 59 deletions(-) diff --git a/st.c b/st.c index f43cfd3..6ba467d 100644 --- a/st.c +++ b/st.c @@ -161,6 +161,7 @@ static void csidump(void); static void csihandle(void); static void csiparse(void); static void csireset(void); +static void osc_color_response(int, int, int); static int eschandle(uchar); static void strdump(void); static void strhandle(void); @@ -1835,39 +1836,28 @@ csireset(void) } void -osc4_color_response(int num) +osc_color_response(int num, int index, int is_osc4) { int n; char buf[32]; unsigned char r, g, b; - if (xgetcolor(num, &r, &g, &b)) { - fprintf(stderr, "erresc: failed to fetch osc4 color %d\n", num); + if (xgetcolor(is_osc4 ? num : index, &r, &g, &b)) { + fprintf(stderr, "erresc: failed to fetch %s color %d\n", + is_osc4 ? "osc4" : "osc", + is_osc4 ? num : index); return; } - n = snprintf(buf, sizeof buf, "\033]4;%d;rgb:%02x%02x/%02x%02x/%02x%02x\007", - num, r, r, g, g, b, b); - - ttywrite(buf, n, 1); -} - -void -osc_color_response(int index, int num) -{ - int n; - char buf[32]; - unsigned char r, g, b; - - if (xgetcolor(index, &r, &g, &b)) { - fprintf(stderr, "erresc: failed to fetch osc color %d\n", index); - return; + n = snprintf(buf, sizeof buf, "\033]%s%d;rgb:%02x%02x/%02x%02x/%02x%02x\007", + is_osc4 ? "4;" : "", num, r, r, g, g, b, b); + if (n < 0 || n >= sizeof(buf)) { + fprintf(stderr, "error: %s while printing %s response\n", + n < 0 ? "snprintf failed" : "truncation occurred", + is_osc4 ? "osc4" : "osc"); + } else { + ttywrite(buf, n, 1); } - - n = snprintf(buf, sizeof buf, "\033]%d;rgb:%02x%02x/%02x%02x/%02x%02x\007", - num, r, r, g, g, b, b); - - ttywrite(buf, n, 1); } void @@ -1875,6 +1865,11 @@ strhandle(void) { char *p = NULL, *dec; int j, narg, par; + const struct { int idx; char *str; } osc_table[] = { + { defaultfg, "foreground" }, + { defaultbg, "background" }, + { defaultcs, "cursor" } + }; term.esc &= ~(ESC_STR_END|ESC_STR); strparse(); @@ -1909,43 +1904,22 @@ strhandle(void) } return; case 10: - if (narg < 2) - break; - - p = strescseq.args[1]; - - if (!strcmp(p, "?")) - osc_color_response(defaultfg, 10); - else if (xsetcolorname(defaultfg, p)) - fprintf(stderr, "erresc: invalid foreground color: %s\n", p); - else - tfulldirt(); - return; case 11: - if (narg < 2) - break; - - p = strescseq.args[1]; - - if (!strcmp(p, "?")) - osc_color_response(defaultbg, 11); - else if (xsetcolorname(defaultbg, p)) - fprintf(stderr, "erresc: invalid background color: %s\n", p); - else - tfulldirt(); - return; case 12: if (narg < 2) break; - p = strescseq.args[1]; + if ((j = par - 10) < 0 || j >= LEN(osc_table)) + break; /* shouldn't be possible */ - if (!strcmp(p, "?")) - osc_color_response(defaultcs, 12); - else if (xsetcolorname(defaultcs, p)) - fprintf(stderr, "erresc: invalid cursor color: %s\n", p); - else + if (!strcmp(p, "?")) { + osc_color_response(par, osc_table[j].idx, 0); + } else if (xsetcolorname(osc_table[j].idx, p)) { + fprintf(stderr, "erresc: invalid %s color: %s\n", + osc_table[j].str, p); + } else { tfulldirt(); + } return; case 4: /* color set */ if (narg < 3) @@ -1955,9 +1929,9 @@ strhandle(void) case 104: /* color reset */ j = (narg > 1) ? atoi(strescseq.args[1]) : -1; - if (p && !strcmp(p, "?")) - osc4_color_response(j); - else if (xsetcolorname(j, p)) { + if (p && !strcmp(p, "?")) { + osc_color_response(j, 0, 1); + } else if (xsetcolorname(j, p)) { if (par == 104 && narg <= 1) return; /* color reset without parameter */ fprintf(stderr, "erresc: invalid color j=%d, p=%s\n", diff --git a/st.h b/st.h index 519b9bd..fd3b0d8 100644 --- a/st.h +++ b/st.h @@ -111,8 +111,6 @@ void *xmalloc(size_t); void *xrealloc(void *, size_t); char *xstrdup(const char *); -int xgetcolor(int x, unsigned char *r, unsigned char *g, unsigned char *b); - /* config.h globals */ extern char *utmp; extern char *scroll; diff --git a/win.h b/win.h index e6e4369..6de960d 100644 --- a/win.h +++ b/win.h @@ -30,6 +30,7 @@ void xdrawline(Line, int, int, int); void xfinishdraw(void); void xloadcols(void); int xsetcolorname(int, const char *); +int xgetcolor(int, unsigned char *, unsigned char *, unsigned char *); void xseticontitle(char *); void xsettitle(char *); int xsetcursor(int); From baa9357e96d2478baa52a3301e70ac80a229b726 Mon Sep 17 00:00:00 2001 From: Hiltjo Posthuma Date: Sun, 1 May 2022 18:38:40 +0200 Subject: [PATCH 185/185] Makefile: add manual path for OpenBSD --- config.mk | 1 + 1 file changed, 1 insertion(+) diff --git a/config.mk b/config.mk index 4c4c5d5..ddf278a 100644 --- a/config.mk +++ b/config.mk @@ -30,6 +30,7 @@ STLDFLAGS = $(LIBS) $(LDFLAGS) #LIBS = -L$(X11LIB) -lm -lX11 -lutil -lXft \ # `$(PKG_CONFIG) --libs fontconfig` \ # `$(PKG_CONFIG) --libs freetype2` +#MANPREFIX = ${PREFIX}/man # compiler and linker # CC = c99