#include <sys/time.h>
#include <sys/types.h>
#include <sys/wait.h>
+#include <termios.h>
#include <time.h>
#include <unistd.h>
#include <libgen.h>
#define XK_SWITCH_MOD (1<<13)
/* macros */
-#define MIN(a, b) ((a) < (b) ? (a) : (b))
-#define MAX(a, b) ((a) < (b) ? (b) : (a))
-#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 ISCONTROLC0(c) (BETWEEN(c, 0, 0x1f) || (c) == '\177')
-#define ISCONTROLC1(c) (BETWEEN(c, 0x80, 0x9f))
-#define ISCONTROL(c) (ISCONTROLC0(c) || ISCONTROLC1(c))
-#define ISDELIM(u) (utf8strchr(worddelimiters, u) != NULL)
-#define LIMIT(x, a, b) (x) = (x) < (a) ? (a) : (x) > (b) ? (b) : (x)
-#define ATTRCMP(a, b) ((a).mode != (b).mode || (a).fg != (b).fg || (a).bg != (b).bg)
-#define IS_SET(flag) ((term.mode & (flag)) != 0)
-#define TIMEDIFF(t1, t2) ((t1.tv_sec-t2.tv_sec)*1000 + (t1.tv_nsec-t2.tv_nsec)/1E6)
-#define MODBIT(x, set, bit) ((set) ? ((x) |= (bit)) : ((x) &= ~(bit)))
-
-#define TRUECOLOR(r,g,b) (1 << 24 | (r) << 16 | (g) << 8 | (b))
-#define IS_TRUECOL(x) (1 << 24 & (x))
-#define TRUERED(x) (((x) & 0xff0000) >> 8)
-#define TRUEGREEN(x) (((x) & 0xff00))
-#define TRUEBLUE(x) (((x) & 0xff) << 8)
-
+#define MIN(a, b) ((a) < (b) ? (a) : (b))
+#define MAX(a, b) ((a) < (b) ? (b) : (a))
+#define LEN(a) (sizeof(a) / sizeof(a)[0])
+#define NUMMAXLEN(x) ((int)(sizeof(x) * 2.56 + 0.5) + 1)
+#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))
+#define ISDELIM(u) (utf8strchr(worddelimiters, u) != NULL)
+#define LIMIT(x, a, b) (x) = (x) < (a) ? (a) : (x) > (b) ? (b) : (x)
+#define ATTRCMP(a, b) ((a).mode != (b).mode || (a).fg != (b).fg || \
+ (a).bg != (b).bg)
+#define IS_SET(flag) ((term.mode & (flag)) != 0)
+#define TIMEDIFF(t1, t2) ((t1.tv_sec-t2.tv_sec)*1000 + \
+ (t1.tv_nsec-t2.tv_nsec)/1E6)
+#define MODBIT(x, set, bit) ((set) ? ((x) |= (bit)) : ((x) &= ~(bit)))
+
+#define TRUECOLOR(r,g,b) (1 << 24 | (r) << 16 | (g) << 8 | (b))
+#define IS_TRUECOL(x) (1 << 24 & (x))
+#define TRUERED(x) (((x) & 0xff0000) >> 8)
+#define TRUEGREEN(x) (((x) & 0xff00))
+#define TRUEBLUE(x) (((x) & 0xff) << 8)
+
+/* constants */
+#define ISO14755CMD "dmenu -w %lu -p codepoint: </dev/null"
enum glyph_attribute {
ATTR_NULL = 0,
MODE_MOUSEMANY = 1 << 18,
MODE_BRCKTPASTE = 1 << 19,
MODE_PRINT = 1 << 20,
+ MODE_UTF8 = 1 << 21,
+ MODE_SIXEL = 1 << 22,
MODE_MOUSE = MODE_MOUSEBTN|MODE_MOUSEMOTION|MODE_MOUSEX10\
|MODE_MOUSEMANY,
};
enum escape_state {
ESC_START = 1,
ESC_CSI = 2,
- ESC_STR = 4, /* DCS, OSC, PM, APC */
+ ESC_STR = 4, /* OSC, PM, APC */
ESC_ALTCHARSET = 8,
ESC_STR_END = 16, /* a final string was encountered */
ESC_TEST = 32, /* Enter in test mode */
+ ESC_UTF8 = 64,
+ ESC_DCS =128,
};
enum window_state {
uint b;
uint mask;
char *s;
-} Mousekey;
+} MouseShortcut;
typedef struct {
KeySym k;
static void xzoomreset(const Arg *);
static void printsel(const Arg *);
static void printscreen(const Arg *) ;
+static void iso14755(const Arg *);
static void toggleprinter(const Arg *);
+static void sendbreak(const Arg *);
/* Config.h for applying patches and the configuration. */
#include "config.h"
int width;
int ascent;
int descent;
+ int badslant;
+ int badweight;
short lbearing;
short rbearing;
XftFont *match;
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 ttynew(void);
-static void ttyread(void);
+static size_t ttyread(void);
static void ttyresize(void);
static void ttysend(char *, size_t);
static void ttywrite(const char *, size_t);
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;
xwrite(int fd, const char *s, size_t len)
{
size_t aux = len;
+ ssize_t r;
while (len > 0) {
- ssize_t r = write(fd, s, len);
+ r = write(fd, s, len);
if (r < 0)
return r;
len -= r;
s += r;
}
+
return aux;
}
return 0;
*u = udecoded;
utf8validate(u, len);
+
return len;
}
for (*i = 0; *i < LEN(utfmask); ++(*i))
if (((uchar)c & utfmask[*i]) == utfbyte[*i])
return (uchar)c & ~utfmask[*i];
+
return 0;
}
len = utf8validate(&u, 0);
if (len > UTF_SIZ)
return 0;
+
for (i = len - 1; i != 0; --i) {
c[i] = utf8encodebyte(u, 0);
u >>= 6;
}
c[0] = utf8encodebyte(u, len);
+
return len;
}
if (r == u)
return &(s[i]);
}
+
return NULL;
}
*u = UTF_INVALID;
for (i = 1; *u > utfmax[i]; ++i)
;
+
return i;
}
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 = SEL_IDLE;
+ sel.snap = 0;
sel.ob.x = -1;
sel.primary = NULL;
sel.clipboard = NULL;
bpress(XEvent *e)
{
struct timespec now;
- Mousekey *mk;
+ MouseShortcut *ms;
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)) {
- ttysend(mk->s, strlen(mk->s));
+ for (ms = mshortcuts; ms < mshortcuts + LEN(mshortcuts); ms++) {
+ if (e->xbutton.button == ms->b
+ && match(ms->mask, e->xbutton.state)) {
+ ttysend(ms->s, strlen(ms->s));
return;
}
}
/* append every set & selected glyph to the selection */
for (y = sel.nb.y; y <= sel.ne.y; y++) {
- linelen = tlinelen(y);
+ if ((linelen = tlinelen(y)) == 0) {
+ *ptr++ = '\n';
+ continue;
+ }
if (sel.type == SEL_RECTANGULAR) {
gp = &term.line[y][sel.nb.x];
*repl++ = '\r';
}
- if (IS_SET(MODE_BRCKTPASTE))
+ if (IS_SET(MODE_BRCKTPASTE) && ofs == 0)
ttywrite("\033[200~", 6);
ttysend((char *)data, nitems * format / 8);
- if (IS_SET(MODE_BRCKTPASTE))
+ if (IS_SET(MODE_BRCKTPASTE) && rem == 0)
ttywrite("\033[201~", 6);
XFree(data);
/* number of 32-bit chunks returned */
* 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
die("who are you?\n");
}
- if (!(sh = getenv("SHELL"))) {
+ if ((sh = getenv("SHELL")) == NULL)
sh = (pw->pw_shell[0]) ? pw->pw_shell : shell;
- }
if (opt_cmd)
prog = opt_cmd[0];
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 (opt_line) {
if ((cmdfd = open(opt_line, O_RDWR)) < 0)
die("open line failed: %s\n", strerror(errno));
- close(0);
- dup(cmdfd);
+ dup2(cmdfd, 0);
stty();
return;
}
}
}
-void
+size_t
ttyread(void)
{
static char buf[BUFSIZ];
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) && !IS_SET(MODE_SIXEL)) {
+ /* 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;
}
void
ttywrite(const char *s, size_t n)
{
- fd_set wfd;
- struct timespec tv;
+ fd_set wfd, rfd;
ssize_t r;
+ size_t lim = 256;
/*
* Remember that we are using a pty, which might be a modem line.
*/
while (n > 0) {
FD_ZERO(&wfd);
+ FD_ZERO(&rfd);
FD_SET(cmdfd, &wfd);
- tv.tv_sec = 0;
- tv.tv_nsec = 0;
+ FD_SET(cmdfd, &rfd);
/* Check if we can write. */
- if (pselect(cmdfd+1, NULL, &wfd, NULL, &tv, NULL) < 0) {
+ if (pselect(cmdfd+1, &rfd, &wfd, NULL, NULL, NULL) < 0) {
if (errno == EINTR)
continue;
die("select failed: %s\n", strerror(errno));
}
- if(!FD_ISSET(cmdfd, &wfd)) {
- /* No, then free some buffer space. */
- ttyread();
- } else {
+ if (FD_ISSET(cmdfd, &wfd)) {
/*
- * Only write 256 bytes at maximum. This seems to be a
- * reasonable value for a serial line. Bigger values
- * might clog the I/O.
+ * Only write the bytes written by ttywrite() or the
+ * default of 256. This seems to be a reasonable value
+ * for a serial line. Bigger values might clog the I/O.
*/
- r = write(cmdfd, s, (n < 256)? n : 256);
- if (r < 0) {
- die("write error on tty: %s\n",
- strerror(errno));
- }
+ if ((r = write(cmdfd, s, (n < lim)? n : lim)) < 0)
+ goto write_error;
if (r < n) {
/*
* We weren't able to write out everything.
* This means the buffer is getting full
* again. Empty it.
*/
- ttyread();
+ if (n < lim)
+ lim = ttyread();
n -= r;
s += r;
} else {
break;
}
}
+ if (FD_ISSET(cmdfd, &rfd))
+ lim = ttyread();
}
+ return;
+
+write_error:
+ die("write error on tty: %s\n", strerror(errno));
}
void
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) && !IS_SET(MODE_SIXEL)) {
+ 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;
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:
+ 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;
}
xsettitle(strescseq.args[0]);
return;
case 'P': /* DCS -- Device Control String */
+ term.mode |= ESC_DCS;
case '_': /* APC -- Application Program Command */
case '^': /* PM -- Privacy Message */
return;
memset(&strescseq, 0, sizeof(strescseq));
}
+void
+sendbreak(const Arg *arg)
+{
+ if (tcsendbreak(cmdfd, 0))
+ perror("Error sending break");
+}
+
void
tprinter(char *s, size_t len)
{
}
}
+void
+iso14755(const Arg *arg)
+{
+ char cmd[sizeof(ISO14755CMD) + NUMMAXLEN(xw.win)];
+ FILE *p;
+ char *us, *e, codepoint[9], uc[UTF_SIZ];
+ unsigned long utf32;
+
+ snprintf(cmd, sizeof(cmd), ISO14755CMD, xw.win);
+ if (!(p = popen(cmd, "r")))
+ return;
+
+ us = fgets(codepoint, sizeof(codepoint), p);
+ pclose(p);
+
+ if (!us || *us == '\0' || *us == '-' || strlen(us) > 7)
+ return;
+ if ((utf32 = strtoul(us, &e, 16)) == ULONG_MAX ||
+ (*e != '\n' && *e != '\0'))
+ return;
+
+ ttysend(uc, utf8encode(utf32, uc));
+}
+
void
toggleprinter(const Arg *arg)
{
tputc(u);
}
+void
+tdefutf8(char ascii)
+{
+ if (ascii == 'G')
+ term.mode |= MODE_UTF8;
+ else if (ascii == '@')
+ term.mode &= ~MODE_UTF8;
+}
+
void
tdeftran(char ascii)
{
void
tstrsequence(uchar c)
{
+ strreset();
+
switch (c) {
case 0x90: /* DCS -- Device Control String */
c = 'P';
+ term.esc |= ESC_DCS;
break;
case 0x9f: /* APC -- Application Program Command */
c = '_';
c = ']';
break;
}
- strreset();
strescseq.type = c;
term.esc |= ESC_STR;
}
case '\023': /* XOFF (IGNORED) */
case 0177: /* DEL (IGNORED) */
return;
+ case 0x80: /* TODO: PAD */
+ case 0x81: /* TODO: HOP */
+ case 0x82: /* TODO: BPH */
+ case 0x83: /* TODO: NBH */
case 0x84: /* TODO: IND */
break;
case 0x85: /* NEL -- Next line */
tnewline(1); /* always go to first col */
break;
+ case 0x86: /* TODO: SSA */
+ case 0x87: /* TODO: ESA */
+ break;
case 0x88: /* HTS -- Horizontal tab stop */
term.tabs[term.c.x] = 1;
break;
+ case 0x89: /* TODO: HTJ */
+ case 0x8a: /* TODO: VTS */
+ case 0x8b: /* TODO: PLD */
+ case 0x8c: /* TODO: PLU */
case 0x8d: /* TODO: RI */
case 0x8e: /* TODO: SS2 */
case 0x8f: /* TODO: SS3 */
+ case 0x91: /* TODO: PU1 */
+ case 0x92: /* TODO: PU2 */
+ case 0x93: /* TODO: STS */
+ case 0x94: /* TODO: CCH */
+ case 0x95: /* TODO: MW */
+ case 0x96: /* TODO: SPA */
+ case 0x97: /* TODO: EPA */
case 0x98: /* TODO: SOS */
+ case 0x99: /* TODO: SGCI */
break;
case 0x9a: /* DECID -- Identify Terminal */
ttywrite(vtiden, sizeof(vtiden) - 1);
case 0x9c: /* TODO: ST */
break;
case 0x90: /* DCS -- Device Control String */
- case 0x9f: /* APC -- Application Program Command */
- case 0x9e: /* PM -- Privacy Message */
case 0x9d: /* OSC -- Operating System Command */
+ case 0x9e: /* PM -- Privacy Message */
+ case 0x9f: /* APC -- Application Program Command */
tstrsequence(ascii);
return;
}
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 */
int width, len;
Glyph *gp;
- len = utf8encode(u, c);
- if ((width = wcwidth(u)) == -1) {
- memcpy(c, "\357\277\275", 4); /* UTF_INVALID */
- width = 1;
+ control = ISCONTROL(u);
+ if (!IS_SET(MODE_UTF8) && !IS_SET(MODE_SIXEL)) {
+ 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))
tprinter(c, len);
- control = ISCONTROL(u);
/*
* STR sequence must be checked before anything else
if (term.esc & ESC_STR) {
if (u == '\a' || u == 030 || u == 032 || u == 033 ||
ISCONTROLC1(u)) {
- term.esc &= ~(ESC_START|ESC_STR);
+ term.esc &= ~(ESC_START|ESC_STR|ESC_DCS);
+ if (IS_SET(MODE_SIXEL)) {
+ /* TODO: render sixel */;
+ term.mode &= ~MODE_SIXEL;
+ return;
+ }
term.esc |= ESC_STR_END;
- } else if (strescseq.len + len < sizeof(strescseq.buf) - 1) {
- memmove(&strescseq.buf[strescseq.len], c, len);
- strescseq.len += len;
+ goto check_control_code;
+ }
+
+
+ if (IS_SET(MODE_SIXEL)) {
+ /* TODO: implement sixel mode */
return;
- } else {
- /*
- * Here is a bug in terminals. If the user never sends
- * some code to stop the str or esc command, then st
- * will stop responding. But this is better than
- * silently failing with unknown characters. At least
- * then users will report back.
- *
- * In the case users ever get fixed, here is the code:
- */
- /*
- * term.esc = 0;
- * strhandle();
- */
+ }
+ if (term.esc&ESC_DCS && strescseq.len == 0 && u == 'q')
+ term.mode |= MODE_SIXEL;
+
+ if (strescseq.len+len >= sizeof(strescseq.buf)-1) {
+ /*
+ * Here is a bug in terminals. If the user never sends
+ * some code to stop the str or esc command, then st
+ * will stop responding. But this is better than
+ * silently failing with unknown characters. At least
+ * then users will report back.
+ *
+ * In the case users ever get fixed, here is the code:
+ */
+ /*
+ * term.esc = 0;
+ * strhandle();
+ */
return;
}
+
+ memmove(&strescseq.buf[strescseq.len], c, len);
+ strescseq.len += len;
+ return;
}
+check_control_code:
/*
* Actions of control codes must be performed as soon they arrive
* because they can be embedded inside a control sequence, and
csihandle();
}
return;
+ } else if (term.esc & ESC_UTF8) {
+ tdefutf8(u);
} else if (term.esc & ESC_ALTCHARSET) {
tdeftran(u);
} else if (term.esc & ESC_TEST) {
} else
name = colorname[i];
}
+
return XftColorAllocName(xw.dpy, xw.vis, xw.cmap, name, ncolor);
}
XftColorFree(xw.dpy, xw.vis, xw.cmap, &dc.col[x]);
dc.col[x] = ncolor;
- 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);
+ return 0;
}
/*
void
xhints(void)
{
- XClassHint class = {opt_class ? opt_class : termname, termname};
+ XClassHint class = {opt_name ? opt_name : termname,
+ opt_class ? opt_class : termname};
XWMHints wm = {.flags = InputHint, .input = 1};
XSizeHints *sizeh = NULL;
case YNegative:
return SouthWestGravity;
}
+
return SouthEastGravity;
}
{
FcPattern *match;
FcResult result;
+ XGlyphInfo extents;
+ int wantattr, haveattr;
- match = FcFontMatch(NULL, pattern, &result);
+ match = XftFontMatch(xw.dpy, xw.scr, pattern, &result);
if (!match)
return 1;
return 1;
}
+ if ((XftPatternGetInteger(pattern, "slant", 0, &wantattr) ==
+ XftResultMatch)) {
+ /*
+ * Check if xft was unable to find a font with the appropriate
+ * slant but gave us one anyway. Try to mitigate.
+ */
+ if ((XftPatternGetInteger(f->match->pattern, "slant", 0,
+ &haveattr) != XftResultMatch) || haveattr < wantattr) {
+ f->badslant = 1;
+ fputs("st: font slant does not match\n", stderr);
+ }
+ }
+
+ if ((XftPatternGetInteger(pattern, "weight", 0, &wantattr) ==
+ XftResultMatch)) {
+ if ((XftPatternGetInteger(f->match->pattern, "weight", 0,
+ &haveattr) != XftResultMatch) || haveattr != wantattr) {
+ f->badweight = 1;
+ fputs("st: font weight does not match\n", stderr);
+ }
+ }
+
+ 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();
}
Cursor cursor;
Window parent;
pid_t thispid = getpid();
+ XColor xmousefg, xmousebg;
if (!(xw.dpy = XOpenDisplay(NULL)))
die("Can't open display\n");
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;
die("XCreateIC failed. Could not obtain input method.\n");
/* white cursor, black outline */
- cursor = XCreateFontCursor(xw.dpy, XC_xterm);
+ cursor = XCreateFontCursor(xw.dpy, mouseshape);
XDefineCursor(xw.dpy, xw.win, cursor);
- XRecolorCursor(xw.dpy, cursor,
- &(XColor){.red = 0xffff, .green = 0xffff, .blue = 0xffff},
- &(XColor){.red = 0x0000, .green = 0x0000, .blue = 0x0000});
+
+ if (XParseColor(xw.dpy, xw.cmap, colorname[mousefg], &xmousefg) == 0) {
+ xmousefg.red = 0xffff;
+ xmousefg.green = 0xffff;
+ xmousefg.blue = 0xffff;
+ }
+
+ if (XParseColor(xw.dpy, xw.cmap, colorname[mousebg], &xmousebg) == 0) {
+ xmousebg.red = 0x0000;
+ xmousebg.green = 0x0000;
+ xmousebg.blue = 0x0000;
+ }
+
+ XRecolorCursor(xw.dpy, cursor, &xmousefg, &xmousebg);
xw.xembed = XInternAtom(xw.dpy, "_XEMBED", False);
xw.wmdeletewin = XInternAtom(xw.dpy, "WM_DELETE_WINDOW", False);
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++;
}
XRenderColor colfg, colbg;
XRectangle r;
- /* Determine foreground and background colors based on mode. */
- if (base.fg == defaultfg) {
- if (base.mode & ATTR_ITALIC)
- base.fg = defaultitalic;
- else if ((base.mode & ATTR_ITALIC) && (base.mode & ATTR_BOLD))
- base.fg = defaultitalic;
- else if (base.mode & ATTR_UNDERLINE)
- base.fg = defaultunderline;
+ /* Fallback on color display for attributes not supported by the font */
+ if (base.mode & ATTR_ITALIC && base.mode & ATTR_BOLD) {
+ if (dc.ibfont.badslant || dc.ibfont.badweight)
+ base.fg = defaultattr;
+ } else if ((base.mode & ATTR_ITALIC && dc.ifont.badslant) ||
+ (base.mode & ATTR_BOLD && dc.bfont.badweight)) {
+ base.fg = defaultattr;
}
if (IS_TRUECOL(base.fg)) {
{
int numspecs;
XftGlyphFontSpec spec;
+
numspecs = xmakeglyphfontspecs(&spec, &g, 1, x, y);
xdrawglyphfontspecs(&spec, g, numspecs, x, y);
}
{
static int oldx = 0, oldy = 0;
int curx;
- Glyph g = {' ', ATTR_NULL, defaultbg, defaultcs};
+ Glyph g = {' ', ATTR_NULL, defaultbg, defaultcs}, og;
+ int ena_sel = sel.ob.x != -1 && sel.alt == IS_SET(MODE_ALTSCREEN);
+ Color drawcol;
LIMIT(oldx, 0, term.col-1);
LIMIT(oldy, 0, term.row-1);
if (term.line[term.c.y][curx].mode & ATTR_WDUMMY)
curx--;
+ /* remove the old cursor */
+ og = term.line[oldy][oldx];
+ if (ena_sel && selected(oldx, oldy))
+ og.mode ^= ATTR_REVERSE;
+ xdrawglyph(og, oldx, oldy);
+
g.u = term.line[term.c.y][term.c.x].u;
- /* remove the old cursor */
- xdrawglyph(term.line[oldy][oldx], oldx, oldy);
+ /*
+ * Select the right color for the right mode.
+ */
+ if (IS_SET(MODE_REVERSE)) {
+ g.mode |= ATTR_REVERSE;
+ g.bg = defaultfg;
+ if (ena_sel && selected(term.c.x, term.c.y)) {
+ drawcol = dc.col[defaultcs];
+ g.fg = defaultrcs;
+ } else {
+ drawcol = dc.col[defaultrcs];
+ g.fg = defaultcs;
+ }
+ } else {
+ if (ena_sel && selected(term.c.x, term.c.y)) {
+ drawcol = dc.col[defaultrcs];
+ g.fg = defaultfg;
+ g.bg = defaultrcs;
+ } else {
+ drawcol = dc.col[defaultcs];
+ }
+ }
if (IS_SET(MODE_HIDE))
return;
/* draw the new one */
if (xw.state & WIN_FOCUSED) {
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;
- }
-
- g.mode |= term.line[term.c.y][curx].mode & ATTR_WIDE;
- xdrawglyph(g, term.c.x, term.c.y);
- 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 - 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,
- cursorthickness, xw.ch);
- break;
+ case 7: /* st extension: snowman */
+ utf8decode("☃", &g.u, UTF_SIZ);
+ case 0: /* Blinking Block */
+ case 1: /* Blinking Block (Default) */
+ case 2: /* Steady Block */
+ g.mode |= term.line[term.c.y][curx].mode & ATTR_WIDE;
+ xdrawglyph(g, term.c.x, term.c.y);
+ break;
+ case 3: /* Blinking Underline */
+ case 4: /* Steady Underline */
+ XftDrawRect(xw.draw, &drawcol,
+ borderpx + curx * xw.cw,
+ borderpx + (term.c.y + 1) * xw.ch - \
+ cursorthickness,
+ xw.cw, cursorthickness);
+ break;
+ case 5: /* Blinking bar */
+ case 6: /* Steady bar */
+ XftDrawRect(xw.draw, &drawcol,
+ borderpx + curx * xw.cw,
+ borderpx + term.c.y * xw.ch,
+ cursorthickness, xw.ch);
+ break;
}
} else {
- XftDrawRect(xw.draw, &dc.col[defaultcs],
+ XftDrawRect(xw.draw, &drawcol,
borderpx + curx * xw.cw,
borderpx + term.c.y * xw.ch,
xw.cw - 1, 1);
- XftDrawRect(xw.draw, &dc.col[defaultcs],
+ XftDrawRect(xw.draw, &drawcol,
borderpx + curx * xw.cw,
borderpx + term.c.y * xw.ch,
1, xw.ch - 1);
- XftDrawRect(xw.draw, &dc.col[defaultcs],
+ XftDrawRect(xw.draw, &drawcol,
borderpx + (curx + 1) * xw.cw - 1,
borderpx + term.c.y * xw.ch,
1, xw.ch - 1);
- XftDrawRect(xw.draw, &dc.col[defaultcs],
+ XftDrawRect(xw.draw, &drawcol,
borderpx + curx * xw.cw,
borderpx + (term.c.y + 1) * xw.ch - 1,
xw.cw, 1);
{
int i, x, y, ox, numspecs;
Glyph base, new;
- XftGlyphFontSpec* specs;
+ XftGlyphFontSpec *specs;
int ena_sel = sel.ob.x != -1 && sel.alt == IS_SET(MODE_ALTSCREEN);
if (!(xw.state & WIN_VISIBLE))
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
do {
XNextEvent(xw.dpy, &ev);
/*
- * XFilterEvent is required to be called after you using XOpenIM,
- * this is not unnecessary.It does not only filter the key event,
- * but some clientmessage for input method as well.
+ * This XFilterEvent call is required because of XOpenIM. It
+ * does filter out the key event and some client message for
+ * the input method too.
*/
if (XFilterEvent(&ev, None))
continue;
}
} 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] [-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);
+ 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
xw.l = xw.t = 0;
xw.isfixed = False;
- xw.cursor = 0;
+ xw.cursor = cursorshape;
ARGBEGIN {
case 'a':
case 'l':
opt_line = EARGF(usage());
break;
+ case 'n':
+ opt_name = EARGF(usage());
+ break;
case 't':
+ case 'T':
opt_title = EARGF(usage());
break;
case 'w':
opt_embed = EARGF(usage());
break;
case 'v':
+ die("%s " VERSION " (c) 2010-2016 st engineers\n", argv0);
+ break;
default:
usage();
} ARGEND;
return 0;
}
+