-/* See LICENSE for licence details. */
+/* See LICENSE for license details. */
#include <ctype.h>
#include <errno.h>
#include <fcntl.h>
enum window_state {
WIN_VISIBLE = 1,
- WIN_REDRAW = 2,
- WIN_FOCUSED = 4
+ WIN_FOCUSED = 2
};
enum selection_type {
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 xresettitle(void);
static void xsetpointermotion(int);
static void xseturgency(int);
-static void xsetsel(char *);
+static void xsetsel(char *, Time);
static void xtermclear(int, int, int, int);
static void xunloadfont(Font *);
static void xunloadfonts(void);
static void selnormalize(void);
static inline bool selected(int, int);
static char *getsel(void);
-static void selcopy(void);
+static void selcopy(Time);
static void selscroll(int, int);
static void selsnap(int, int *, int *, int);
static int x2col(int);
static char *opt_embed = NULL;
static char *opt_class = NULL;
static char *opt_font = NULL;
+static char *opt_line = NULL;
static int oldbutton = 3; /* button event on startup: 3 = release */
static char *usedfont = NULL;
return LIMIT(y, 0, term.row-1);
}
-int tlinelen(int y) {
+int
+tlinelen(int y) {
int i = term.col;
if(term.line[y][i - 1].mode & ATTR_WRAP)
ptr = str = xmalloc(bufsize);
/* append every set & selected glyph to the selection */
- for(y = sel.nb.y; y < sel.ne.y + 1; y++) {
+ for(y = sel.nb.y; y <= sel.ne.y; y++) {
linelen = tlinelen(y);
if(sel.type == SEL_RECTANGULAR) {
}
void
-selcopy(void) {
- xsetsel(getsel());
+selcopy(Time t) {
+ xsetsel(getsel(), t);
}
void
XSelectionEvent *xsev;
ofs = 0;
- xsev = (XSelectionEvent *)e;
+ xsev = &e->xselection;
if (xsev->property == None)
return;
do {
xev.selection = xsre->selection;
xev.target = xsre->target;
xev.time = xsre->time;
+ if (xsre->property == None)
+ xsre->property = xsre->target;
+
/* reject */
xev.property = None;
}
void
-xsetsel(char *str) {
+xsetsel(char *str, Time t) {
free(sel.primary);
sel.primary = str;
- XSetSelectionOwner(xw.dpy, XA_PRIMARY, xw.win, CurrentTime);
+ XSetSelectionOwner(xw.dpy, XA_PRIMARY, xw.win, t);
+ if (XGetSelectionOwner(xw.dpy, XA_PRIMARY) != xw.win)
+ selclear(0);
}
void
selclear(NULL);
} else {
getbuttoninfo(e);
- selcopy();
+ selcopy(e->xbutton.time);
}
sel.mode = 0;
tsetdirt(sel.nb.y, sel.ne.y);
exit(EXIT_SUCCESS);
}
+
+void
+stty(void)
+{
+ char cmd[_POSIX_ARG_MAX], **p, *q, *s;
+ size_t n, siz;
+
+ if((n = strlen(stty_args)) > sizeof(cmd)-1)
+ die("incorrect stty parameters\n");
+ memcpy(cmd, stty_args, n);
+ q = cmd + n;
+ siz = sizeof(cmd) - n;
+ for(p = opt_cmd; p && (s = *p); ++p) {
+ if((n = strlen(s)) > siz-1)
+ die("stty parameter length too long\n");
+ *q++ = ' ';
+ q = memcpy(q, s, n);
+ q += n;
+ siz-= n + 1;
+ }
+ *q = '\0';
+ system(cmd);
+}
+
void
ttynew(void) {
int m, s;
struct winsize w = {term.row, term.col, 0, 0};
+ if(opt_io) {
+ term.mode |= MODE_PRINT;
+ iofd = (!strcmp(opt_io, "-")) ?
+ STDOUT_FILENO :
+ open(opt_io, O_WRONLY | O_CREAT, 0666);
+ if(iofd < 0) {
+ fprintf(stderr, "Error opening %s:%s\n",
+ opt_io, strerror(errno));
+ }
+ }
+
+ if (opt_line) {
+ if((cmdfd = open(opt_line, O_RDWR)) < 0)
+ die("open line failed: %s\n", strerror(errno));
+ close(STDIN_FILENO);
+ dup(cmdfd);
+ stty();
+ return;
+ }
+
/* seems to work fine on linux, openbsd and freebsd */
if(openpty(&m, &s, NULL, NULL, &w) < 0)
die("openpty failed: %s\n", strerror(errno));
die("fork failed\n");
break;
case 0:
+ close(iofd);
setsid(); /* create a new process group */
dup2(s, STDIN_FILENO);
dup2(s, STDOUT_FILENO);
close(s);
cmdfd = m;
signal(SIGCHLD, sigchld);
- if(opt_io) {
- term.mode |= MODE_PRINT;
- iofd = (!strcmp(opt_io, "-")) ?
- STDOUT_FILENO :
- open(opt_io, O_WRONLY | O_CREAT, 0666);
- if(iofd < 0) {
- fprintf(stderr, "Error opening %s:%s\n",
- opt_io, strerror(errno));
- }
- }
break;
}
}
term.top = 0;
term.bot = term.row - 1;
term.mode = MODE_WRAP;
- memset(term.trantbl, sizeof(term.trantbl), CS_USA);
+ memset(term.trantbl, CS_USA, sizeof(term.trantbl));
term.charset = 0;
for(i = 0; i < 2; i++) {
miny = 0;
maxy = term.row - 1;
}
- LIMIT(x, 0, term.col-1);
- LIMIT(y, miny, maxy);
term.c.state &= ~CURSOR_WRAPNEXT;
- term.c.x = x;
- term.c.y = y;
+ term.c.x = LIMIT(x, 0, term.col-1);
+ term.c.y = LIMIT(y, miny, maxy);
}
void
for(--x; x > 0 && !term.tabs[x]; --x)
/* nothing */ ;
}
- tmoveto(x, term.c.y);
+ term.c.x = LIMIT(x, 0, term.col-1);
}
void
void
tstrsequence(uchar c) {
- if (c & 0x80) {
- switch (c) {
- case 0x90: /* DCS -- Device Control String */
- c = 'P';
- break;
- case 0x9f: /* APC -- Application Program Command */
- c = '_';
- break;
- case 0x9e: /* PM -- Privacy Message */
- c = '^';
- break;
- case 0x9d: /* OSC -- Operating System Command */
- c = ']';
- break;
- }
+ switch (c) {
+ case 0x90: /* DCS -- Device Control String */
+ c = 'P';
+ break;
+ case 0x9f: /* APC -- Application Program Command */
+ c = '_';
+ break;
+ case 0x9e: /* PM -- Privacy Message */
+ c = '^';
+ break;
+ case 0x9d: /* OSC -- Operating System Command */
+ c = ']';
+ break;
}
strreset();
strescseq.type = c;
term.esc |= ESC_STR;
- return;
}
void
}
/* only CAN, SUB, \a and C1 chars interrupt a sequence */
term.esc &= ~(ESC_STR_END|ESC_STR);
- return;
}
/*
int i;
int minrow = MIN(row, term.row);
int mincol = MIN(col, term.col);
- int slide = term.c.y - row + 1;
bool *bp;
TCursor c;
return;
}
- /* free unneeded rows */
- i = 0;
- if(slide > 0) {
- /*
- * slide screen to keep cursor where we expect it -
- * tscrollup would work here, but we can optimize to
- * memmove because we're freeing the earlier lines
- */
- for(/* i = 0 */; i < slide; i++) {
- free(term.line[i]);
- free(term.alt[i]);
- }
- memmove(term.line, term.line + slide, row * sizeof(Line));
- memmove(term.alt, term.alt + slide, row * sizeof(Line));
+ /*
+ * slide screen to keep cursor where we expect it -
+ * tscrollup would work here, but we can optimize to
+ * memmove because we're freeing the earlier lines
+ */
+ for(i = 0; i <= term.c.y - row; i++) {
+ free(term.line[i]);
+ free(term.alt[i]);
+ }
+ /* ensure that both src and dst are not NULL */
+ if (i > 0) {
+ memmove(term.line, term.line + i, row * sizeof(Line));
+ memmove(term.alt, term.alt + i, row * sizeof(Line));
}
for(i += row; i < term.row; i++) {
free(term.line[i]);
return x == 0 ? 0 : 0x3737 + 0x2828 * x;
}
+bool
+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;
- XRenderColor color = { .alpha = 0xffff };
static bool loaded;
Color *cp;
XftColorFree(xw.dpy, xw.vis, xw.cmap, cp);
}
- /* load colors [0-15] and [256-LEN(colorname)] (config.h) */
- for(i = 0; i < LEN(colorname); i++) {
- if(!colorname[i])
- continue;
- if(!XftColorAllocName(xw.dpy, xw.vis, xw.cmap, colorname[i], &dc.col[i])) {
- die("Could not allocate color '%s'\n", colorname[i]);
+ 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);
}
- }
-
- /* load colors [16-231] ; same colors as xterm */
- for(i = 16; i < 6*6*6+16; i++) {
- 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 );
- if(!XftColorAllocValue(xw.dpy, xw.vis, xw.cmap, &color, &dc.col[i]))
- die("Could not allocate color %d\n", i);
- }
-
- /* load colors [232-255] ; grayscale */
- for(; i < 256; i++) {
- color.red = color.green = color.blue = 0x0808 + 0x0a0a * (i-(6*6*6+16));
- if(!XftColorAllocValue(xw.dpy, xw.vis, xw.cmap, &color, &dc.col[i]))
- die("Could not allocate color %d\n", i);
- }
loaded = true;
}
int
xsetcolorname(int x, const char *name) {
- XRenderColor color = { .alpha = 0xffff };
Color ncolor;
- if(!BETWEEN(x, 0, LEN(colorname)))
+ if(!BETWEEN(x, 0, LEN(dc.col)))
return 1;
- if(!name) {
- if(BETWEEN(x, 16, 16 + 215)) { /* 256 color */
- color.red = sixd_to_16bit( ((x-16)/36)%6 );
- color.green = sixd_to_16bit( ((x-16)/6) %6 );
- color.blue = sixd_to_16bit( ((x-16)/1) %6 );
- if(!XftColorAllocValue(xw.dpy, xw.vis,
- xw.cmap, &color, &ncolor)) {
- return 1;
- }
-
- XftColorFree(xw.dpy, xw.vis, xw.cmap, &dc.col[x]);
- dc.col[x] = ncolor;
- return 0;
- } else if(BETWEEN(x, 16 + 216, 255)) { /* greyscale */
- color.red = color.green = color.blue = \
- 0x0808 + 0x0a0a * (x - (16 + 216));
- if(!XftColorAllocValue(xw.dpy, xw.vis,
- xw.cmap, &color, &ncolor)) {
- return 1;
- }
- XftColorFree(xw.dpy, xw.vis, xw.cmap, &dc.col[x]);
- dc.col[x] = ncolor;
- return 0;
- } else { /* system colors */
- name = colorname[x];
- }
- }
- if(!XftColorAllocName(xw.dpy, xw.vis, xw.cmap, name, &ncolor))
+ if(!xloadcolor(x, name, &ncolor))
return 1;
XftColorFree(xw.dpy, xw.vis, xw.cmap, &dc.col[x]);
case 4: /* Steady Underline */
XftDrawRect(xw.draw, &dc.col[defaultcs],
borderpx + curx * xw.cw,
- borderpx + (term.c.y + 1) * xw.ch - 1,
- xw.cw, 1);
+ 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,
- 1, xw.ch);
+ borderpx + curx * xw.cw,
+ borderpx + term.c.y * xw.ch,
+ cursorthickness, xw.ch);
break;
}
} else {
void
expose(XEvent *ev) {
- XExposeEvent *e = &ev->xexpose;
-
- if(xw.state & WIN_REDRAW) {
- if(!e->count)
- xw.state &= ~WIN_REDRAW;
- }
redraw();
}
visibility(XEvent *ev) {
XVisibilityEvent *e = &ev->xvisibility;
- if(e->state == VisibilityFullyObscured) {
- xw.state &= ~WIN_VISIBLE;
- } else if(!(xw.state & WIN_VISIBLE)) {
- /* need a full redraw for next Expose, not just a buf copy */
- xw.state |= WIN_VISIBLE | WIN_REDRAW;
- }
+ MODBIT(xw.state, e->state != VisibilityFullyObscured, WIN_VISIBLE);
}
void
long deltatime;
/* Waiting for window mapping */
- while(1) {
+ do {
XNextEvent(xw.dpy, &ev);
- if(XFilterEvent(&ev, None))
- continue;
if(ev.type == ConfigureNotify) {
w = ev.xconfigure.width;
h = ev.xconfigure.height;
- } else if(ev.type == MapNotify) {
- break;
}
- }
+ } while(ev.type != MapNotify);
ttynew();
cresize(w, h);
clock_gettime(CLOCK_MONOTONIC, &now);
drawtimeout.tv_sec = 0;
- drawtimeout.tv_nsec = (1000/xfps) * 1E6;
+ drawtimeout.tv_nsec = (1000 * 1E6)/ xfps;
tv = &drawtimeout;
dodraw = 0;
dodraw = 1;
}
deltatime = TIMEDIFF(now, last);
- if(deltatime > (xev? (1000/xfps) : (1000/actionfps))
- || deltatime < 0) {
+ if(deltatime > 1000 / (xev ? xfps : actionfps)) {
dodraw = 1;
last = now;
}
void
usage(void) {
- die("%s " VERSION " (c) 2010-2015 st engineers\n" \
+ 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 ...]\n", argv0);
+ " [-i] [-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",
+ argv0);
}
int
main(int argc, char *argv[]) {
- char *titles;
uint cols = 80, rows = 24;
xw.l = xw.t = 0;
opt_class = EARGF(usage());
break;
case 'e':
- /* eat all remaining arguments */
- if(argc > 1) {
- opt_cmd = &argv[1];
- if(argv[1] != NULL && opt_title == NULL) {
- titles = xstrdup(argv[1]);
- opt_title = basename(titles);
- }
- }
+ if(argc > 1)
+ --argc, ++argv;
goto run;
case 'f':
opt_font = EARGF(usage());
case 'o':
opt_io = EARGF(usage());
break;
+ case 'l':
+ opt_line = EARGF(usage());
+ break;
case 't':
opt_title = EARGF(usage());
break;
} ARGEND;
run:
+ if(argc > 0) {
+ /* eat all remaining arguments */
+ opt_cmd = argv;
+ if(!opt_title)
+ opt_title = basename(xstrdup(argv[0]));
+ }
setlocale(LC_CTYPE, "");
XSetLocaleModifiers("");
- tnew(cols? cols : 1, rows? rows : 1);
+ tnew(MAX(cols, 1), MAX(rows, 1));
xinit();
selinit();
run();