#define TRUERED(x) (((x) & 0xff0000) >> 8)
#define TRUEGREEN(x) (((x) & 0xff00))
#define TRUEBLUE(x) (((x) & 0xff) << 8)
+#define TLINE(y) ((y) < term.scr ? term.hist[((y) + term.histi - term.scr \
+ + histsize + 1) % histsize] : term.line[(y) - term.scr])
enum glyph_attribute {
} TCursor;
/* CSI Escape sequence structs */
-/* ESC '[' [[ [<priv>] <arg> [;]] <mode>] */
+/* ESC '[' [[ [<priv>] <arg> [;]] <mode> [<mode>]] */
typedef struct {
char buf[ESC_BUF_SIZ]; /* raw string */
int len; /* raw string length */
char priv;
int arg[ESC_ARG_SIZ];
int narg; /* nb of args */
- char mode;
+ char mode[2];
} CSIEscape;
/* STR Escape sequence structs */
int col; /* nb col */
Line *line; /* screen */
Line *alt; /* alternate screen */
+ Line *hist; /* history buffer */
+ int histi; /* history index */
+ int scr; /* scroll back */
bool *dirty; /* dirtyness of lines */
TCursor c; /* cursor */
int top; /* top scroll limit */
int ch; /* char height */
int cw; /* char width */
char state; /* focus, redraw, visible */
+ int cursor; /* cursor style */
} XWindow;
typedef struct {
/* function definitions used in config.h */
static void clipcopy(const Arg *);
static void clippaste(const Arg *);
+static void kscrolldown(const Arg *);
+static void kscrollup(const Arg *);
static void numlock(const Arg *);
static void selpaste(const Arg *);
static void xzoom(const Arg *);
static void tputc(char *, int);
static void treset(void);
static void tresize(int, int);
-static void tscrollup(int, int);
-static void tscrolldown(int, int);
+static void tscrollup(int, int, bool);
+static void tscrolldown(int, int, bool);
static void tsetattr(int *, int);
static void tsetchar(char *, Glyph *, int, int);
static void tsetscroll(int, int);
static 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 = 0;
+ sel.snap = 0;
sel.ob.x = -1;
sel.primary = NULL;
sel.clipboard = NULL;
static int tlinelen(int y) {
int i = term.col;
- if(term.line[y][i - 1].mode & ATTR_WRAP)
- return i;
+ if(TLINE(y)[i - 1].mode & ATTR_WRAP)
+ return i;
- while(i > 0 && term.line[y][i - 1].c[0] == ' ')
+ while(i > 0 && TLINE(y)[i - 1].c[0] == ' ')
--i;
return i;
* Snap around if the word wraps around at the end or
* beginning of a line.
*/
- prevgp = &term.line[*y][*x];
+ prevgp = &TLINE(*y)[*x];
prevdelim = strchr(worddelimiters, prevgp->c[0]) != NULL;
for(;;) {
newx = *x + direction;
yt = *y, xt = *x;
else
yt = newy, xt = newx;
- if(!(term.line[yt][xt].mode & ATTR_WRAP))
+ if(!(TLINE(yt)[xt].mode & ATTR_WRAP))
break;
}
if (newx >= tlinelen(newy))
break;
- gp = &term.line[newy][newx];
+ gp = &TLINE(newy)[newx];
delim = strchr(worddelimiters, gp->c[0]) != NULL;
if(!(gp->mode & ATTR_WDUMMY) && (delim != prevdelim
|| (delim && gp->c[0] != prevgp->c[0])))
*x = (direction < 0) ? 0 : term.col - 1;
if(direction < 0 && *y > 0) {
for(; *y > 0; *y += direction) {
- if(!(term.line[*y-1][term.col-1].mode
+ if(!(TLINE(*y-1)[term.col-1].mode
& ATTR_WRAP)) {
break;
}
}
} else if(direction > 0 && *y < term.row-1) {
for(; *y < term.row; *y += direction) {
- if(!(term.line[*y][term.col-1].mode
+ if(!(TLINE(*y)[term.col-1].mode
& ATTR_WRAP)) {
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);
- }
-
- len = 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);
+ return;
}
void
struct timespec now;
Mousekey *mk;
- 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)) {
linelen = tlinelen(y);
if(sel.type == SEL_RECTANGULAR) {
- gp = &term.line[y][sel.nb.x];
+ gp = &TLINE(y)[sel.nb.x];
lastx = sel.ne.x;
} else {
- gp = &term.line[y][sel.nb.y == y ? sel.nb.x : 0];
+ gp = &TLINE(y)[sel.nb.y == y ? sel.nb.x : 0];
lastx = (sel.ne.y == y) ? sel.ne.x : term.col-1;
}
- last = &term.line[y][MIN(lastx, linelen-1)];
+ last = &TLINE(y)[MIN(lastx, linelen-1)];
while(last >= gp && last->c[0] == ' ')
--last;
ofs = 0;
xsev = (XSelectionEvent *)e;
+ if (xsev->property == None)
+ return;
do {
if(XGetWindowProperty(xw.dpy, xw.win, xsev->property, ofs,
BUFSIZ/4, False, AnyPropertyType,
XA_ATOM, 32, PropModeReplace,
(uchar *) &string, 1);
xev.property = xsre->property;
- } else if(xsre->target == sel.xtarget) {
+ } 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;
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) {
bmotion(XEvent *e) {
int oldey, oldex, oldsby, oldsey;
- if(IS_SET(MODE_MOUSE) && !(e->xbutton.state & forceselmod)) {
- mousereport(e);
- return;
- }
-
if(!sel.mode)
return;
/* keep any uncomplete utf8 char for the next call */
memmove(buf, ptr, buflen);
+ if(term.scr > 0 && term.scr < histsize-1) term.scr++;
}
void
ttywrite(const char *s, size_t n) {
+ Arg arg = (Arg){ .i = term.scr };
+
+ kscrolldown(&arg);
+
if(xwrite(cmdfd, s, n) == -1)
die("write error on tty: %s\n", strerror(errno));
}
}
void
-tscrolldown(int orig, int n) {
+kscrolldown(const Arg* a) {
+ int n = a->i;
+
+ if(n < 0)
+ n = term.row + n;
+
+ if(n > term.scr)
+ n = term.scr;
+
+ if(term.scr > 0) {
+ term.scr -= n;
+ selscroll(0, -n);
+ tfulldirt();
+ }
+}
+
+void
+kscrollup(const Arg* a) {
+ int n = a->i;
+
+ if(n < 0)
+ n = term.row + n;
+
+ if(term.scr <= histsize - n) {
+ term.scr += n;
+ selscroll(0, n);
+ tfulldirt();
+ }
+}
+
+void
+tscrolldown(int orig, int n, bool copyhist) {
int i;
Line temp;
LIMIT(n, 0, term.bot-orig+1);
tsetdirt(orig, term.bot-n);
+ if(copyhist) {
+ term.histi = (term.histi - 1 + histsize) % histsize;
+ temp = term.hist[term.histi];
+ term.hist[term.histi] = term.line[term.bot];
+ term.line[term.bot] = temp;
+ }
+
tclearregion(0, term.bot-n+1, term.col-1, term.bot);
for(i = term.bot; i >= orig+n; i--) {
}
void
-tscrollup(int orig, int n) {
+tscrollup(int orig, int n, bool copyhist) {
int i;
Line temp;
LIMIT(n, 0, term.bot-orig+1);
+ if(copyhist) {
+ term.histi = (term.histi + 1) % histsize;
+ temp = term.hist[term.histi];
+ term.hist[term.histi] = term.line[orig];
+ term.line[orig] = temp;
+ }
+
tclearregion(0, orig, term.col-1, orig+n-1);
tsetdirt(orig+n, term.bot);
int y = term.c.y;
if(y == term.bot) {
- tscrollup(term.top, 1);
+ tscrollup(term.top, 1, true);
} else {
y++;
}
break;
p++;
}
- csiescseq.mode = *p;
+ csiescseq.mode[0] = *p++;
+ csiescseq.mode[1] = (p < csiescseq.buf+csiescseq.len) ? *p : '\0';
}
/* for absolute user moves, when decom is set */
void
tinsertblankline(int n) {
if(BETWEEN(term.c.y, term.top, term.bot))
- tscrolldown(term.c.y, n);
+ tscrolldown(term.c.y, n, false);
}
void
tdeleteline(int n) {
if(BETWEEN(term.c.y, term.top, term.bot))
- tscrollup(term.c.y, n);
+ tscrollup(term.c.y, n, false);
}
int32_t
char buf[40];
int len;
- switch(csiescseq.mode) {
+ switch(csiescseq.mode[0]) {
default:
unknown:
fprintf(stderr, "erresc: unknown csi ");
break;
case 'S': /* SU -- Scroll <n> line up */
DEFAULT(csiescseq.arg[0], 1);
- tscrollup(term.top, csiescseq.arg[0]);
+ tscrollup(term.top, csiescseq.arg[0], false);
break;
case 'T': /* SD -- Scroll <n> line down */
DEFAULT(csiescseq.arg[0], 1);
- tscrolldown(term.top, csiescseq.arg[0]);
+ tscrolldown(term.top, csiescseq.arg[0], false);
break;
case 'L': /* IL -- Insert <n> blank lines */
DEFAULT(csiescseq.arg[0], 1);
case 'u': /* DECRC -- Restore cursor position (ANSI.SYS) */
tcursor(CURSOR_LOAD);
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:
+ goto unknown;
+ }
+ break;
}
}
void
strparse(void) {
+ int c;
char *p = strescseq.buf;
strescseq.narg = 0;
strescseq.buf[strescseq.len] = '\0';
- while(p && strescseq.narg < STR_ARG_SIZ)
- strescseq.args[strescseq.narg++] = strsep(&p, ";");
+
+ if(*p == '\0')
+ return;
+
+ while(strescseq.narg < STR_ARG_SIZ) {
+ strescseq.args[strescseq.narg++] = p;
+ while((c = *p) != ';' && c != '\0')
+ ++p;
+ if(c == '\0')
+ return;
+ *p++ = '\0';
+ }
}
void
return 0;
case 'D': /* IND -- Linefeed */
if(term.c.y == term.bot) {
- tscrollup(term.top, 1);
+ tscrollup(term.top, 1, true);
} else {
tmoveto(term.c.x, term.c.y+1);
}
break;
case 'M': /* RI -- Reverse index */
if(term.c.y == term.top) {
- tscrolldown(term.top, 1);
+ tscrolldown(term.top, 1, true);
} else {
tmoveto(term.c.x, term.c.y-1);
}
void
tresize(int col, int row) {
- int i;
+ int i, j;
int minrow = MIN(row, term.row);
int mincol = MIN(col, term.col);
int slide = term.c.y - row + 1;
/* resize to new height */
term.line = xrealloc(term.line, row * sizeof(Line));
term.alt = xrealloc(term.alt, row * sizeof(Line));
+ term.hist = xrealloc(term.hist, histsize * sizeof(Line));
term.dirty = xrealloc(term.dirty, row * sizeof(*term.dirty));
term.tabs = xrealloc(term.tabs, col * sizeof(*term.tabs));
+ for(i = 0; i < histsize; i++) {
+ term.hist[i] = xrealloc(term.hist[i], col * sizeof(Glyph));
+ for(j = mincol; j < col; j++) {
+ term.hist[i][j] = term.c.attr;
+ memcpy(term.hist[i][j].c, " ", 2);
+ }
+ }
+
/* resize each row to new width, zero-pad if needed */
for(i = 0; i < minrow; i++) {
term.line[i] = xrealloc(term.line[i], col * sizeof(Glyph));
/* draw the new one */
if(xw.state & WIN_FOCUSED) {
- if(IS_SET(MODE_REVERSE)) {
- g.mode |= ATTR_REVERSE;
- g.fg = defaultcs;
- g.bg = defaultfg;
- }
+ 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;
+ }
- sl = utf8len(g.c);
- width = (term.line[term.c.y][curx].mode & ATTR_WIDE)\
- ? 2 : 1;
- xdraws(g.c, g, term.c.x, term.c.y, width, sl);
+ sl = utf8len(g.c);
+ width = (term.line[term.c.y][curx].mode & ATTR_WIDE)\
+ ? 2 : 1;
+ xdraws(g.c, g, term.c.x, term.c.y, width, sl);
+ 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 - 1,
+ xw.cw, 1);
+ 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);
+ break;
+ }
} else {
XftDrawRect(xw.draw, &dc.col[defaultcs],
borderpx + curx * xw.cw,
xtermclear(0, y, term.col, y);
term.dirty[y] = 0;
- base = term.line[y][0];
+ base = TLINE(y)[0];
ic = ib = ox = 0;
for(x = x1; x < x2; x++) {
- new = term.line[y][x];
+ new = TLINE(y)[x];
if(new.mode == ATTR_WDUMMY)
continue;
if(ena_sel && selected(x, y))
if(ib > 0)
xdraws(buf, base, ox, y, ic, ib);
}
- xdrawcursor();
+ if(term.scr == 0)
+ xdrawcursor();
}
void
}
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))
+ if(FD_ISSET(xfd, &rfd)) {
xev = actionfps;
+ if(blinktimeout) {
+ lastblink = now;
+ MODBIT(term.mode, 1, MODE_BLINK);
+ blinkset = 1;
+ }
+ }
clock_gettime(CLOCK_MONOTONIC, &now);
drawtimeout.tv_sec = 0;
xw.l = xw.t = 0;
xw.isfixed = False;
+ xw.cursor = 0;
ARGBEGIN {
case 'a':