#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 DIVCEIL(n, d) (((n) + ((d) - 1)) / (d))
#define ISCONTROLC0(c) (BETWEEN(c, 0, 0x1f) || (c) == '\177')
#define ISCONTROLC1(c) (BETWEEN(c, 0x80, 0x9f))
#define ISCONTROL(c) (ISCONTROLC0(c) || ISCONTROLC1(c))
MODE_MOUSEMANY = 1 << 18,
MODE_BRCKTPASTE = 1 << 19,
MODE_PRINT = 1 << 20,
+ MODE_UTF8 = 1 << 21,
MODE_MOUSE = MODE_MOUSEBTN|MODE_MOUSEMOTION|MODE_MOUSEX10\
|MODE_MOUSEMANY,
};
ESC_ALTCHARSET = 8,
ESC_STR_END = 16, /* a final string was encountered */
ESC_TEST = 32, /* Enter in test mode */
+ ESC_UTF8 = 64,
};
enum window_state {
static void techo(Rune);
static void tcontrolcode(uchar );
static void tdectest(char );
+static void tdefutf8(char);
static int32_t tdefcolor(int *, int *, int);
static void tdeftran(char);
static inline int match(uint, uint);
static void xsetpointermotion(int);
static void xseturgency(int);
static void xsetsel(char *, Time);
-static void xtermclear(int, int, int, int);
static void xunloadfont(Font *);
static void xunloadfonts(void);
static void xresize(int, int);
static pid_t pid;
static Selection sel;
static int iofd = 1;
-static char **opt_cmd = NULL;
-static char *opt_io = NULL;
-static char *opt_title = NULL;
-static char *opt_embed = NULL;
+static char **opt_cmd = 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 *opt_embed = NULL;
+static char *opt_font = NULL;
+static char *opt_io = NULL;
+static char *opt_line = NULL;
+static char *opt_name = NULL;
+static char *opt_title = NULL;
+static int oldbutton = 3; /* button event on startup: 3 = release */
static char *usedfont = NULL;
static double usedfontsize = 0;
* Deleting the property again tells the selection owner to send the
* next data chunk in the property.
*/
- if (e->type == PropertyNotify)
- XDeleteProperty(xw.dpy, xw.win, (int)property);
+ XDeleteProperty(xw.dpy, xw.win, (int)property);
}
void
if ((n = strlen(s)) > siz-1)
die("stty parameter length too long\n");
*q++ = ' ';
- q = memcpy(q, s, n);
+ memcpy(q, s, n);
q += n;
- siz-= n + 1;
+ siz -= n + 1;
}
*q = '\0';
if (system(cmd) != 0)
if ((ret = read(cmdfd, buf+buflen, LEN(buf)-buflen)) < 0)
die("Couldn't read from shell: %s\n", strerror(errno));
- /* process every complete utf8 char */
buflen += ret;
ptr = buf;
- while ((charsize = utf8decode(ptr, &unicodep, buflen))) {
- tputc(unicodep);
- ptr += charsize;
- buflen -= charsize;
- }
+ for (;;) {
+ if (IS_SET(MODE_UTF8)) {
+ /* process a complete utf8 char */
+ charsize = utf8decode(ptr, &unicodep, buflen);
+ if (charsize == 0)
+ break;
+ tputc(unicodep);
+ ptr += charsize;
+ buflen -= charsize;
+
+ } else {
+ if (buflen <= 0)
+ break;
+ tputc(*ptr++ & 0xFF);
+ buflen--;
+ }
+ }
/* keep any uncomplete utf8 char for the next call */
- memmove(buf, ptr, buflen);
+ if (buflen > 0)
+ memmove(buf, ptr, buflen);
return ret;
}
ttysend(char *s, size_t n)
{
int len;
+ char *t, *lim;
Rune u;
ttywrite(s, n);
- if (IS_SET(MODE_ECHO))
- while ((len = utf8decode(s, &u, n)) > 0) {
- techo(u);
- n -= len;
- s += len;
+ if (!IS_SET(MODE_ECHO))
+ return;
+
+ lim = &s[n];
+ for (t = s; t < lim; t += len) {
+ if (IS_SET(MODE_UTF8)) {
+ len = utf8decode(t, &u, n);
+ } else {
+ u = *t & 0xFF;
+ len = 1;
}
+ if (len <= 0)
+ break;
+ techo(u);
+ n -= len;
+ }
}
void
term.tabs[i] = 1;
term.top = 0;
term.bot = term.row - 1;
- term.mode = MODE_WRAP;
+ term.mode = MODE_WRAP|MODE_UTF8;
memset(term.trantbl, CS_USA, sizeof(term.trantbl));
term.charset = 0;
tputc(u);
}
+void
+tdefutf8(char ascii)
+{
+ if (ascii == 'G')
+ term.mode |= MODE_UTF8;
+ else if (ascii == '@')
+ term.mode &= ~MODE_UTF8;
+}
+
void
tdeftran(char ascii)
{
case '#':
term.esc |= ESC_TEST;
return 0;
+ case '%':
+ term.esc |= ESC_UTF8;
+ return 0;
case 'P': /* DCS -- Device Control String */
case '_': /* APC -- Application Program Command */
case '^': /* PM -- Privacy Message */
Glyph *gp;
control = ISCONTROL(u);
- len = utf8encode(u, c);
- if (!control && (width = wcwidth(u)) == -1) {
- memcpy(c, "\357\277\275", 4); /* UTF_INVALID */
- width = 1;
+ if (!IS_SET(MODE_UTF8)) {
+ c[0] = u;
+ width = len = 1;
+ } else {
+ len = utf8encode(u, c);
+ if (!control && (width = wcwidth(u)) == -1) {
+ memcpy(c, "\357\277\275", 4); /* UTF_INVALID */
+ width = 1;
+ }
}
if (IS_SET(MODE_PRINT))
csihandle();
}
return;
+ } else if (term.esc & ESC_UTF8) {
+ tdefutf8(u);
} else if (term.esc & ESC_ALTCHARSET) {
tdeftran(u);
} else if (term.esc & ESC_TEST) {
return 0;
}
-void
-xtermclear(int col1, int row1, int col2, int row2)
-{
- XftDrawRect(xw.draw,
- &dc.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);
-}
-
/*
* Absolute coordinates.
*/
void
xhints(void)
{
- XClassHint class = {termname, opt_class ? opt_class : termname};
+ XClassHint class = {opt_name ? opt_name : termname,
+ opt_class ? opt_class : termname};
XWMHints wm = {.flags = InputHint, .input = 1};
XSizeHints *sizeh = NULL;
{
FcPattern *match;
FcResult result;
+ XGlyphInfo extents;
- match = FcFontMatch(NULL, pattern, &result);
+ match = XftFontMatch(xw.dpy, xw.scr, pattern, &result);
if (!match)
return 1;
return 1;
}
+ XftTextExtentsUtf8(xw.dpy, f->match,
+ (const FcChar8 *) ascii_printable,
+ strlen(ascii_printable), &extents);
+
f->set = NULL;
f->pattern = FcPatternDuplicate(pattern);
f->rbearing = f->match->max_advance_width;
f->height = f->ascent + f->descent;
- f->width = f->lbearing + f->rbearing;
+ f->width = DIVCEIL(extents.xOff, strlen(ascii_printable));
return 0;
}
defaultfontsize = usedfontsize;
}
- FcConfigSubstitute(0, pattern, FcMatchPattern);
- FcDefaultSubstitute(pattern);
-
if (xloadfont(&dc.font, pattern))
die("st: can't open font %s\n", fontstr);
xunloadfonts();
xloadfonts(usedfont, arg->f);
cresize(0, 0);
+ ttyresize();
redraw();
xhints();
}
if (xw.gm & XNegative)
xw.l += DisplayWidth(xw.dpy, xw.scr) - xw.w - 2;
if (xw.gm & YNegative)
- xw.t += DisplayWidth(xw.dpy, xw.scr) - xw.h - 2;
+ xw.t += DisplayHeight(xw.dpy, xw.scr) - xw.h - 2;
/* Events */
xw.attrs.background_pixel = dc.col[defaultbg].pixel;
specs[numspecs].font = frc[f].font;
specs[numspecs].glyph = glyphidx;
specs[numspecs].x = (short)xp;
- specs[numspecs].y = (short)(winy + frc[f].font->ascent);
+ specs[numspecs].y = (short)yp;
xp += runewidth;
numspecs++;
}
if (!term.dirty[y])
continue;
- xtermclear(0, y, term.col, y);
term.dirty[y] = 0;
specs = term.specbuf;
tresize(col, row);
xresize(col, row);
- ttyresize();
}
void
return;
cresize(e->xconfigure.width, e->xconfigure.height);
+ ttyresize();
}
void
}
} while (ev.type != MapNotify);
- ttynew();
cresize(w, h);
+ ttynew();
+ ttyresize();
clock_gettime(CLOCK_MONOTONIC, &last);
lastblink = last;
void
usage(void)
{
- 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] [-T title] [-w windowid] [-e command ...]"
- " [command ...]\n"
- " st [-a] [-v] [-c class] [-f font] [-g geometry] [-o file]\n"
- " [-i] [-t title] [-T title] [-w windowid] -l line"
- " [stty_args ...]\n",
- argv0);
+ die("usage: %s [-aiv] [-c class] [-f font] [-g geometry]"
+ " [-n name] [-o file]\n"
+ " [-T title] [-t title] [-w windowid]"
+ " [[-e] command [args ...]]\n"
+ " %s [-aiv] [-c class] [-f font] [-g geometry]"
+ " [-n name] [-o file]\n"
+ " [-T title] [-t title] [-w windowid] -l line"
+ " [stty_args ...]\n", argv0, argv0);
}
int
case 'l':
opt_line = EARGF(usage());
break;
+ case 'n':
+ opt_name = EARGF(usage());
+ break;
case 't':
case 'T':
opt_title = EARGF(usage());
opt_embed = EARGF(usage());
break;
case 'v':
+ die("%s " VERSION " (c) 2010-2016 st engineers\n", argv0);
+ break;
default:
usage();
} ARGEND;