#define ATTRCMP(a, b) ((a).mode != (b).mode || (a).fg != (b).fg || (a).bg != (b).bg)
#define IS_SET(flag) (term.mode & (flag))
#define TIMEDIFF(t1, t2) ((t1.tv_sec-t2.tv_sec)*1000 + (t1.tv_usec-t2.tv_usec)/1000)
-#define X2COL(x) (((x) - BORDER)/xw.cw)
-#define Y2ROW(y) (((y) - BORDER)/xw.ch)
#define VT102ID "\033[?6c"
enum cursor_state {
CURSOR_DEFAULT = 0,
- CURSOR_HIDE = 1,
- CURSOR_WRAPNEXT = 2
+ CURSOR_WRAPNEXT = 1,
+ CURSOR_ORIGIN = 2
};
enum glyph_state {
MODE_MOUSEMOTION = 64,
MODE_MOUSE = 32|64,
MODE_REVERSE = 128,
- MODE_KBDLOCK = 256
+ MODE_KBDLOCK = 256,
+ MODE_HIDE = 512
};
enum escape_state {
static void tinsertblank(int);
static void tinsertblankline(int);
static void tmoveto(int, int);
+static void tmoveato(int x, int y);
static void tnew(int, int);
static void tnewline(int);
static void tputtab(bool);
sel.xtarget = XA_STRING;
}
+static int
+x2col(int x) {
+ x -= borderpx;
+ x /= xw.cw;
+
+ return LIMIT(x, 0, term.col-1);
+}
+
+static int
+y2row(int y) {
+ y -= borderpx;
+ y /= xw.ch;
+
+ return LIMIT(y, 0, term.row-1);
+}
+
static inline bool
selected(int x, int y) {
int bx, ex;
if(b)
*b = e->xbutton.button;
- *x = X2COL(e->xbutton.x);
- *y = Y2ROW(e->xbutton.y);
+ *x = x2col(e->xbutton.x);
+ *y = y2row(e->xbutton.y);
+
sel.b.x = sel.by < sel.ey ? sel.bx : sel.ex;
sel.b.y = MIN(sel.by, sel.ey);
sel.e.x = sel.by < sel.ey ? sel.ex : sel.bx;
void
mousereport(XEvent *e) {
- int x = X2COL(e->xbutton.x);
- int y = Y2ROW(e->xbutton.y);
+ int x = x2col(e->xbutton.x);
+ int y = y2row(e->xbutton.y);
int button = e->xbutton.button;
int state = e->xbutton.state;
char buf[] = { '\033', '[', 'M', 0, 32+x+1, 32+y+1 };
draw();
}
sel.mode = 1;
- sel.ex = sel.bx = X2COL(e->xbutton.x);
- sel.ey = sel.by = Y2ROW(e->xbutton.y);
+ sel.ex = sel.bx = x2col(e->xbutton.x);
+ sel.ey = sel.by = y2row(e->xbutton.y);
+ } else if(e->xbutton.button == Button4) {
+ ttywrite("\031", 1);
+ } else if(e->xbutton.button == Button5) {
+ ttywrite("\005", 1);
}
}
sel.bx = -1;
gettimeofday(&now, NULL);
- if(TIMEDIFF(now, sel.tclick2) <= TRIPLECLICK_TIMEOUT) {
+ if(TIMEDIFF(now, sel.tclick2) <= tripleclicktimeout) {
/* triple click on the line */
sel.b.x = sel.bx = 0;
sel.e.x = sel.ex = term.col;
sel.b.y = sel.e.y = sel.ey;
selcopy();
- } else if(TIMEDIFF(now, sel.tclick1) <= DOUBLECLICK_TIMEOUT) {
+ } else if(TIMEDIFF(now, sel.tclick1) <= doubleclicktimeout) {
/* double click to select word */
sel.bx = sel.ex;
while(sel.bx > 0 && term.line[sel.ey][sel.bx-1].state & GLYPH_SET &&
signal(SIGTERM, SIG_DFL);
signal(SIGALRM, SIG_DFL);
- DEFAULT(envshell, SHELL);
- putenv("TERM="TNAME);
+ DEFAULT(envshell, shell);
+ setenv("TERM", termname, 1);
args = opt_cmd ? opt_cmd : (char *[]){envshell, "-i", NULL};
execvp(args[0], args);
exit(EXIT_FAILURE);
term.c = (TCursor){{
.mode = ATTR_NULL,
- .fg = DefaultFG,
- .bg = DefaultBG
+ .fg = defaultfg,
+ .bg = defaultbg
}, .x = 0, .y = 0, .state = CURSOR_DEFAULT};
memset(term.tabs, 0, term.col * sizeof(*term.tabs));
- for(i = TAB; i < term.col; i += TAB)
+ for(i = tabspaces; i < term.col; i += tabspaces)
term.tabs[i] = 1;
term.top = 0;
term.bot = term.row - 1;
term.mode = MODE_WRAP;
tclearregion(0, 0, term.col-1, term.row-1);
+ tmoveto(0, 0);
+ tcursor(CURSOR_SAVE);
}
void
}
}
+/* for absolute user moves, when decom is set */
+void
+tmoveato(int x, int y) {
+ tmoveto(x, y + ((term.c.state & CURSOR_ORIGIN) ? term.top: 0));
+}
+
void
tmoveto(int x, int y) {
+ int miny, maxy;
+
+ if(term.c.state & CURSOR_ORIGIN) {
+ miny = term.top;
+ maxy = term.bot;
+ } else {
+ miny = 0;
+ maxy = term.row - 1;
+ }
LIMIT(x, 0, term.col-1);
- LIMIT(y, 0, term.row-1);
+ LIMIT(y, miny, maxy);
term.c.state &= ~CURSOR_WRAPNEXT;
term.c.x = x;
term.c.y = y;
case 0:
term.c.attr.mode &= ~(ATTR_REVERSE | ATTR_UNDERLINE | ATTR_BOLD \
| ATTR_ITALIC | ATTR_BLINK);
- term.c.attr.fg = DefaultFG;
- term.c.attr.bg = DefaultBG;
+ term.c.attr.fg = defaultfg;
+ term.c.attr.bg = defaultbg;
break;
case 1:
term.c.attr.mode |= ATTR_BOLD;
}
break;
case 39:
- term.c.attr.fg = DefaultFG;
+ term.c.attr.fg = defaultfg;
break;
case 48:
if(i + 2 < l && attr[i + 1] == 5) {
}
break;
case 49:
- term.c.attr.bg = DefaultBG;
+ term.c.attr.bg = defaultbg;
break;
default:
if(BETWEEN(attr[i], 30, 37)) {
if(mode != term.mode)
redraw();
break;
- case 6: /* XXX: DECOM -- Origin */
+ case 6: /* DECOM -- Origin */
+ MODBIT(term.c.state, set, CURSOR_ORIGIN);
+ tmoveato(0, 0);
break;
case 7: /* DECAWM -- Auto wrap */
MODBIT(term.mode, set, MODE_WRAP);
break;
- case 8: /* XXX: DECARM -- Auto repeat */
- break;
case 0: /* Error (IGNORED) */
+ case 2: /* DECANM -- ANSI/VT52 (IGNORED) */
+ case 3: /* DECCOLM -- Column (IGNORED) */
+ case 4: /* DECSCLM -- Scroll (IGNORED) */
+ case 8: /* DECARM -- Auto repeat (IGNORED) */
+ case 18: /* DECPFF -- Printer feed (IGNORED) */
+ case 19: /* DECPEX -- Printer extent (IGNORED) */
+ case 42: /* DECNRCM -- National characters (IGNORED) */
case 12: /* att610 -- Start blinking cursor (IGNORED) */
break;
- case 25:
- MODBIT(term.c.state, !set, CURSOR_HIDE);
+ case 25: /* DECTCEM -- Text Cursor Enable Mode */
+ MODBIT(term.mode, !set, MODE_HIDE);
break;
case 1000: /* 1000,1002: enable xterm mouse report */
MODBIT(term.mode, set, MODE_MOUSEBTN);
break;
case 1049: /* = 1047 and 1048 */
case 47:
- case 1047:
- if(IS_SET(MODE_ALTSCREEN))
+ case 1047: {
+ bool alt = IS_SET(MODE_ALTSCREEN) != 0;
+ if(alt)
tclearregion(0, 0, term.col-1, term.row-1);
- if((set && !IS_SET(MODE_ALTSCREEN)) ||
- (!set && IS_SET(MODE_ALTSCREEN))) {
+ if(set ^ alt) /* set is always 1 or 0 */
tswapscreen();
- }
if(*args != 1049)
break;
+ }
/* pass through */
case 1048:
tcursor((set) ? CURSOR_SAVE : CURSOR_LOAD);
break;
default:
- /* case 2: DECANM -- ANSI/VT52 (NOT SUPPOURTED) */
- /* case 3: DECCOLM -- Column (NOT SUPPORTED) */
- /* case 4: DECSCLM -- Scroll (NOT SUPPORTED) */
- /* case 18: DECPFF -- Printer feed (NOT SUPPORTED) */
- /* case 19: DECPEX -- Printer extent (NOT SUPPORTED) */
- /* case 42: DECNRCM -- National characters (NOT SUPPORTED) */
fprintf(stderr,
"erresc: unknown private set/reset mode %d\n",
*args);
tinsertblank(csiescseq.arg[0]);
break;
case 'A': /* CUU -- Cursor <n> Up */
- case 'e':
DEFAULT(csiescseq.arg[0], 1);
tmoveto(term.c.x, term.c.y-csiescseq.arg[0]);
break;
case 'B': /* CUD -- Cursor <n> Down */
+ case 'e': /* VPR --Cursor <n> Down */
DEFAULT(csiescseq.arg[0], 1);
tmoveto(term.c.x, term.c.y+csiescseq.arg[0]);
break;
ttywrite(VT102ID, sizeof(VT102ID) - 1);
break;
case 'C': /* CUF -- Cursor <n> Forward */
- case 'a':
+ case 'a': /* HPR -- Cursor <n> Forward */
DEFAULT(csiescseq.arg[0], 1);
tmoveto(term.c.x+csiescseq.arg[0], term.c.y);
break;
case 'f': /* HVP */
DEFAULT(csiescseq.arg[0], 1);
DEFAULT(csiescseq.arg[1], 1);
- tmoveto(csiescseq.arg[1]-1, csiescseq.arg[0]-1);
+ tmoveato(csiescseq.arg[1]-1, csiescseq.arg[0]-1);
break;
case 'I': /* CHT -- Cursor Forward Tabulation <n> tab stops */
DEFAULT(csiescseq.arg[0], 1);
break;
case 'd': /* VPA -- Move to <row> */
DEFAULT(csiescseq.arg[0], 1);
- tmoveto(term.c.x, csiescseq.arg[0]-1);
+ tmoveato(term.c.x, csiescseq.arg[0]-1);
break;
case 'h': /* SM -- Set terminal mode */
tsetmode(csiescseq.priv, 1, csiescseq.arg, csiescseq.narg);
DEFAULT(csiescseq.arg[0], 1);
DEFAULT(csiescseq.arg[1], term.row);
tsetscroll(csiescseq.arg[0]-1, csiescseq.arg[1]-1);
- tmoveto(0, 0);
+ tmoveato(0, 0);
}
break;
case 's': /* DECSC -- Save cursor position (ANSI.SYS) */
}
}
/*
- * STR sequences must be checked before of anything
- * because it can use some control codes as part of the sequence
+ * STR sequences must be checked before anything else
+ * because it can use some control codes as part of the sequence.
*/
if(term.esc & ESC_STR) {
switch(ascii) {
}
return;
}
+
/*
* Actions of control codes must be performed as soon they arrive
* because they can be embedded inside a control sequence, and
case '\030': /* CAN */
csireset();
return;
- case '\005': /* ENQ (IGNORED) */
- case '\000': /* NUL (IGNORED) */
- case '\021': /* XON (IGNORED) */
- case '\023': /* XOFF (IGNORED) */
- case 0177: /* DEL (IGNORED) */
+ case '\005': /* ENQ (IGNORED) */
+ case '\000': /* NUL (IGNORED) */
+ case '\021': /* XON (IGNORED) */
+ case '\023': /* XOFF (IGNORED) */
+ case 0177: /* DEL (IGNORED) */
return;
}
} else if(term.esc & ESC_START) {
memset(bp, 0, sizeof(*term.tabs) * (col - term.col));
while(--bp > term.tabs && !*bp)
/* nothing */ ;
- for(bp += TAB; bp < term.tabs + col; bp += TAB)
+ for(bp += tabspaces; bp < term.tabs + col; bp += tabspaces)
*bp = 1;
}
/* update terminal size */
term.col = col;
term.row = row;
- /* make use of the LIMIT in tmoveto */
- tmoveto(term.c.x, term.c.y);
/* reset scrolling region */
tsetscroll(0, row-1);
+ /* make use of the LIMIT in tmoveto */
+ tmoveto(term.c.x, term.c.y);
return (slide > 0);
}
void
xresize(int col, int row) {
- xw.tw = MAX(1, 2*BORDER + col * xw.cw);
- xw.th = MAX(1, 2*BORDER + row * xw.ch);
+ xw.tw = MAX(1, 2*borderpx + col * xw.cw);
+ xw.th = MAX(1, 2*borderpx + row * xw.ch);
XftDrawChange(xw.xft_draw, xw.buf);
}
void
xtermclear(int col1, int row1, int col2, int row2) {
XftDrawRect(xw.xft_draw,
- &dc.xft_col[IS_SET(MODE_REVERSE) ? DefaultFG : DefaultBG],
- BORDER + col1 * xw.cw,
- BORDER + row1 * xw.ch,
+ &dc.xft_col[IS_SET(MODE_REVERSE) ? defaultfg : defaultbg],
+ borderpx + col1 * xw.cw,
+ borderpx + row1 * xw.ch,
(col2-col1+1) * xw.cw,
(row2-row1+1) * xw.ch);
}
void
xclear(int x1, int y1, int x2, int y2) {
XftDrawRect(xw.xft_draw,
- &dc.xft_col[IS_SET(MODE_REVERSE) ? DefaultFG : DefaultBG],
+ &dc.xft_col[IS_SET(MODE_REVERSE) ? defaultfg : defaultbg],
x1, y1, x2-x1, y2-y1);
}
void
xhints(void) {
- XClassHint class = {opt_class ? opt_class : TNAME, TNAME};
+ XClassHint class = {opt_class ? opt_class : termname, termname};
XWMHints wm = {.flags = InputHint, .input = 1};
XSizeHints *sizeh = NULL;
sizeh->width = xw.w;
sizeh->height_inc = xw.ch;
sizeh->width_inc = xw.cw;
- sizeh->base_height = 2*BORDER;
- sizeh->base_width = 2*BORDER;
+ sizeh->base_height = 2*borderpx;
+ sizeh->base_width = 2*borderpx;
} else {
sizeh->flags = PMaxSize | PMinSize;
sizeh->min_width = sizeh->max_width = xw.fw;
FcResult result;
double fontval;
- pattern = FcNameParse((FcChar8 *)fontstr);
+ if(fontstr[0] == '-') {
+ pattern = XftXlfdParse(fontstr, False, False);
+ } else {
+ pattern = FcNameParse((FcChar8 *)fontstr);
+ }
+
if(!pattern)
die("st: can't open font %s\n", fontstr);
xw.vis = XDefaultVisual(xw.dpy, xw.scr);
/* font */
- usedfont = (opt_font == NULL)? FONT : opt_font;
+ usedfont = (opt_font == NULL)? font : opt_font;
xloadfonts(usedfont, 0);
/* colors */
xw.w = xw.fw;
} else {
/* window - default size */
- xw.h = 2*BORDER + term.row * xw.ch;
- xw.w = 2*BORDER + term.col * xw.cw;
+ xw.h = 2*borderpx + term.row * xw.ch;
+ xw.w = 2*borderpx + term.col * xw.cw;
xw.fx = 0;
xw.fy = 0;
}
- attrs.background_pixel = dc.xft_col[DefaultBG].pixel;
- attrs.border_pixel = dc.xft_col[DefaultBG].pixel;
+ attrs.background_pixel = dc.xft_col[defaultbg].pixel;
+ attrs.border_pixel = dc.xft_col[defaultbg].pixel;
attrs.bit_gravity = NorthWestGravity;
attrs.event_mask = FocusChangeMask | KeyPressMask
| ExposureMask | VisibilityChangeMask | StructureNotifyMask
void
xdraws(char *s, Glyph base, int x, int y, int charlen, int bytelen) {
- int winx = BORDER + x * xw.cw, winy = BORDER + y * xw.ch,
+ int winx = borderpx + x * xw.cw, winy = borderpx + y * xw.ch,
width = charlen * xw.cw;
Font *font = &dc.font;
XGlyphInfo extents;
*temp, revfg, revbg;
XRenderColor colfg, colbg;
- if(base.mode & ATTR_REVERSE)
- temp = fg, fg = bg, bg = temp;
-
if(base.mode & ATTR_BOLD) {
if(BETWEEN(base.fg, 0, 7)) {
/* basic system colors */
if(base.mode & ATTR_ITALIC)
font = &dc.ifont;
- if(base.mode & (ATTR_ITALIC|ATTR_ITALIC))
+ if((base.mode & ATTR_ITALIC) && (base.mode & ATTR_BOLD))
font = &dc.ibfont;
if(IS_SET(MODE_REVERSE)) {
- if(fg == &dc.xft_col[DefaultFG]) {
- fg = &dc.xft_col[DefaultBG];
+ if(fg == &dc.xft_col[defaultfg]) {
+ fg = &dc.xft_col[defaultbg];
} else {
colfg.red = ~fg->color.red;
colfg.green = ~fg->color.green;
fg = &revfg;
}
- if(bg == &dc.xft_col[DefaultBG]) {
- bg = &dc.xft_col[DefaultFG];
+ if(bg == &dc.xft_col[defaultbg]) {
+ bg = &dc.xft_col[defaultfg];
} else {
colbg.red = ~bg->color.red;
colbg.green = ~bg->color.green;
}
}
+ if(base.mode & ATTR_REVERSE)
+ temp = fg, fg = bg, bg = temp;
+
XftTextExtentsUtf8(xw.dpy, font->xft_set, (FcChar8 *)s, bytelen,
&extents);
width = extents.xOff;
/* Intelligent cleaning up of the borders. */
if(x == 0) {
- xclear(0, (y == 0)? 0 : winy, BORDER,
+ xclear(0, (y == 0)? 0 : winy, borderpx,
winy + xw.ch + (y == term.row-1)? xw.h : 0);
}
if(x + charlen >= term.col-1) {
(y == term.row-1)? xw.h : (winy + xw.ch));
}
if(y == 0)
- xclear(winx, 0, winx + width, BORDER);
+ xclear(winx, 0, winx + width, borderpx);
if(y == term.row-1)
xclear(winx, winy + xw.ch, winx + width, xw.h);
xdrawcursor(void) {
static int oldx = 0, oldy = 0;
int sl;
- Glyph g = {{' '}, ATTR_NULL, DefaultBG, DefaultCS, 0};
+ Glyph g = {{' '}, ATTR_NULL, defaultbg, defaultcs, 0};
LIMIT(oldx, 0, term.col-1);
LIMIT(oldy, 0, term.row-1);
}
/* draw the new one */
- if(!(term.c.state & CURSOR_HIDE)) {
+ if(!(IS_SET(MODE_HIDE))) {
if(!(xw.state & WIN_FOCUSED))
- g.bg = DefaultUCS;
+ g.bg = defaultucs;
if(IS_SET(MODE_REVERSE))
- g.mode |= ATTR_REVERSE, g.fg = DefaultCS, g.bg = DefaultFG;
+ g.mode |= ATTR_REVERSE, g.fg = defaultcs, g.bg = defaultfg;
sl = utf8size(g.c);
xdraws(g.c, g, term.c.x, term.c.y, 1, sl);
int ic, ib, x, y, ox, sl;
Glyph base, new;
char buf[DRAW_BUF_SIZ];
- bool ena_sel = sel.bx != -1, alt = IS_SET(MODE_ALTSCREEN);
+ bool ena_sel = sel.bx != -1, alt = IS_SET(MODE_ALTSCREEN) != 0;
- if((sel.alt && !alt) || (!sel.alt && alt))
+ if((sel.alt != 0) ^ alt)
ena_sel = 0;
if(!(xw.state & WIN_VISIBLE))
return;
void
focus(XEvent *ev) {
if(ev->type == FocusIn) {
+ XSetICFocus(xw.xic);
xw.state |= WIN_FOCUSED;
xseturgency(0);
} else {
+ XUnsetICFocus(xw.xic);
xw.state &= ~WIN_FOCUSED;
}
}
selpaste();
break;
case XK_Return:
+ if(meta)
+ ttywrite("\033", 1);
+
if(IS_SET(MODE_CRLF)) {
ttywrite("\r\n", 2);
} else {
if(height != 0)
xw.h = height;
- col = (xw.w - 2*BORDER) / xw.cw;
- row = (xw.h - 2*BORDER) / xw.ch;
- if(col == term.col && row == term.row)
- return;
+ col = (xw.w - 2*borderpx) / xw.cw;
+ row = (xw.h - 2*borderpx) / xw.ch;
tresize(col, row);
xresize(col, row);
while(XPending(xw.dpy)) {
XNextEvent(xw.dpy, &ev);
- if(XFilterEvent(&ev, xw.win))
+ if(XFilterEvent(&ev, None))
continue;
if(handler[ev.type])
(handler[ev.type])(&ev);
run:
setlocale(LC_CTYPE, "");
+ XSetLocaleModifiers("");
tnew(80, 24);
xinit();
ttynew();