#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 {
int x, y;
} nb, ne, ob, oe;
- char *clip;
+ char *primary, *clipboard;
Atom xtarget;
bool alt;
struct timespec tclick1;
} Shortcut;
/* 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);
[MotionNotify] = bmotion,
[ButtonPress] = bpress,
[ButtonRelease] = brelease,
- [SelectionClear] = selclear,
+/*
+ * Uncomment if you want the selection to disappear when you select something
+ * different in another window.
+ */
+/* [SelectionClear] = selclear, */
[SelectionNotify] = selnotify,
[SelectionRequest] = selrequest,
};
typedef struct {
XftFont *font;
int flags;
+ long unicodep;
} Fontcache;
/* Fontcache is an array now. A new font will be appended to the array. */
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.clip = NULL;
+ sel.primary = NULL;
+ sel.clipboard = NULL;
sel.xtarget = XInternAtom(xw.dpy, "UTF8_STRING", 0);
if(sel.xtarget == None)
sel.xtarget = XA_STRING;
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; // jason
+// 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);
}
void
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;
int format;
uchar *data, *last, *repl;
Atom type;
+ XSelectionEvent *xsev;
ofs = 0;
+ xsev = (XSelectionEvent *)e;
+ if (xsev->property == None)
+ return;
do {
- if(XGetWindowProperty(xw.dpy, xw.win, XA_PRIMARY, ofs, BUFSIZ/4,
- False, AnyPropertyType, &type, &format,
- &nitems, &rem, &data)) {
+ if(XGetWindowProperty(xw.dpy, xw.win, xsev->property, ofs,
+ BUFSIZ/4, False, AnyPropertyType,
+ &type, &format, &nitems, &rem,
+ &data)) {
fprintf(stderr, "Clipboard allocation failed\n");
return;
}
}
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);
+ }
+}
+
+void
clippaste(const Arg *dummy) {
Atom clipboard;
clipboard = XInternAtom(xw.dpy, "CLIPBOARD", 0);
- XConvertSelection(xw.dpy, clipboard, sel.xtarget, XA_PRIMARY,
+ XConvertSelection(xw.dpy, clipboard, sel.xtarget, clipboard,
xw.win, CurrentTime);
}
selrequest(XEvent *e) {
XSelectionRequestEvent *xsre;
XSelectionEvent xev;
- Atom xa_targets, string;
+ Atom xa_targets, string, clipboard;
+ char *seltext;
xsre = (XSelectionRequestEvent *) e;
xev.type = SelectionNotify;
XA_ATOM, 32, PropModeReplace,
(uchar *) &string, 1);
xev.property = xsre->property;
- } else if(xsre->target == sel.xtarget && sel.clip != NULL) {
- XChangeProperty(xsre->display, xsre->requestor, xsre->property,
- xsre->target, 8, PropModeReplace,
- (uchar *) sel.clip, strlen(sel.clip));
- 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 */
void
xsetsel(char *str) {
- /* register the selection for both the clipboard and the primary */
- Atom clipboard;
-
- free(sel.clip);
- sel.clip = str;
+ free(sel.primary);
+ sel.primary = str;
XSetSelectionOwner(xw.dpy, XA_PRIMARY, xw.win, CurrentTime);
-
- clipboard = XInternAtom(xw.dpy, "CLIPBOARD", 0);
- XSetSelectionOwner(xw.dpy, clipboard, xw.win, CurrentTime);
}
void
/* 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));
xdraws(char *s, Glyph base, int x, int y, int charlen, int bytelen) {
int winx = borderpx + x * xw.cw, winy = borderpx + y * xw.ch,
width = charlen * xw.cw, xp, i;
- int frcflags;
+ int frcflags, charexists;
int u8fl, u8fblen, u8cblen, doesexist;
char *u8c, *u8fs;
long unicodep;
/* Search the font cache. */
for(i = 0; i < frclen; i++) {
- if(XftCharExists(xw.dpy, frc[i].font, unicodep)
- && frc[i].flags == frcflags) {
+ charexists = XftCharExists(xw.dpy, frc[i].font, unicodep);
+ /* Everything correct. */
+ if(charexists && frc[i].flags == frcflags)
+ break;
+ /* We got a default font for a not found glyph. */
+ if(!charexists && frc[i].flags == frcflags \
+ && frc[i].unicodep == unicodep) {
break;
}
}
FcMatchPattern);
FcDefaultSubstitute(fcpattern);
- fontpattern = FcFontSetMatch(0, fcsets,
- FcTrue, fcpattern, &fcres);
+ 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 = unicodep;
i = frclen;
frclen++;
/* 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':