#include <fcntl.h>
#include <limits.h>
#include <locale.h>
+#include <pwd.h>
#include <stdarg.h>
#include <stdbool.h>
#include <stdio.h>
#define REDRAW_TIMEOUT (80*1000) /* 80 ms */
+/* macros */
+#define CLEANMASK(mask) (mask & (ShiftMask|ControlMask|Mod1Mask|Mod2Mask|Mod3Mask|Mod4Mask|Mod5Mask))
#define SERRNO strerror(errno)
#define MIN(a, b) ((a) < (b) ? (a) : (b))
#define MAX(a, b) ((a) < (b) ? (b) : (a))
ESC_STR = 4, /* DSC, OSC, PM, APC */
ESC_ALTCHARSET = 8,
ESC_STR_END = 16, /* a final string was encountered */
+ ESC_TEST = 32, /* Enter in test mode */
};
enum window_state {
struct timeval tclick2;
} Selection;
+typedef union {
+ int i;
+ unsigned int ui;
+ float f;
+ const void *v;
+} Arg;
+
+typedef struct {
+ unsigned int mod;
+ KeySym keysym;
+ void (*func)(const Arg *);
+ const Arg arg;
+} Shortcut;
+
+/* function definitions used in config.h */
+static void xzoom(const Arg *);
+
+/* Config.h for applying patches and the configuration. */
#include "config.h"
/* Font structure */
static void tscrollup(int, int);
static void tscrolldown(int, int);
static void tsetattr(int*, int);
-static void tsetchar(char*);
+static void tsetchar(char *, Glyph *, int, int);
static void tsetscroll(int, int);
static void tswapscreen(void);
static void tsetdirt(int, int);
static char *kmap(KeySym, uint);
static void kpress(XEvent *);
static void cmessage(XEvent *);
+static void cresize(int width, int height);
static void resize(XEvent *);
static void focus(XEvent *);
static void brelease(XEvent *);
static int utf8size(char *);
static int isfullutf8(char *, int);
+static ssize_t xwrite(int, char *, size_t);
static void *xmalloc(size_t);
static void *xrealloc(void *, size_t);
static void *xcalloc(size_t nmemb, size_t size);
-static char *smstrcat(char *, ...);
static void (*handler[LASTEvent])(XEvent *) = {
[KeyPress] = kpress,
static char *opt_class = NULL;
static char *opt_font = NULL;
+static char *usedfont = NULL;
+static int usedfontsize = 0;
+
+ssize_t
+xwrite(int fd, char *s, size_t len) {
+ size_t aux = len;
+
+ while(len > 0) {
+ ssize_t r = write(fd, s, len);
+ if(r < 0)
+ return r;
+ len -= r;
+ s += r;
+ }
+ return aux;
+}
+
void *
xmalloc(size_t len) {
void *p = malloc(len);
return p;
}
-char *
-smstrcat(char *src, ...)
-{
- va_list fmtargs;
- char *ret, *p, *v;
- int len, slen, flen;
-
- len = slen = strlen(src);
-
- va_start(fmtargs, src);
- for(;;) {
- v = va_arg(fmtargs, char *);
- if(v == NULL)
- break;
- len += strlen(v);
- }
- va_end(fmtargs);
-
- p = ret = xmalloc(len+1);
- memmove(p, src, slen);
- p += slen;
-
- va_start(fmtargs, src);
- for(;;) {
- v = va_arg(fmtargs, char *);
- if(v == NULL)
- break;
- flen = strlen(v);
- memmove(p, v, flen);
- p += flen;
- }
- va_end(fmtargs);
-
- ret[len] = '\0';
-
- return ret;
-}
-
int
utf8decode(char *s, long *u) {
uchar c;
selcopy(void) {
char *str, *ptr, *p;
int x, y, bufsize, is_selected = 0, size;
- Glyph *gp;
+ Glyph *gp, *last;
if(sel.bx == -1) {
str = NULL;
/* append every set & selected glyph to the selection */
for(y = 0; y < term.row; y++) {
- for(x = 0; x < term.col; x++) {
- gp = &term.line[y][x];
+ gp = &term.line[y][0];
+ last = gp + term.col;
+ while(--last >= gp && !(last->state & GLYPH_SET))
+ /* nothing */;
+
+ for(x = 0; gp <= last; x++, ++gp) {
if(!(is_selected = selected(x, y)))
continue;
+
p = (gp->state & GLYPH_SET) ? gp->c : " ";
size = utf8size(p);
memcpy(ptr, p, size);
execsh(void) {
char **args;
char *envshell = getenv("SHELL");
+ const struct passwd *pass = getpwuid(getuid());
+ char buf[sizeof(long) * 8 + 1];
unsetenv("COLUMNS");
unsetenv("LINES");
unsetenv("TERMCAP");
+ if(pass) {
+ setenv("LOGNAME", pass->pw_name, 1);
+ setenv("USER", pass->pw_name, 1);
+ setenv("SHELL", pass->pw_shell, 0);
+ setenv("HOME", pass->pw_dir, 0);
+ }
+
+ snprintf(buf, sizeof(buf), "%lu", xw.win);
+ setenv("WINDOWID", buf, 1);
+
signal(SIGCHLD, SIG_DFL);
signal(SIGHUP, SIG_DFL);
signal(SIGINT, SIG_DFL);
DEFAULT(envshell, SHELL);
putenv("TERM="TNAME);
- args = opt_cmd ? opt_cmd : (char*[]){envshell, "-i", NULL};
+ args = opt_cmd ? opt_cmd : (char *[]){envshell, "-i", NULL};
execvp(args[0], args);
exit(EXIT_FAILURE);
}
cmdfd = m;
signal(SIGCHLD, sigchld);
if(opt_io) {
- if(!strcmp(opt_io, "-")) {
- iofd = STDOUT_FILENO;
- } else {
- if((iofd = open(opt_io, O_WRONLY | O_CREAT, 0666)) < 0) {
- fprintf(stderr, "Error opening %s:%s\n",
- opt_io, strerror(errno));
- }
+ iofd = (!strcmp(opt_io, "-")) ?
+ STDOUT_FILENO :
+ open(opt_io, O_WRONLY | O_CREAT, 0666);
+ if(iofd < 0) {
+ fprintf(stderr, "Error opening %s:%s\n",
+ opt_io, strerror(errno));
}
}
}
}
void
-tsetchar(char *c) {
- char *vt100_0[62] = { /* 0x41 - 0x7e */
+tsetchar(char *c, Glyph *attr, int x, int y) {
+ static char *vt100_0[62] = { /* 0x41 - 0x7e */
"↑", "↓", "→", "←", "█", "▚", "☃", /* A - G */
0, 0, 0, 0, 0, 0, 0, 0, /* H - O */
0, 0, 0, 0, 0, 0, 0, 0, /* P - W */
/*
* The table is proudly stolen from rxvt.
*/
- if(term.c.attr.mode & ATTR_GFX) {
+ if(attr->mode & ATTR_GFX) {
if(c[0] >= 0x41 && c[0] <= 0x7e
&& vt100_0[c[0] - 0x41]) {
c = vt100_0[c[0] - 0x41];
}
}
- term.dirty[term.c.y] = 1;
- term.line[term.c.y][term.c.x] = term.c.attr;
- memcpy(term.line[term.c.y][term.c.x].c, c, UTF_SIZ);
- term.line[term.c.y][term.c.x].state |= GLYPH_SET;
+ term.dirty[y] = 1;
+ term.line[y][x] = *attr;
+ memcpy(term.line[y][x].c, c, UTF_SIZ);
+ term.line[y][x].state |= GLYPH_SET;
}
void
uchar ascii = *c;
bool control = ascii < '\x20' || ascii == 0177;
- if(iofd != -1)
- write(iofd, c, len);
+ if(iofd != -1) {
+ if (xwrite(iofd, c, len) < 0) {
+ fprintf(stderr, "Error writting in %s:%s\n",
+ opt_io, strerror(errno));
+ close(iofd);
+ iofd = -1;
+ }
+ }
/*
* STR sequences must be checked before of anything
* because it can use some control codes as part of the sequence
fprintf(stderr, "esc unhandled charset: ESC ( %c\n", ascii);
}
term.esc = 0;
+ } else if(term.esc & ESC_TEST) {
+ if(ascii == '8') { /* DEC screen alignment test. */
+ char E[UTF_SIZ] = "E";
+ int x, y;
+
+ for(x = 0; x < term.col; ++x) {
+ for(y = 0; y < term.row; ++y)
+ tsetchar(E, &term.c.attr, x, y);
+ }
+ }
+ term.esc = 0;
} else {
switch(ascii) {
case '[':
term.esc |= ESC_CSI;
break;
+ case '#':
+ term.esc |= ESC_TEST;
+ break;
case 'P': /* DCS -- Device Control String */
case '_': /* APC -- Application Program Command */
case '^': /* PM -- Privacy Message */
sel.bx = -1;
if(IS_SET(MODE_WRAP) && term.c.state & CURSOR_WRAPNEXT)
tnewline(1); /* always go to first col */
- tsetchar(c);
+ tsetchar(c, &term.c.attr, term.c.x, term.c.y);
if(term.c.x+1 < term.col)
tmoveto(term.c.x+1, term.c.y);
else
*bp = 1;
}
/* update terminal size */
- term.col = col, term.row = row;
+ term.col = col;
+ term.row = row;
/* make use of the LIMIT in tmoveto */
tmoveto(term.c.x, term.c.y);
/* reset scrolling region */
XFree(sizeh);
}
-void
-xinitfont(Font *f, char *fontstr) {
- FcPattern *pattern, *match;
+int
+xloadfont(Font *f, FcPattern *pattern) {
+ FcPattern *match;
FcResult result;
- pattern = FcNameParse((FcChar8 *)fontstr);
- if(!pattern)
- die("st: can't open font %s\n", fontstr);
-
match = XftFontMatch(xw.dpy, xw.scr, pattern, &result);
- FcPatternDestroy(pattern);
if(!match)
- die("st: can't open font %s\n", fontstr);
+ return 1;
if(!(f->xft_set = XftFontOpenPattern(xw.dpy, match))) {
FcPatternDestroy(match);
- die("st: can't open font %s.\n", fontstr);
+ return 1;
}
f->ascent = f->xft_set->ascent;
f->height = f->xft_set->height;
f->width = f->lbearing + f->rbearing;
+
+ return 0;
}
void
-initfonts(char *fontstr) {
- char *fstr;
+xloadfonts(char *fontstr, int fontsize) {
+ FcPattern *pattern;
+ FcResult result;
+ double fontval;
- xinitfont(&dc.font, fontstr);
+ pattern = FcNameParse((FcChar8 *)fontstr);
+ if(!pattern)
+ die("st: can't open font %s\n", fontstr);
+
+ if(fontsize > 0) {
+ FcPatternDel(pattern, FC_PIXEL_SIZE);
+ FcPatternAddDouble(pattern, FC_PIXEL_SIZE, (double)fontsize);
+ usedfontsize = fontsize;
+ } else {
+ result = FcPatternGetDouble(pattern, FC_PIXEL_SIZE, 0, &fontval);
+ if(result == FcResultMatch) {
+ usedfontsize = (int)fontval;
+ } else {
+ /*
+ * Default font size is 12, if none given. This is to
+ * have a known usedfontsize value.
+ */
+ FcPatternAddDouble(pattern, FC_PIXEL_SIZE, 12);
+ usedfontsize = 12;
+ }
+ }
+
+ if(xloadfont(&dc.font, pattern))
+ die("st: can't open font %s\n", fontstr);
+
+ /* Setting character width and height. */
xw.cw = dc.font.width;
xw.ch = dc.font.height;
- fstr = smstrcat(fontstr, ":weight=bold", NULL);
- xinitfont(&dc.bfont, fstr);
- free(fstr);
+ FcPatternDel(pattern, FC_WEIGHT);
+ FcPatternAddInteger(pattern, FC_WEIGHT, FC_WEIGHT_BOLD);
+ if(xloadfont(&dc.bfont, pattern))
+ die("st: can't open font %s\n", fontstr);
- fstr = smstrcat(fontstr, ":slant=italic,oblique", NULL);
- xinitfont(&dc.ifont, fstr);
- free(fstr);
+ FcPatternDel(pattern, FC_SLANT);
+ FcPatternAddInteger(pattern, FC_SLANT, FC_SLANT_ITALIC);
+ if(xloadfont(&dc.ibfont, pattern))
+ die("st: can't open font %s\n", fontstr);
+
+ FcPatternDel(pattern, FC_WEIGHT);
+ if(xloadfont(&dc.ifont, pattern))
+ die("st: can't open font %s\n", fontstr);
- fstr = smstrcat(fontstr, ":weight=bold:slant=italic,oblique", NULL);
- xinitfont(&dc.ibfont, fstr);
- free(fstr);
+ FcPatternDestroy(pattern);
+}
+
+void
+xzoom(const Arg *arg)
+{
+ xloadfonts(usedfont, usedfontsize + arg->i);
+ cresize(0, 0);
+ draw();
}
void
xw.vis = XDefaultVisual(xw.dpy, xw.scr);
/* font */
- initfonts((opt_font != NULL)? opt_font : FONT);
+ usedfont = (opt_font == NULL)? FONT : opt_font;
+ xloadfonts(usedfont, 0);
/* colors */
xw.cmap = XDefaultColormap(xw.dpy, xw.scr);
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;
}
}
kpress(XEvent *ev) {
XKeyEvent *e = &ev->xkey;
KeySym ksym;
- char buf[32];
- char *customkey;
- int len;
- int meta;
- int shift;
+ char buf[32], *customkey;
+ int len, meta, shift, i;
Status status;
if (IS_SET(MODE_KBDLOCK))
shift = e->state & ShiftMask;
len = XmbLookupString(xw.xic, e, buf, sizeof(buf), &ksym, &status);
- /* 1. custom keys from config.h */
+ /* 1. shortcuts */
+ for(i = 0; i < LEN(shortcuts); i++) {
+ if((ksym == shortcuts[i].keysym)
+ && (CLEANMASK(shortcuts[i].mod) == \
+ CLEANMASK(e->state))
+ && shortcuts[i].func) {
+ shortcuts[i].func(&(shortcuts[i].arg));
+ }
+ }
+
+ /* 2. custom keys from config.h */
if((customkey = kmap(ksym, e->state))) {
ttywrite(customkey, strlen(customkey));
/* 2. hardcoded (overrides X lookup) */
}
void
-resize(XEvent *e) {
+cresize(int width, int height)
+{
int col, row;
- if(e->xconfigure.width == xw.w && e->xconfigure.height == xw.h)
- return;
+ if(width != 0)
+ xw.w = width;
+ if(height != 0)
+ xw.h = height;
- xw.w = e->xconfigure.width;
- xw.h = e->xconfigure.height;
col = (xw.w - 2*BORDER) / xw.cw;
row = (xw.h - 2*BORDER) / xw.ch;
if(col == term.col && row == term.row)
ttyresize();
}
+void
+resize(XEvent *e) {
+ if(e->xconfigure.width == xw.w && e->xconfigure.height == xw.h)
+ return;
+
+ cresize(e->xconfigure.width, e->xconfigure.height);
+}
+
void
run(void) {
XEvent ev;
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);
- ttynew();
xinit();
+ ttynew();
selinit();
run();
return 0;