#include <sys/time.h>
#include <sys/types.h>
#include <sys/wait.h>
+#include <termios.h>
#include <time.h>
#include <unistd.h>
#include <libgen.h>
#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 DEFAULT(a, b) (a) = (a) ? (a) : (b)
-#define BETWEEN(x, a, b) ((a) <= (x) && (x) <= (b))
-#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)
+#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 DEFAULT(a, b) (a) = (a) ? (a) : (b)
+#define BETWEEN(x, a, b) ((a) <= (x) && (x) <= (b))
+#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)
enum glyph_attribute {
uint b;
uint mask;
char *s;
-} Mousekey;
+} MouseShortcut;
typedef struct {
KeySym k;
static void printsel(const Arg *);
static void printscreen(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 tdeftran(char);
static inline int match(uint, uint);
static void ttynew(void);
-static void ttyread(void);
+static size_t ttyread(void);
static void ttyresize(void);
static void ttysend(char *, size_t);
static void ttywrite(const char *, size_t);
ssize_t
xwrite(int fd, const char *s, size_t len)
{
- size_t aux = len, r;
+ size_t aux = len;
+ ssize_t r;
while (len > 0) {
r = write(fd, s, len);
len -= r;
s += r;
}
+
return aux;
}
return 0;
*u = udecoded;
utf8validate(u, len);
+
return len;
}
for (*i = 0; *i < LEN(utfmask); ++(*i))
if (((uchar)c & utfmask[*i]) == utfbyte[*i])
return (uchar)c & ~utfmask[*i];
+
return 0;
}
len = utf8validate(&u, 0);
if (len > UTF_SIZ)
return 0;
+
for (i = len - 1; i != 0; --i) {
c[i] = utf8encodebyte(u, 0);
u >>= 6;
}
c[0] = utf8encodebyte(u, len);
+
return len;
}
if (r == u)
return &(s[i]);
}
+
return NULL;
}
*u = UTF_INVALID;
for (i = 1; *u > utfmax[i]; ++i)
;
+
return i;
}
void
selinit(void)
{
- memset(&sel.tclick1, 0, sizeof(sel.tclick1));
- memset(&sel.tclick2, 0, sizeof(sel.tclick2));
+ 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;
bpress(XEvent *e)
{
struct timespec now;
- Mousekey *mk;
+ MouseShortcut *ms;
if (IS_SET(MODE_MOUSE) && !(e->xbutton.state & forceselmod)) {
mousereport(e);
return;
}
- for (mk = mshortcuts; mk < mshortcuts + LEN(mshortcuts); mk++) {
- if (e->xbutton.button == mk->b
- && match(mk->mask, e->xbutton.state)) {
- ttysend(mk->s, strlen(mk->s));
+ 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;
}
}
/* append every set & selected glyph to the selection */
for (y = sel.nb.y; y <= sel.ne.y; y++) {
- linelen = tlinelen(y);
+ if ((linelen = tlinelen(y)) == 0) {
+ *ptr++ = '\n';
+ continue;
+ }
if (sel.type == SEL_RECTANGULAR) {
gp = &term.line[y][sel.nb.x];
*repl++ = '\r';
}
- if (IS_SET(MODE_BRCKTPASTE))
+ if (IS_SET(MODE_BRCKTPASTE) && ofs == 0)
ttywrite("\033[200~", 6);
ttysend((char *)data, nitems * format / 8);
- if (IS_SET(MODE_BRCKTPASTE))
+ if (IS_SET(MODE_BRCKTPASTE) && rem == 0)
ttywrite("\033[201~", 6);
XFree(data);
/* number of 32-bit chunks returned */
die("who are you?\n");
}
- if (!(sh = getenv("SHELL"))) {
+ if ((sh = getenv("SHELL")) == NULL)
sh = (pw->pw_shell[0]) ? pw->pw_shell : shell;
- }
if (opt_cmd)
prog = opt_cmd[0];
if (opt_line) {
if ((cmdfd = open(opt_line, O_RDWR)) < 0)
die("open line failed: %s\n", strerror(errno));
- close(0);
- dup(cmdfd);
+ dup2(cmdfd, 0);
stty();
return;
}
}
}
-void
+size_t
ttyread(void)
{
static char buf[BUFSIZ];
/* keep any uncomplete utf8 char for the next call */
memmove(buf, ptr, buflen);
+
+ return ret;
}
void
ttywrite(const char *s, size_t n)
{
- fd_set wfd;
- struct timespec tv;
+ fd_set wfd, rfd;
ssize_t r;
+ size_t lim = 256;
/*
* Remember that we are using a pty, which might be a modem line.
*/
while (n > 0) {
FD_ZERO(&wfd);
+ FD_ZERO(&rfd);
FD_SET(cmdfd, &wfd);
- tv.tv_sec = 0;
- tv.tv_nsec = 0;
+ FD_SET(cmdfd, &rfd);
/* Check if we can write. */
- if (pselect(cmdfd+1, NULL, &wfd, NULL, &tv, NULL) < 0) {
+ if (pselect(cmdfd+1, &rfd, &wfd, NULL, NULL, NULL) < 0) {
if (errno == EINTR)
continue;
die("select failed: %s\n", strerror(errno));
}
- if(!FD_ISSET(cmdfd, &wfd)) {
- /* No, then free some buffer space. */
- ttyread();
- } else {
+ if (FD_ISSET(cmdfd, &rfd))
+ lim = ttyread();
+ if (FD_ISSET(cmdfd, &wfd)) {
/*
- * Only write 256 bytes at maximum. This seems to be a
- * reasonable value for a serial line. Bigger values
- * might clog the I/O.
+ * Only write the bytes written by ttywrite() or the
+ * default of 256. This seems to be a reasonable value
+ * for a serial line. Bigger values might clog the I/O.
*/
- r = write(cmdfd, s, (n < 256)? n : 256);
- if (r < 0) {
- die("write error on tty: %s\n",
- strerror(errno));
- }
+ if ((r = write(cmdfd, s, (n < lim)? n : lim)) < 0)
+ goto write_error;
if (r < n) {
/*
* We weren't able to write out everything.
* This means the buffer is getting full
* again. Empty it.
*/
- ttyread();
+ if (n < lim)
+ lim = ttyread();
n -= r;
s += r;
} else {
}
}
}
+ return;
+
+write_error:
+ die("write error on tty: %s\n", strerror(errno));
}
void
break;
case ' ':
switch (csiescseq.mode[1]) {
- case 'q': /* DECSCUSR -- Set Cursor Style */
- DEFAULT(csiescseq.arg[0], 1);
- if (!BETWEEN(csiescseq.arg[0], 0, 6)) {
- goto unknown;
- }
- xw.cursor = csiescseq.arg[0];
- break;
- default:
+ case 'q': /* DECSCUSR -- Set Cursor Style */
+ DEFAULT(csiescseq.arg[0], 1);
+ if (!BETWEEN(csiescseq.arg[0], 0, 6)) {
goto unknown;
+ }
+ xw.cursor = csiescseq.arg[0];
+ break;
+ default:
+ goto unknown;
}
break;
}
memset(&strescseq, 0, sizeof(strescseq));
}
+void
+sendbreak(const Arg *arg)
+{
+ if (tcsendbreak(cmdfd, 0))
+ perror("Error sending break");
+}
+
void
tprinter(char *s, size_t len)
{
case '\023': /* XOFF (IGNORED) */
case 0177: /* DEL (IGNORED) */
return;
+ case 0x80: /* TODO: PAD */
+ case 0x81: /* TODO: HOP */
+ case 0x82: /* TODO: BPH */
+ case 0x83: /* TODO: NBH */
case 0x84: /* TODO: IND */
break;
case 0x85: /* NEL -- Next line */
tnewline(1); /* always go to first col */
break;
+ case 0x86: /* TODO: SSA */
+ case 0x87: /* TODO: ESA */
+ break;
case 0x88: /* HTS -- Horizontal tab stop */
term.tabs[term.c.x] = 1;
break;
+ case 0x89: /* TODO: HTJ */
+ case 0x8a: /* TODO: VTS */
+ case 0x8b: /* TODO: PLD */
+ case 0x8c: /* TODO: PLU */
case 0x8d: /* TODO: RI */
case 0x8e: /* TODO: SS2 */
case 0x8f: /* TODO: SS3 */
+ case 0x91: /* TODO: PU1 */
+ case 0x92: /* TODO: PU2 */
+ case 0x93: /* TODO: STS */
+ case 0x94: /* TODO: CCH */
+ case 0x95: /* TODO: MW */
+ case 0x96: /* TODO: SPA */
+ case 0x97: /* TODO: EPA */
case 0x98: /* TODO: SOS */
+ case 0x99: /* TODO: SGCI */
break;
case 0x9a: /* DECID -- Identify Terminal */
ttywrite(vtiden, sizeof(vtiden) - 1);
case 0x9c: /* TODO: ST */
break;
case 0x90: /* DCS -- Device Control String */
- case 0x9f: /* APC -- Application Program Command */
- case 0x9e: /* PM -- Privacy Message */
case 0x9d: /* OSC -- Operating System Command */
+ case 0x9e: /* PM -- Privacy Message */
+ case 0x9f: /* APC -- Application Program Command */
tstrsequence(ascii);
return;
}
int width, len;
Glyph *gp;
+ control = ISCONTROL(u);
len = utf8encode(u, c);
- if ((width = wcwidth(u)) == -1) {
+ if (!control && (width = wcwidth(u)) == -1) {
memcpy(c, "\357\277\275", 4); /* UTF_INVALID */
width = 1;
}
if (IS_SET(MODE_PRINT))
tprinter(c, len);
- control = ISCONTROL(u);
/*
* STR sequence must be checked before anything else
} else
name = colorname[i];
}
+
return XftColorAllocName(xw.dpy, xw.vis, xw.cmap, name, ncolor);
}
XftColorFree(xw.dpy, xw.vis, xw.cmap, &dc.col[x]);
dc.col[x] = ncolor;
+
return 0;
}
case YNegative:
return SouthWestGravity;
}
+
return SouthEastGravity;
}
Cursor cursor;
Window parent;
pid_t thispid = getpid();
+ XColor xmousefg, xmousebg;
if (!(xw.dpy = XOpenDisplay(NULL)))
die("Can't open display\n");
die("XCreateIC failed. Could not obtain input method.\n");
/* white cursor, black outline */
- cursor = XCreateFontCursor(xw.dpy, XC_xterm);
+ cursor = XCreateFontCursor(xw.dpy, mouseshape);
XDefineCursor(xw.dpy, xw.win, cursor);
- XRecolorCursor(xw.dpy, cursor,
- &(XColor){.red = 0xffff, .green = 0xffff, .blue = 0xffff},
- &(XColor){.red = 0x0000, .green = 0x0000, .blue = 0x0000});
+
+ 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);
{
int numspecs;
XftGlyphFontSpec spec;
+
numspecs = xmakeglyphfontspecs(&spec, &g, 1, x, y);
xdrawglyphfontspecs(&spec, g, numspecs, x, y);
}
{
static int oldx = 0, oldy = 0;
int curx;
- Glyph g = {' ', ATTR_NULL, defaultbg, defaultcs};
+ 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);
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;
- /* remove the old cursor */
- xdrawglyph(term.line[oldy][oldx], oldx, oldy);
+ /*
+ * 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 0: /* Blinking Block */
- case 1: /* Blinking Block (Default) */
- case 2: /* Steady Block */
- if (IS_SET(MODE_REVERSE)) {
- g.mode |= ATTR_REVERSE;
- g.fg = defaultcs;
- g.bg = defaultfg;
- }
-
- 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, &dc.col[defaultcs],
- 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, &dc.col[defaultcs],
- borderpx + curx * xw.cw,
- borderpx + term.c.y * xw.ch,
- cursorthickness, xw.ch);
- break;
+ 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, &dc.col[defaultcs],
+ XftDrawRect(xw.draw, &drawcol,
borderpx + curx * xw.cw,
borderpx + term.c.y * xw.ch,
xw.cw - 1, 1);
- XftDrawRect(xw.draw, &dc.col[defaultcs],
+ XftDrawRect(xw.draw, &drawcol,
borderpx + curx * xw.cw,
borderpx + term.c.y * xw.ch,
1, xw.ch - 1);
- XftDrawRect(xw.draw, &dc.col[defaultcs],
+ XftDrawRect(xw.draw, &drawcol,
borderpx + (curx + 1) * xw.cw - 1,
borderpx + term.c.y * xw.ch,
1, xw.ch - 1);
- XftDrawRect(xw.draw, &dc.col[defaultcs],
+ XftDrawRect(xw.draw, &drawcol,
borderpx + curx * xw.cw,
borderpx + (term.c.y + 1) * xw.ch - 1,
xw.cw, 1);
{
int i, x, y, ox, numspecs;
Glyph base, new;
- XftGlyphFontSpec* specs;
+ XftGlyphFontSpec *specs;
int ena_sel = sel.ob.x != -1 && sel.alt == IS_SET(MODE_ALTSCREEN);
if (!(xw.state & WIN_VISIBLE))
do {
XNextEvent(xw.dpy, &ev);
/*
- * XFilterEvent is required to be called after you using XOpenIM,
- * this is not unnecessary.It does not only filter the key event,
- * but some clientmessage for input method as well.
+ * 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;
{
die("%s " VERSION " (c) 2010-2015 st engineers\n"
"usage: st [-a] [-v] [-c class] [-f font] [-g geometry] [-o file]\n"
- " [-i] [-t title] [-w windowid] [-e command ...] [command ...]\n"
+ " [-i] [-t title] [-T title] [-w windowid] [-e command ...]"
+ " [command ...]\n"
" st [-a] [-v] [-c class] [-f font] [-g geometry] [-o file]\n"
- " [-i] [-t title] [-w windowid] [-l line] [stty_args ...]\n",
+ " [-i] [-t title] [-T title] [-w windowid] -l line"
+ " [stty_args ...]\n",
argv0);
}
xw.l = xw.t = 0;
xw.isfixed = False;
- xw.cursor = 0;
+ xw.cursor = cursorshape;
ARGBEGIN {
case 'a':
opt_line = EARGF(usage());
break;
case 't':
+ case 'T':
opt_title = EARGF(usage());
break;
case 'w':
return 0;
}
+