#include <X11/cursorfont.h>
#include <X11/keysym.h>
#include <X11/Xft/Xft.h>
+#include <X11/XKBlib.h>
#include <fontconfig/fontconfig.h>
#include <wchar.h>
#define XK_NO_MOD 0
#define XK_SWITCH_MOD (1<<13)
-#define REDRAW_TIMEOUT (80*1000) /* 80 ms */
-
/* macros */
#define MIN(a, b) ((a) < (b) ? (a) : (b))
#define MAX(a, b) ((a) < (b) ? (b) : (a))
static void numlock(const Arg *);
static void selpaste(const Arg *);
static void xzoom(const Arg *);
+static void xzoomabs(const Arg *);
+static void xzoomreset(const Arg *);
static void printsel(const Arg *);
static void printscreen(const Arg *) ;
static void toggleprinter(const Arg *);
static void die(const char *, ...);
static void draw(void);
-static void redraw(int);
+static void redraw(void);
static void drawregion(int, int, int, int);
static void execsh(void);
static void sigchld(int);
static char *usedfont = NULL;
static double usedfontsize = 0;
+static double defaultfontsize = 0;
static uchar utfbyte[UTF_SIZ + 1] = {0x80, 0, 0xC0, 0xE0, 0xF0};
static uchar utfmask[UTF_SIZ + 1] = {0xC0, 0x80, 0xE0, 0xF0, 0xF8};
typedef struct {
XftFont *font;
int flags;
+ long unicodep;
} Fontcache;
/* Fontcache is an array now. A new font will be appended to the array. */
static int tlinelen(int y) {
int i = term.col;
- while (i > 0 && term.line[y][i - 1].c[0] == ' ')
+ if(term.line[y][i - 1].mode & ATTR_WRAP)
+ return i;
+
+ while(i > 0 && term.line[y][i - 1].c[0] == ' ')
--i;
return i;
lastx = (sel.ne.y == y) ? sel.ne.x : term.col-1;
}
last = &term.line[y][MIN(lastx, linelen-1)];
+ while(last >= gp && last->c[0] == ' ')
+ --last;
for( ; gp <= last; ++gp) {
if(gp->mode & ATTR_WDUMMY)
* st.
* FIXME: Fix the computer world.
*/
- if(sel.ne.y > y || lastx >= linelen)
+ if((y < sel.ne.y || lastx >= linelen) && !(last->mode & ATTR_WRAP))
*ptr++ = '\n';
}
*ptr = 0;
void
xsetsel(char *str) {
- /* register the selection for both the clipboard and the primary */
- Atom clipboard;
-
free(sel.clip);
sel.clip = str;
-
XSetSelectionOwner(xw.dpy, XA_PRIMARY, xw.win, CurrentTime);
-
- clipboard = XInternAtom(xw.dpy, "CLIPBOARD", 0);
- XSetSelectionOwner(xw.dpy, clipboard, xw.win, CurrentTime);
}
void
void
execsh(void) {
- char **args, *sh;
+ char **args, *sh, *prog;
const struct passwd *pw;
char buf[sizeof(long) * 8 + 1];
else
die("who are you?\n");
}
- unsetenv("COLUMNS");
- unsetenv("LINES");
- unsetenv("TERMCAP");
- sh = (pw->pw_shell[0]) ? pw->pw_shell : shell;
+ if (!(sh = getenv("SHELL"))) {
+ sh = (pw->pw_shell[0]) ? pw->pw_shell : shell;
+ }
+
+ if(opt_cmd)
+ prog = opt_cmd[0];
+ else if(utmp)
+ prog = utmp;
+ else
+ prog = sh;
+ args = (opt_cmd) ? opt_cmd : (char *[]) {prog, NULL};
+
snprintf(buf, sizeof(buf), "%lu", xw.win);
+ unsetenv("COLUMNS");
+ unsetenv("LINES");
+ unsetenv("TERMCAP");
setenv("LOGNAME", pw->pw_name, 1);
setenv("USER", pw->pw_name, 1);
setenv("SHELL", sh, 1);
signal(SIGTERM, SIG_DFL);
signal(SIGALRM, SIG_DFL);
- args = opt_cmd ? opt_cmd : (char *[]){sh, "-i", NULL};
- execvp(args[0], args);
- exit(EXIT_FAILURE);
+ execvp(prog, args);
+ _exit(EXIT_FAILURE);
}
void
mode = term.mode;
MODBIT(term.mode, set, MODE_REVERSE);
if(mode != term.mode)
- redraw(REDRAW_TIMEOUT);
+ redraw();
break;
case 6: /* DECOM -- Origin */
MODBIT(term.c.state, set, CURSOR_ORIGIN);
* TODO if defaultbg color is changed, borders
* are dirty
*/
- redraw(0);
+ redraw();
}
return;
}
if(!(xw.state & WIN_FOCUSED))
xseturgency(1);
if (bellvolume)
- XBell(xw.dpy, bellvolume);
+ XkbBell(xw.dpy, xw.win, bellvolume, (Atom)NULL);
}
break;
case '\033': /* ESC */
term.esc &= ~(ESC_CSI|ESC_ALTCHARSET|ESC_TEST);
term.esc |= ESC_START;
return;
- case '\016': /* SO */
- term.charset = 0;
- return;
- case '\017': /* SI */
- term.charset = 1;
+ case '\016': /* SO (LS1 -- Locking shift 1) */
+ case '\017': /* SI (LS0 -- Locking shift 0) */
+ term.charset = 1 - (ascii - '\016');
return;
case '\032': /* SUB */
tsetchar(question, &term.c.attr, term.c.x, term.c.y);
case 'k': /* old title set compatibility */
tstrsequence(ascii);
return 0;
- case '(': /* set primary charset G0 */
- case ')': /* set secondary charset G1 */
- case '*': /* set tertiary charset G2 */
- case '+': /* set quaternary charset G3 */
+ case 'n': /* LS2 -- Locking shift 2 */
+ case 'o': /* LS3 -- Locking shift 3 */
+ term.charset = 2 + (ascii - 'n');
+ break;
+ case '(': /* GZD4 -- set primary charset G0 */
+ case ')': /* G1D4 -- set secondary charset G1 */
+ case '*': /* G2D4 -- set tertiary charset G2 */
+ case '+': /* G3D4 -- set quaternary charset G3 */
term.icharset = ascii - '(';
term.esc |= ESC_ALTCHARSET;
return 0;
unicodep = ascii = *c;
} else {
utf8decode(c, &unicodep, UTF_SIZ);
- width = wcwidth(unicodep);
+ if ((width = wcwidth(unicodep)) == -1) {
+ c = "\357\277\275"; /* UTF_INVALID */
+ width = 1;
+ }
control = ISCONTROLC1(unicodep);
ascii = unicodep;
}
if(IS_SET(MODE_WRAP) && (term.c.state & CURSOR_WRAPNEXT)) {
gp->mode |= ATTR_WRAP;
tnewline(1);
+ gp = &term.line[term.c.y][term.c.x];
}
- if(IS_SET(MODE_INSERT) && term.c.x+1 < term.col)
- memmove(gp+1, gp, (term.col - term.c.x - 1) * sizeof(Glyph));
+ if(IS_SET(MODE_INSERT) && term.c.x+width < term.col)
+ memmove(gp+width, gp, (term.col - term.c.x - width) * sizeof(Glyph));
- if(term.c.x+width > term.col)
+ if(term.c.x+width > term.col) {
tnewline(1);
+ gp = &term.line[term.c.y][term.c.x];
+ }
tsetchar(c, &term.c.attr, term.c.x, term.c.y);
if(!pattern)
die("st: can't open font %s\n", fontstr);
- if(fontsize > 0) {
+ if(fontsize > 1) {
FcPatternDel(pattern, FC_PIXEL_SIZE);
FcPatternDel(pattern, FC_SIZE);
FcPatternAddDouble(pattern, FC_PIXEL_SIZE, (double)fontsize);
FcPatternAddDouble(pattern, FC_PIXEL_SIZE, 12);
usedfontsize = 12;
}
+ defaultfontsize = usedfontsize;
}
FcConfigSubstitute(0, pattern, FcMatchPattern);
FcPatternGetDouble(dc.font.match->pattern,
FC_PIXEL_SIZE, 0, &fontval);
usedfontsize = fontval;
+ if(fontsize == 0)
+ defaultfontsize = fontval;
}
/* Setting character width and height. */
void
xzoom(const Arg *arg) {
+ Arg larg;
+
+ larg.i = usedfontsize + arg->i;
+ xzoomabs(&larg);
+}
+
+void
+xzoomabs(const Arg *arg) {
xunloadfonts();
- xloadfonts(usedfont, usedfontsize + arg->i);
+ xloadfonts(usedfont, arg->i);
cresize(0, 0);
- redraw(0);
+ redraw();
xhints();
}
+void
+xzoomreset(const Arg *arg) {
+ Arg larg;
+
+ if(defaultfontsize > 0) {
+ larg.i = defaultfontsize;
+ xzoomabs(&larg);
+ }
+}
+
void
xinit(void) {
XGCValues gcvalues;
| ButtonMotionMask | ButtonPressMask | ButtonReleaseMask;
xw.attrs.colormap = xw.cmap;
- parent = opt_embed ? strtol(opt_embed, NULL, 0) : \
- XRootWindow(xw.dpy, xw.scr);
+ if (!(opt_embed && (parent = strtol(opt_embed, NULL, 0))))
+ parent = XRootWindow(xw.dpy, xw.scr);
xw.win = XCreateWindow(xw.dpy, parent, xw.l, xw.t,
xw.w, xw.h, 0, XDefaultDepth(xw.dpy, xw.scr), InputOutput,
xw.vis, CWBackPixel | CWBorderPixel | CWBitGravity
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++;
xdraws(term.line[oldy][oldx].c, term.line[oldy][oldx], oldx,
oldy, width, sl);
- /* draw the new one */
- if(!(IS_SET(MODE_HIDE))) {
- if(xw.state & WIN_FOCUSED) {
- if(IS_SET(MODE_REVERSE)) {
- g.mode |= ATTR_REVERSE;
- g.fg = defaultcs;
- g.bg = defaultfg;
- }
+ if(IS_SET(MODE_HIDE))
+ return;
- 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);
- } else {
- XftDrawRect(xw.draw, &dc.col[defaultcs],
- borderpx + curx * xw.cw,
- borderpx + term.c.y * xw.ch,
- xw.cw - 1, 1);
- XftDrawRect(xw.draw, &dc.col[defaultcs],
- borderpx + curx * xw.cw,
- borderpx + term.c.y * xw.ch,
- 1, xw.ch - 1);
- XftDrawRect(xw.draw, &dc.col[defaultcs],
- borderpx + (curx + 1) * xw.cw - 1,
- borderpx + term.c.y * xw.ch,
- 1, xw.ch - 1);
- XftDrawRect(xw.draw, &dc.col[defaultcs],
- borderpx + curx * xw.cw,
- borderpx + (term.c.y + 1) * xw.ch - 1,
- xw.cw, 1);
+ /* draw the new one */
+ if(xw.state & WIN_FOCUSED) {
+ if(IS_SET(MODE_REVERSE)) {
+ g.mode |= ATTR_REVERSE;
+ g.fg = defaultcs;
+ g.bg = defaultfg;
}
- oldx = curx, oldy = term.c.y;
+
+ 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);
+ } else {
+ XftDrawRect(xw.draw, &dc.col[defaultcs],
+ borderpx + curx * xw.cw,
+ borderpx + term.c.y * xw.ch,
+ xw.cw - 1, 1);
+ XftDrawRect(xw.draw, &dc.col[defaultcs],
+ borderpx + curx * xw.cw,
+ borderpx + term.c.y * xw.ch,
+ 1, xw.ch - 1);
+ XftDrawRect(xw.draw, &dc.col[defaultcs],
+ borderpx + (curx + 1) * xw.cw - 1,
+ borderpx + term.c.y * xw.ch,
+ 1, xw.ch - 1);
+ XftDrawRect(xw.draw, &dc.col[defaultcs],
+ borderpx + curx * xw.cw,
+ borderpx + (term.c.y + 1) * xw.ch - 1,
+ xw.cw, 1);
}
+ oldx = curx, oldy = term.c.y;
}
}
void
-redraw(int timeout) {
- struct timespec tv = {0, timeout * 1000};
-
+redraw(void) {
tfulldirt();
draw();
-
- if(timeout > 0) {
- nanosleep(&tv, NULL);
- XSync(xw.dpy, False); /* necessary for a good tput flash */
- }
}
void
if(!e->count)
xw.state &= ~WIN_REDRAW;
}
- redraw(0);
+ redraw();
}
void
TIMEDIFF(now,
lastblink)));
}
+ drawtimeout.tv_sec = \
+ drawtimeout.tv_nsec / 1E9;
+ drawtimeout.tv_nsec %= (long)1E9;
} else {
tv = NULL;
}
void
usage(void) {
- die("%s " VERSION " (c) 2010-2014 st engineers\n" \
+ 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 ...]\n", argv0);
}