X-Git-Url: https://jasonwoof.com/gitweb/?p=st.git;a=blobdiff_plain;f=st.c;h=4a505b528fb562e3487402bbd77675852062cbc2;hp=2e559634902cf10905d511d0bcd7781ec6b561df;hb=d945d7093d1822bf249e2e38ce97e5b37f695fdb;hpb=bb6dc332067fddcdcb0940ebc9b5f41c61429c46 diff --git a/st.c b/st.c index 2e55963..4a505b5 100644 --- a/st.c +++ b/st.c @@ -27,6 +27,7 @@ #include #include #include +#include #include #include @@ -62,22 +63,19 @@ char *argv0; #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)) #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)) +#define ISCONTROLC0(c) (BETWEEN(c, 0, 0x1f) || (c) == '\177') #define ISCONTROLC1(c) (BETWEEN(c, 0x80, 0x9f)) #define ISCONTROL(c) (ISCONTROLC0(c) || ISCONTROLC1(c)) #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_usec-t2.tv_usec)/1000) -#define CEIL(x) (((x) != (int) (x)) ? (x) + 1 : (x)) +#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)) @@ -85,20 +83,23 @@ char *argv0; #define TRUERED(x) (((x) & 0xff0000) >> 8) #define TRUEGREEN(x) (((x) & 0xff00)) #define TRUEBLUE(x) (((x) & 0xff) << 8) +#define TLINE(y) ((y) < term.scr ? term.hist[((y) + term.histi - term.scr \ + + histsize + 1) % histsize] : term.line[(y) - term.scr]) -#define VT102ID "\033[?6c" - enum glyph_attribute { ATTR_NULL = 0, - ATTR_REVERSE = 1, - ATTR_UNDERLINE = 2, - ATTR_BOLD = 4, - ATTR_ITALIC = 8, - ATTR_BLINK = 16, - ATTR_WRAP = 32, - ATTR_WIDE = 64, - ATTR_WDUMMY = 128, + ATTR_BOLD = 1 << 0, + ATTR_FAINT = 1 << 1, + ATTR_ITALIC = 1 << 2, + ATTR_UNDERLINE = 1 << 3, + ATTR_BLINK = 1 << 4, + ATTR_REVERSE = 1 << 5, + ATTR_INVISIBLE = 1 << 6, + ATTR_STRUCK = 1 << 7, + ATTR_WRAP = 1 << 8, + ATTR_WIDE = 1 << 9, + ATTR_WDUMMY = 1 << 10, }; enum cursor_movement { @@ -113,27 +114,27 @@ enum cursor_state { }; enum term_mode { - MODE_WRAP = 1, - MODE_INSERT = 2, - MODE_APPKEYPAD = 4, - MODE_ALTSCREEN = 8, - MODE_CRLF = 16, - MODE_MOUSEBTN = 32, - MODE_MOUSEMOTION = 64, - MODE_REVERSE = 128, - MODE_KBDLOCK = 256, - MODE_HIDE = 512, - MODE_ECHO = 1024, - MODE_APPCURSOR = 2048, - MODE_MOUSESGR = 4096, - MODE_8BIT = 8192, - MODE_BLINK = 16384, - MODE_FBLINK = 32768, - MODE_FOCUS = 65536, - MODE_MOUSEX10 = 131072, - MODE_MOUSEMANY = 262144, - MODE_BRCKTPASTE = 524288, - MODE_PRINT = 1048576, + MODE_WRAP = 1 << 0, + MODE_INSERT = 1 << 1, + MODE_APPKEYPAD = 1 << 2, + MODE_ALTSCREEN = 1 << 3, + MODE_CRLF = 1 << 4, + MODE_MOUSEBTN = 1 << 5, + MODE_MOUSEMOTION = 1 << 6, + MODE_REVERSE = 1 << 7, + MODE_KBDLOCK = 1 << 8, + MODE_HIDE = 1 << 9, + MODE_ECHO = 1 << 10, + MODE_APPCURSOR = 1 << 11, + MODE_MOUSESGR = 1 << 12, + MODE_8BIT = 1 << 13, + MODE_BLINK = 1 << 14, + MODE_FBLINK = 1 << 15, + MODE_FOCUS = 1 << 16, + MODE_MOUSEX10 = 1 << 17, + MODE_MOUSEMANY = 1 << 18, + MODE_BRCKTPASTE = 1 << 19, + MODE_PRINT = 1 << 20, MODE_MOUSE = MODE_MOUSEBTN|MODE_MOUSEMOTION|MODE_MOUSEX10\ |MODE_MOUSEMANY, }; @@ -180,7 +181,6 @@ typedef unsigned short ushort; typedef XftDraw *Draw; typedef XftColor Color; -typedef Colormap Colormap; typedef struct { char c[UTF_SIZ]; /* character code */ @@ -199,14 +199,14 @@ typedef struct { } TCursor; /* CSI Escape sequence structs */ -/* ESC '[' [[ [] [;]] ] */ +/* ESC '[' [[ [] [;]] []] */ typedef struct { char buf[ESC_BUF_SIZ]; /* raw string */ int len; /* raw string length */ char priv; int arg[ESC_ARG_SIZ]; int narg; /* nb of args */ - char mode; + char mode[2]; } CSIEscape; /* STR Escape sequence structs */ @@ -225,6 +225,9 @@ typedef struct { int col; /* nb col */ Line *line; /* screen */ Line *alt; /* alternate screen */ + Line *hist; /* history buffer */ + int histi; /* history index */ + int scr; /* scroll back */ bool *dirty; /* dirtyness of lines */ TCursor c; /* cursor */ int top; /* top scroll limit */ @@ -259,6 +262,7 @@ typedef struct { int ch; /* char height */ int cw; /* char width */ char state; /* focus, redraw, visible */ + int cursor; /* cursor style */ } XWindow; typedef struct { @@ -292,11 +296,11 @@ typedef struct { int x, y; } nb, ne, ob, oe; - char *clip; + char *primary, *clipboard; Atom xtarget; bool alt; - struct timeval tclick1; - struct timeval tclick2; + struct timespec tclick1; + struct timespec tclick2; } Selection; typedef union { @@ -314,10 +318,15 @@ typedef struct { } Shortcut; /* function definitions used in config.h */ +static void clipcopy(const Arg *); static void clippaste(const Arg *); +static void kscrolldown(const Arg *); +static void kscrollup(const Arg *); 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 *); @@ -347,7 +356,7 @@ typedef struct { 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); @@ -357,6 +366,7 @@ static void csidump(void); static void csihandle(void); static void csiparse(void); static void csireset(void); +static int eschandle(uchar ascii); static void strdump(void); static void strhandle(void); static void strparse(void); @@ -373,6 +383,7 @@ static void tdeletechar(int); static void tdeleteline(int); static void tinsertblank(int); static void tinsertblankline(int); +static int tlinelen(int); static void tmoveto(int, int); static void tmoveato(int, int); static void tnew(int, int); @@ -380,9 +391,9 @@ static void tnewline(int); static void tputtab(int); static void tputc(char *, int); static void treset(void); -static int tresize(int, int); -static void tscrollup(int, int); -static void tscrolldown(int, int); +static void tresize(int, int); +static void tscrollup(int, int, bool); +static void tscrolldown(int, int, bool); static void tsetattr(int *, int); static void tsetchar(char *, Glyph *, int, int); static void tsetscroll(int, int); @@ -402,6 +413,7 @@ static void ttyread(void); static void ttyresize(void); static void ttysend(char *, size_t); static void ttywrite(const char *, size_t); +static void tstrsequence(uchar c); static void xdraws(char *, Glyph, int, int, int, int); static void xhints(void); @@ -476,7 +488,11 @@ static void (*handler[LASTEvent])(XEvent *) = { [MotionNotify] = bmotion, [ButtonPress] = bpress, [ButtonRelease] = brelease, - [SelectionClear] = selclear, +/* + * Uncomment if you want the selection to disappear when you select something + * different in another window. + */ +/* [SelectionClear] = selclear, */ [SelectionNotify] = selnotify, [SelectionRequest] = selrequest, }; @@ -501,6 +517,7 @@ static int oldbutton = 3; /* button event on startup: 3 = release */ 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}; @@ -518,6 +535,7 @@ enum { typedef struct { XftFont *font; int flags; + long unicodep; } Fontcache; /* Fontcache is an array now. A new font will be appended to the array. */ @@ -631,11 +649,13 @@ utf8validate(long *u, size_t i) { static 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 = 0; + sel.snap = 0; sel.ob.x = -1; - sel.clip = NULL; + sel.primary = NULL; + sel.clipboard = NULL; sel.xtarget = XInternAtom(xw.dpy, "UTF8_STRING", 0); if(sel.xtarget == None) sel.xtarget = XA_STRING; @@ -660,7 +680,10 @@ y2row(int y) { static int tlinelen(int y) { int i = term.col; - while (i > 0 && term.line[y][i - 1].c[0] == ' ') + if(TLINE(y)[i - 1].mode & ATTR_WRAP) + return i; + + while(i > 0 && TLINE(y)[i - 1].c[0] == ' ') --i; return i; @@ -670,7 +693,7 @@ static void selnormalize(void) { int i; - if(sel.ob.y == sel.oe.y) { + if(sel.ob.y == sel.oe.y || sel.type == SEL_RECTANGULAR) { sel.nb.x = MIN(sel.ob.x, sel.oe.x); sel.ne.x = MAX(sel.ob.x, sel.oe.x); } else { @@ -680,6 +703,9 @@ selnormalize(void) { sel.nb.y = MIN(sel.ob.y, sel.oe.y); sel.ne.y = MAX(sel.ob.y, sel.oe.y); + selsnap(sel.snap, &sel.nb.x, &sel.nb.y, -1); + selsnap(sel.snap, &sel.ne.x, &sel.ne.y, +1); + /* expand selection over line breaks */ if (sel.type == SEL_RECTANGULAR) return; @@ -703,43 +729,48 @@ selected(int x, int y) { void selsnap(int mode, int *x, int *y, int direction) { + int newx, newy, xt, yt; + bool delim, prevdelim; + Glyph *gp, *prevgp; + switch(mode) { case SNAP_WORD: /* * Snap around if the word wraps around at the end or * beginning of a line. */ + prevgp = &TLINE(*y)[*x]; + prevdelim = strchr(worddelimiters, prevgp->c[0]) != NULL; for(;;) { - if(direction < 0 && *x <= 0) { - if(*y > 0 && term.line[*y - 1][term.col-1].mode - & ATTR_WRAP) { - *y -= 1; - *x = term.col-1; - } else { + newx = *x + direction; + newy = *y; + if(!BETWEEN(newx, 0, term.col - 1)) { + newy += direction; + newx = (newx + term.col) % term.col; + if (!BETWEEN(newy, 0, term.row - 1)) break; - } - } - if(direction > 0 && *x >= term.col-1) { - if(*y < term.row-1 && term.line[*y][*x].mode - & ATTR_WRAP) { - *y += 1; - *x = 0; - } else { + + if(direction > 0) + yt = *y, xt = *x; + else + yt = newy, xt = newx; + if(!(TLINE(yt)[xt].mode & ATTR_WRAP)) break; - } } - if(term.line[*y][*x+direction].mode & ATTR_WDUMMY) { - *x += direction; - continue; - } + if (newx >= tlinelen(newy)) + break; - if(*x >= tlinelen(*y) || strchr(worddelimiters, - term.line[*y][*x+direction].c[0])) { + gp = &TLINE(newy)[newx]; + delim = strchr(worddelimiters, gp->c[0]) != NULL; + if(!(gp->mode & ATTR_WDUMMY) && (delim != prevdelim + || (delim && gp->c[0] != prevgp->c[0]))) break; - } - *x += direction; + *x = newx; + *y = newy; + prevgp = gp; + prevdelim = delim; } break; case SNAP_LINE: @@ -751,14 +782,14 @@ selsnap(int mode, int *x, int *y, int direction) { *x = (direction < 0) ? 0 : term.col - 1; if(direction < 0 && *y > 0) { for(; *y > 0; *y += direction) { - if(!(term.line[*y-1][term.col-1].mode + if(!(TLINE(*y-1)[term.col-1].mode & ATTR_WRAP)) { break; } } } else if(direction > 0 && *y < term.row-1) { for(; *y < term.row; *y += direction) { - if(!(term.line[*y][term.col-1].mode + if(!(TLINE(*y)[term.col-1].mode & ATTR_WRAP)) { break; } @@ -777,15 +808,6 @@ getbuttoninfo(XEvent *e) { sel.oe.x = x2col(e->xbutton.x); sel.oe.y = y2row(e->xbutton.y); - - if(sel.ob.y < sel.oe.y - || (sel.ob.y == sel.oe.y && sel.ob.x < sel.oe.x)) { - selsnap(sel.snap, &sel.ob.x, &sel.ob.y, -1); - selsnap(sel.snap, &sel.oe.x, &sel.oe.y, +1); - } else { - selsnap(sel.snap, &sel.oe.x, &sel.oe.y, -1); - selsnap(sel.snap, &sel.ob.x, &sel.ob.y, +1); - } selnormalize(); sel.type = SEL_REGULAR; @@ -799,71 +821,72 @@ getbuttoninfo(XEvent *e) { void mousereport(XEvent *e) { - int x = x2col(e->xbutton.x), y = y2row(e->xbutton.y), - button = e->xbutton.button, state = e->xbutton.state, - len; - char buf[40]; - static int ox, oy; - - /* from urxvt */ - if(e->xbutton.type == MotionNotify) { - if(x == ox && y == oy) - return; - if(!IS_SET(MODE_MOUSEMOTION) && !IS_SET(MODE_MOUSEMANY)) - return; - /* MOUSE_MOTION: no reporting if no button is pressed */ - if(IS_SET(MODE_MOUSEMOTION) && oldbutton == 3) - return; - - button = oldbutton + 32; - ox = x; - oy = y; - } else { - if(!IS_SET(MODE_MOUSESGR) && e->xbutton.type == ButtonRelease) { - button = 3; - } else { - button -= Button1; - if(button >= 3) - button += 64 - 3; - } - if(e->xbutton.type == ButtonPress) { - oldbutton = button; - ox = x; - oy = y; - } else if(e->xbutton.type == ButtonRelease) { - oldbutton = 3; - /* MODE_MOUSEX10: no button release reporting */ - if(IS_SET(MODE_MOUSEX10)) - return; - if (button == 64 || button == 65) - return; - } - } - - if(!IS_SET(MODE_MOUSEX10)) { - button += (state & ShiftMask ? 4 : 0) - + (state & Mod4Mask ? 8 : 0) - + (state & ControlMask ? 16 : 0); - } - - len = 0; - if(IS_SET(MODE_MOUSESGR)) { - len = snprintf(buf, sizeof(buf), "\033[<%d;%d;%d%c", - button, x+1, y+1, - e->xbutton.type == ButtonRelease ? 'm' : 'M'); - } else if(x < 223 && y < 223) { - len = snprintf(buf, sizeof(buf), "\033[M%c%c%c", - 32+button, 32+x+1, 32+y+1); - } else { - return; - } - - ttywrite(buf, len); + return; // jason +// int x = x2col(e->xbutton.x), y = y2row(e->xbutton.y), +// button = e->xbutton.button, state = e->xbutton.state, +// len; +// char buf[40]; +// static int ox, oy; +// +// /* from urxvt */ +// if(e->xbutton.type == MotionNotify) { +// if(x == ox && y == oy) +// return; +// if(!IS_SET(MODE_MOUSEMOTION) && !IS_SET(MODE_MOUSEMANY)) +// return; +// /* MOUSE_MOTION: no reporting if no button is pressed */ +// if(IS_SET(MODE_MOUSEMOTION) && oldbutton == 3) +// return; +// +// button = oldbutton + 32; +// ox = x; +// oy = y; +// } else { +// if(!IS_SET(MODE_MOUSESGR) && e->xbutton.type == ButtonRelease) { +// button = 3; +// } else { +// button -= Button1; +// if(button >= 3) +// button += 64 - 3; +// } +// if(e->xbutton.type == ButtonPress) { +// oldbutton = button; +// ox = x; +// oy = y; +// } else if(e->xbutton.type == ButtonRelease) { +// oldbutton = 3; +// /* MODE_MOUSEX10: no button release reporting */ +// if(IS_SET(MODE_MOUSEX10)) +// return; +// if (button == 64 || button == 65) +// return; +// } +// } +// +// if(!IS_SET(MODE_MOUSEX10)) { +// button += (state & ShiftMask ? 4 : 0) +// + (state & Mod4Mask ? 8 : 0) +// + (state & ControlMask ? 16 : 0); +// } +// +// len = 0; +// if(IS_SET(MODE_MOUSESGR)) { +// len = snprintf(buf, sizeof(buf), "\033[<%d;%d;%d%c", +// button, x+1, y+1, +// e->xbutton.type == ButtonRelease ? 'm' : 'M'); +// } else if(x < 223 && y < 223) { +// len = snprintf(buf, sizeof(buf), "\033[M%c%c%c", +// 32+button, 32+x+1, 32+y+1); +// } else { +// return; +// } +// +// ttywrite(buf, len); } void bpress(XEvent *e) { - struct timeval now; + struct timespec now; Mousekey *mk; if(IS_SET(MODE_MOUSE) && !(e->xbutton.state & forceselmod)) { @@ -880,7 +903,7 @@ bpress(XEvent *e) { } if(e->xbutton.button == Button1) { - gettimeofday(&now, NULL); + clock_gettime(CLOCK_MONOTONIC, &now); /* Clear previous selection, logically and visually. */ selclear(NULL); @@ -900,8 +923,6 @@ bpress(XEvent *e) { } else { sel.snap = 0; } - selsnap(sel.snap, &sel.ob.x, &sel.ob.y, -1); - selsnap(sel.snap, &sel.oe.x, &sel.oe.y, +1); selnormalize(); /* @@ -920,7 +941,7 @@ bpress(XEvent *e) { char * getsel(void) { char *str, *ptr; - int x, y, bufsize, size, ex; + int y, bufsize, size, lastx, linelen; Glyph *gp, *last; if(sel.ob.x == -1) @@ -931,16 +952,21 @@ getsel(void) { /* append every set & selected glyph to the selection */ for(y = sel.nb.y; y < sel.ne.y + 1; y++) { - gp = &term.line[y][0]; - last = &gp[term.col-1]; + linelen = tlinelen(y); - while(last >= gp && !(selected(last - gp, y) && - strcmp(last->c, " ") != 0)) { - --last; + if(sel.type == SEL_RECTANGULAR) { + gp = &TLINE(y)[sel.nb.x]; + lastx = sel.ne.x; + } else { + gp = &TLINE(y)[sel.nb.y == y ? sel.nb.x : 0]; + lastx = (sel.ne.y == y) ? sel.ne.x : term.col-1; } + last = &TLINE(y)[MIN(lastx, linelen-1)]; + while(last >= gp && last->c[0] == ' ') + --last; - for(x = 0; gp <= last; x++, ++gp) { - if(!selected(x, y) || (gp->mode & ATTR_WDUMMY)) + for( ; gp <= last; ++gp) { + if(gp->mode & ATTR_WDUMMY) continue; size = utf8len(gp->c); @@ -957,20 +983,8 @@ getsel(void) { * st. * FIXME: Fix the computer world. */ - if(y < sel.ne.y && !(x > 0 && (gp-1)->mode & ATTR_WRAP)) + if((y < sel.ne.y || lastx >= linelen) && !(last->mode & ATTR_WRAP)) *ptr++ = '\n'; - - /* - * If the last selected line expands in the selection - * after the visible text '\n' is appended. - */ - if(y == sel.ne.y) { - ex = sel.ne.x; - if(sel.nb.y == sel.ne.y && sel.ne.x < sel.nb.x) - ex = sel.nb.x; - if(tlinelen(y) < ex) - *ptr++ = '\n'; - } } *ptr = 0; return str; @@ -987,12 +1001,17 @@ selnotify(XEvent *e) { int format; uchar *data, *last, *repl; Atom type; + XSelectionEvent *xsev; ofs = 0; + xsev = (XSelectionEvent *)e; + if (xsev->property == None) + return; do { - if(XGetWindowProperty(xw.dpy, xw.win, XA_PRIMARY, ofs, BUFSIZ/4, - False, AnyPropertyType, &type, &format, - &nitems, &rem, &data)) { + if(XGetWindowProperty(xw.dpy, xw.win, xsev->property, ofs, + BUFSIZ/4, False, AnyPropertyType, + &type, &format, &nitems, &rem, + &data)) { fprintf(stderr, "Clipboard allocation failed\n"); return; } @@ -1028,11 +1047,25 @@ selpaste(const Arg *dummy) { } void +clipcopy(const Arg *dummy) { + Atom clipboard; + + if(sel.clipboard != NULL) + free(sel.clipboard); + + if(sel.primary != NULL) { + sel.clipboard = xstrdup(sel.primary); + clipboard = XInternAtom(xw.dpy, "CLIPBOARD", 0); + XSetSelectionOwner(xw.dpy, clipboard, xw.win, CurrentTime); + } +} + +void clippaste(const Arg *dummy) { Atom clipboard; clipboard = XInternAtom(xw.dpy, "CLIPBOARD", 0); - XConvertSelection(xw.dpy, clipboard, sel.xtarget, XA_PRIMARY, + XConvertSelection(xw.dpy, clipboard, sel.xtarget, clipboard, xw.win, CurrentTime); } @@ -1048,7 +1081,8 @@ void selrequest(XEvent *e) { XSelectionRequestEvent *xsre; XSelectionEvent xev; - Atom xa_targets, string; + Atom xa_targets, string, clipboard; + char *seltext; xsre = (XSelectionRequestEvent *) e; xev.type = SelectionNotify; @@ -1067,11 +1101,29 @@ selrequest(XEvent *e) { XA_ATOM, 32, PropModeReplace, (uchar *) &string, 1); xev.property = xsre->property; - } else if(xsre->target == sel.xtarget && sel.clip != NULL) { - XChangeProperty(xsre->display, xsre->requestor, xsre->property, - xsre->target, 8, PropModeReplace, - (uchar *) sel.clip, strlen(sel.clip)); - xev.property = xsre->property; + } else if(xsre->target == sel.xtarget || xsre->target == XA_STRING) { + /* + * xith XA_STRING non ascii characters may be incorrect in the + * requestor. It is not our problem, use utf8. + */ + clipboard = XInternAtom(xw.dpy, "CLIPBOARD", 0); + if(xsre->selection == XA_PRIMARY) { + seltext = sel.primary; + } else if(xsre->selection == clipboard) { + seltext = sel.clipboard; + } else { + fprintf(stderr, + "Unhandled clipboard selection 0x%lx\n", + xsre->selection); + return; + } + if(seltext != NULL) { + XChangeProperty(xsre->display, xsre->requestor, + xsre->property, xsre->target, + 8, PropModeReplace, + (uchar *)seltext, strlen(seltext)); + xev.property = xsre->property; + } } /* all done, send a notification to the listener */ @@ -1081,16 +1133,10 @@ selrequest(XEvent *e) { void xsetsel(char *str) { - /* register the selection for both the clipboard and the primary */ - Atom clipboard; - - free(sel.clip); - sel.clip = str; + free(sel.primary); + sel.primary = str; XSetSelectionOwner(xw.dpy, XA_PRIMARY, xw.win, CurrentTime); - - clipboard = XInternAtom(xw.dpy, "CLIPBOARD", 0); - XSetSelectionOwner(xw.dpy, clipboard, xw.win, CurrentTime); } void @@ -1149,23 +1195,40 @@ die(const char *errstr, ...) { void execsh(void) { - char **args; - char *envshell = getenv("SHELL"); - const struct passwd *pass = getpwuid(getuid()); + char **args, *sh, *prog; + const struct passwd *pw; char buf[sizeof(long) * 8 + 1]; - unsetenv("COLUMNS"); - unsetenv("LINES"); - unsetenv("TERMCAP"); + errno = 0; + if((pw = getpwuid(getuid())) == NULL) { + if(errno) + die("getpwuid:%s\n", strerror(errno)); + else + die("who are you?\n"); + } - 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); + 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); + setenv("HOME", pw->pw_dir, 1); + setenv("TERM", termname, 1); setenv("WINDOWID", buf, 1); signal(SIGCHLD, SIG_DFL); @@ -1175,25 +1238,21 @@ execsh(void) { signal(SIGTERM, SIG_DFL); signal(SIGALRM, SIG_DFL); - DEFAULT(envshell, shell); - setenv("TERM", termname, 1); - args = opt_cmd ? opt_cmd : (char *[]){envshell, "-i", NULL}; - execvp(args[0], args); - exit(EXIT_FAILURE); + execvp(prog, args); + _exit(EXIT_FAILURE); } void sigchld(int a) { - int stat = 0; + int stat, ret; if(waitpid(pid, &stat, 0) < 0) die("Waiting for pid %hd failed: %s\n", pid, strerror(errno)); - if(WIFEXITED(stat)) { - exit(WEXITSTATUS(stat)); - } else { - exit(EXIT_FAILURE); - } + ret = WIFEXITED(stat) ? WEXITSTATUS(stat) : EXIT_FAILURE; + if (ret != EXIT_SUCCESS) + die("child finished with error '%d'\n", stat); + exit(EXIT_SUCCESS); } void @@ -1264,10 +1323,15 @@ ttyread(void) { /* keep any uncomplete utf8 char for the next call */ memmove(buf, ptr, buflen); + if(term.scr > 0 && term.scr < histsize-1) term.scr++; } void ttywrite(const char *s, size_t n) { + Arg arg = (Arg){ .i = term.scr }; + + kscrolldown(&arg); + if(xwrite(cmdfd, s, n) == -1) die("write error on tty: %s\n", strerror(errno)); } @@ -1367,9 +1431,12 @@ treset(void) { memset(term.trantbl, sizeof(term.trantbl), CS_USA); term.charset = 0; - tclearregion(0, 0, term.col-1, term.row-1); - tmoveto(0, 0); - tcursor(CURSOR_SAVE); + for(i = 0; i < 2; i++) { + tmoveto(0, 0); + tcursor(CURSOR_SAVE); + tclearregion(0, 0, term.col-1, term.row-1); + tswapscreen(); + } } void @@ -1392,13 +1459,51 @@ tswapscreen(void) { } void -tscrolldown(int orig, int n) { +kscrolldown(const Arg* a) { + int n = a->i; + + if(n < 0) + n = term.row + n; + + if(n > term.scr) + n = term.scr; + + if(term.scr > 0) { + term.scr -= n; + selscroll(0, -n); + tfulldirt(); + } +} + +void +kscrollup(const Arg* a) { + int n = a->i; + + if(n < 0) + n = term.row + n; + + if(term.scr <= histsize - n) { + term.scr += n; + selscroll(0, n); + tfulldirt(); + } +} + +void +tscrolldown(int orig, int n, bool copyhist) { int i; Line temp; LIMIT(n, 0, term.bot-orig+1); tsetdirt(orig, term.bot-n); + if(copyhist) { + term.histi = (term.histi - 1 + histsize) % histsize; + temp = term.hist[term.histi]; + term.hist[term.histi] = term.line[term.bot]; + term.line[term.bot] = temp; + } + tclearregion(0, term.bot-n+1, term.col-1, term.bot); for(i = term.bot; i >= orig+n; i--) { @@ -1411,12 +1516,19 @@ tscrolldown(int orig, int n) { } void -tscrollup(int orig, int n) { +tscrollup(int orig, int n, bool copyhist) { int i; Line temp; LIMIT(n, 0, term.bot-orig+1); + if(copyhist) { + term.histi = (term.histi + 1) % histsize; + temp = term.hist[term.histi]; + term.hist[term.histi] = term.line[orig]; + term.line[orig] = temp; + } + tclearregion(0, orig, term.col-1, orig+n-1); tsetdirt(orig+n, term.bot); @@ -1463,7 +1575,7 @@ tnewline(int first_col) { int y = term.c.y; if(y == term.bot) { - tscrollup(term.top, 1); + tscrollup(term.top, 1, true); } else { y++; } @@ -1495,7 +1607,8 @@ csiparse(void) { break; p++; } - csiescseq.mode = *p; + csiescseq.mode[0] = *p++; + csiescseq.mode[1] = (p < csiescseq.buf+csiescseq.len) ? *p : '\0'; } /* for absolute user moves, when decom is set */ @@ -1562,6 +1675,7 @@ tsetchar(char *c, Glyph *attr, int x, int y) { void tclearregion(int x1, int y1, int x2, int y2) { int x, y, temp; + Glyph *gp; if(x1 > x2) temp = x1, x1 = x2, x2 = temp; @@ -1576,10 +1690,13 @@ tclearregion(int x1, int y1, int x2, int y2) { for(y = y1; y <= y2; y++) { term.dirty[y] = 1; for(x = x1; x <= x2; x++) { + gp = &term.line[y][x]; if(selected(x, y)) selclear(NULL); - term.line[y][x] = term.c.attr; - memcpy(term.line[y][x].c, " ", 2); + gp->fg = term.c.attr.fg; + gp->bg = term.c.attr.bg; + gp->mode = 0; + memcpy(gp->c, " ", 2); } } } @@ -1619,13 +1736,13 @@ tinsertblank(int n) { void tinsertblankline(int n) { if(BETWEEN(term.c.y, term.top, term.bot)) - tscrolldown(term.c.y, n); + tscrolldown(term.c.y, n, false); } void tdeleteline(int n) { if(BETWEEN(term.c.y, term.top, term.bot)) - tscrollup(term.c.y, n); + tscrollup(term.c.y, n, false); } int32_t @@ -1685,15 +1802,24 @@ tsetattr(int *attr, int l) { for(i = 0; i < l; i++) { switch(attr[i]) { case 0: - term.c.attr.mode &= ~(ATTR_REVERSE | ATTR_UNDERLINE \ - | ATTR_BOLD | ATTR_ITALIC \ - | ATTR_BLINK); + term.c.attr.mode &= ~( + ATTR_BOLD | + ATTR_FAINT | + ATTR_ITALIC | + ATTR_UNDERLINE | + ATTR_BLINK | + ATTR_REVERSE | + ATTR_INVISIBLE | + ATTR_STRUCK ); term.c.attr.fg = defaultfg; term.c.attr.bg = defaultbg; break; case 1: term.c.attr.mode |= ATTR_BOLD; break; + case 2: + term.c.attr.mode |= ATTR_FAINT; + break; case 3: term.c.attr.mode |= ATTR_ITALIC; break; @@ -1701,15 +1827,21 @@ tsetattr(int *attr, int l) { term.c.attr.mode |= ATTR_UNDERLINE; break; case 5: /* slow blink */ + /* FALLTHROUGH */ case 6: /* rapid blink */ term.c.attr.mode |= ATTR_BLINK; break; case 7: term.c.attr.mode |= ATTR_REVERSE; break; - case 21: + case 8: + term.c.attr.mode |= ATTR_INVISIBLE; + break; + case 9: + term.c.attr.mode |= ATTR_STRUCK; + break; case 22: - term.c.attr.mode &= ~ATTR_BOLD; + term.c.attr.mode &= ~(ATTR_BOLD | ATTR_FAINT); break; case 23: term.c.attr.mode &= ~ATTR_ITALIC; @@ -1718,12 +1850,17 @@ tsetattr(int *attr, int l) { term.c.attr.mode &= ~ATTR_UNDERLINE; break; case 25: - case 26: term.c.attr.mode &= ~ATTR_BLINK; break; case 27: term.c.attr.mode &= ~ATTR_REVERSE; break; + case 28: + term.c.attr.mode &= ~ATTR_INVISIBLE; + break; + case 29: + term.c.attr.mode &= ~ATTR_STRUCK; + break; case 38: if ((idx = tdefcolor(attr, &i, l)) >= 0) term.c.attr.fg = idx; @@ -1787,7 +1924,7 @@ tsetmode(bool priv, bool set, int *args, int narg) { 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); @@ -1909,7 +2046,7 @@ csihandle(void) { char buf[40]; int len; - switch(csiescseq.mode) { + switch(csiescseq.mode[0]) { default: unknown: fprintf(stderr, "erresc: unknown csi "); @@ -1950,7 +2087,7 @@ csihandle(void) { break; case 'c': /* DA -- Device Attributes */ if(csiescseq.arg[0] == 0) - ttywrite(VT102ID, sizeof(VT102ID) - 1); + ttywrite(vtiden, sizeof(vtiden) - 1); break; case 'C': /* CUF -- Cursor Forward */ case 'a': /* HPR -- Cursor Forward */ @@ -2034,11 +2171,11 @@ csihandle(void) { break; case 'S': /* SU -- Scroll line up */ DEFAULT(csiescseq.arg[0], 1); - tscrollup(term.top, csiescseq.arg[0]); + tscrollup(term.top, csiescseq.arg[0], false); break; case 'T': /* SD -- Scroll line down */ DEFAULT(csiescseq.arg[0], 1); - tscrolldown(term.top, csiescseq.arg[0]); + tscrolldown(term.top, csiescseq.arg[0], false); break; case 'L': /* IL -- Insert blank lines */ DEFAULT(csiescseq.arg[0], 1); @@ -2097,6 +2234,19 @@ csihandle(void) { case 'u': /* DECRC -- Restore cursor position (ANSI.SYS) */ tcursor(CURSOR_LOAD); 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: + goto unknown; + } + break; } } @@ -2161,7 +2311,7 @@ strhandle(void) { * TODO if defaultbg color is changed, borders * are dirty */ - redraw(0); + redraw(); } return; } @@ -2181,12 +2331,23 @@ strhandle(void) { void strparse(void) { + int c; char *p = strescseq.buf; strescseq.narg = 0; strescseq.buf[strescseq.len] = '\0'; - while(p && strescseq.narg < STR_ARG_SIZ) - strescseq.args[strescseq.narg++] = strsep(&p, ";"); + + if(*p == '\0') + return; + + while(strescseq.narg < STR_ARG_SIZ) { + strescseq.args[strescseq.narg++] = p; + while((c = *p) != ';' && c != '\0') + ++p; + if(c == '\0') + return; + *p++ = '\0'; + } } void @@ -2259,12 +2420,10 @@ tdumpline(int n) { Glyph *bp, *end; bp = &term.line[n][0]; - end = &bp[term.col-1]; - while(end > bp && !strcmp(" ", end->c)) - --end; - if(bp != end || strcmp(bp->c, " ")) { + end = &bp[MIN(tlinelen(n), term.col) - 1]; + if(bp != end || bp->c[0] != ' ') { for( ;bp <= end; ++bp) - tprinter(bp->c, strlen(bp->c)); + tprinter(bp->c, utf8len(bp->c)); } tprinter("\n", 1); } @@ -2298,13 +2457,13 @@ techo(char *buf, int len) { for(; len > 0; buf++, len--) { char c = *buf; - if(ISCONTROL(c)) { /* control code */ + if(ISCONTROL((uchar) c)) { /* control code */ if(c & 0x80) { c &= 0x7f; tputc("^", 1); tputc("[", 1); } else if(c != '\n' && c != '\r' && c != '\t') { - c ^= '\x40'; + c ^= 0x40; tputc("^", 1); } tputc(&c, 1); @@ -2318,19 +2477,52 @@ techo(char *buf, int len) { void tdeftran(char ascii) { - char c, (*bp)[2]; - static char tbl[][2] = { - {'0', CS_GRAPHIC0}, {'B', CS_USA}, - {0, 0} - }; - - for (bp = &tbl[0]; (c = (*bp)[0]) && c != ascii; ++bp) - /* nothing */; + static char cs[] = "0B"; + static int vcs[] = {CS_GRAPHIC0, CS_USA}; + char *p; - if (c == 0) + if((p = strchr(cs, ascii)) == NULL) { fprintf(stderr, "esc unhandled charset: ESC ( %c\n", ascii); - else - term.trantbl[term.icharset] = (*bp)[1]; + } else { + term.trantbl[term.icharset] = vcs[p - cs]; + } +} + +void +tdectest(char c) { + static char E[UTF_SIZ] = "E"; + int x, y; + + if(c == '8') { /* DEC screen alignment test. */ + for(x = 0; x < term.col; ++x) { + for(y = 0; y < term.row; ++y) + tsetchar(E, &term.c.attr, x, y); + } + } +} + +void +tstrsequence(uchar c) { + if (c & 0x80) { + switch (c) { + case 0x90: /* DCS -- Device Control String */ + c = 'P'; + break; + case 0x9f: /* APC -- Application Program Command */ + c = '_'; + break; + case 0x9e: /* PM -- Privacy Message */ + c = '^'; + break; + case 0x9d: /* OSC -- Operating System Command */ + c = ']'; + break; + } + } + strreset(); + strescseq.type = c; + term.esc |= ESC_STR; + return; } void @@ -2361,7 +2553,7 @@ tcontrolcode(uchar ascii) { 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 */ @@ -2369,11 +2561,9 @@ tcontrolcode(uchar ascii) { 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); @@ -2387,37 +2577,117 @@ tcontrolcode(uchar ascii) { case 0177: /* DEL (IGNORED) */ return; case 0x84: /* TODO: IND */ - case 0x85: /* TODO: NEL */ - case 0x88: /* TODO: HTS */ + break; + case 0x85: /* NEL -- Next line */ + tnewline(1); /* always go to first col */ + break; + case 0x88: /* HTS -- Horizontal tab stop */ + term.tabs[term.c.x] = 1; + break; case 0x8d: /* TODO: RI */ case 0x8e: /* TODO: SS2 */ case 0x8f: /* TODO: SS3 */ - case 0x90: /* TODO: DCS */ case 0x98: /* TODO: SOS */ - case 0x9a: /* TODO: DECID */ + break; + case 0x9a: /* DECID -- Identify Terminal */ + ttywrite(vtiden, sizeof(vtiden) - 1); + break; case 0x9b: /* TODO: CSI */ case 0x9c: /* TODO: ST */ - case 0x9d: /* TODO: OSC */ - case 0x9e: /* TODO: PM */ - case 0x9f: /* TODO: APC */ break; + case 0x90: /* DCS -- Device Control String */ + case 0x9f: /* APC -- Application Program Command */ + case 0x9e: /* PM -- Privacy Message */ + case 0x9d: /* OSC -- Operating System Command */ + tstrsequence(ascii); + return; } /* only CAN, SUB, \a and C1 chars interrupt a sequence */ term.esc &= ~(ESC_STR_END|ESC_STR); return; } -void -tdectest(char c) { - static char E[UTF_SIZ] = "E"; - int x, y; - - if(c == '8') { /* DEC screen alignment test. */ - for(x = 0; x < term.col; ++x) { - for(y = 0; y < term.row; ++y) - tsetchar(E, &term.c.attr, x, y); +/* + * returns 1 when the sequence is finished and it hasn't to read + * more characters for this sequence, otherwise 0 + */ +int +eschandle(uchar ascii) { + switch(ascii) { + case '[': + term.esc |= ESC_CSI; + return 0; + case '#': + term.esc |= ESC_TEST; + return 0; + case 'P': /* DCS -- Device Control String */ + case '_': /* APC -- Application Program Command */ + case '^': /* PM -- Privacy Message */ + case ']': /* OSC -- Operating System Command */ + case 'k': /* old title set compatibility */ + tstrsequence(ascii); + return 0; + 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; + case 'D': /* IND -- Linefeed */ + if(term.c.y == term.bot) { + tscrollup(term.top, 1, true); + } else { + tmoveto(term.c.x, term.c.y+1); + } + break; + case 'E': /* NEL -- Next line */ + tnewline(1); /* always go to first col */ + break; + case 'H': /* HTS -- Horizontal tab stop */ + term.tabs[term.c.x] = 1; + break; + case 'M': /* RI -- Reverse index */ + if(term.c.y == term.top) { + tscrolldown(term.top, 1, true); + } else { + tmoveto(term.c.x, term.c.y-1); } + break; + case 'Z': /* DECID -- Identify Terminal */ + ttywrite(vtiden, sizeof(vtiden) - 1); + break; + case 'c': /* RIS -- Reset to inital state */ + treset(); + xresettitle(); + xloadcols(); + break; + case '=': /* DECPAM -- Application keypad */ + term.mode |= MODE_APPKEYPAD; + break; + case '>': /* DECPNM -- Normal keypad */ + term.mode &= ~MODE_APPKEYPAD; + break; + case '7': /* DECSC -- Save Cursor */ + tcursor(CURSOR_SAVE); + break; + case '8': /* DECRC -- Restore Cursor */ + tcursor(CURSOR_LOAD); + break; + case '\\': /* ST -- String Terminator */ + if(term.esc & ESC_STR_END) + strhandle(); + break; + default: + fprintf(stderr, "erresc: unknown sequence ESC 0x%02X '%c'\n", + (uchar) ascii, isprint(ascii)? ascii:'.'); + break; } + return 1; } void @@ -2433,7 +2703,10 @@ tputc(char *c, int len) { 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; } @@ -2504,78 +2777,9 @@ tputc(char *c, int len) { } else if(term.esc & ESC_TEST) { tdectest(ascii); } else { - switch(ascii) { - case '[': - term.esc |= ESC_CSI; + if (!eschandle(ascii)) return; - case '#': - term.esc |= ESC_TEST; - return; - case 'P': /* DCS -- Device Control String */ - case '_': /* APC -- Application Program Command */ - case '^': /* PM -- Privacy Message */ - case ']': /* OSC -- Operating System Command */ - case 'k': /* old title set compatibility */ - strreset(); - strescseq.type = ascii; - term.esc |= ESC_STR; - return; - case '(': /* set primary charset G0 */ - case ')': /* set secondary charset G1 */ - case '*': /* set tertiary charset G2 */ - case '+': /* set quaternary charset G3 */ - term.icharset = ascii - '('; - term.esc |= ESC_ALTCHARSET; - return; - case 'D': /* IND -- Linefeed */ - if(term.c.y == term.bot) { - tscrollup(term.top, 1); - } else { - tmoveto(term.c.x, term.c.y+1); - } - break; - case 'E': /* NEL -- Next line */ - tnewline(1); /* always go to first col */ - break; - case 'H': /* HTS -- Horizontal tab stop */ - term.tabs[term.c.x] = 1; - break; - case 'M': /* RI -- Reverse index */ - if(term.c.y == term.top) { - tscrolldown(term.top, 1); - } else { - tmoveto(term.c.x, term.c.y-1); - } - break; - case 'Z': /* DECID -- Identify Terminal */ - ttywrite(VT102ID, sizeof(VT102ID) - 1); - break; - case 'c': /* RIS -- Reset to inital state */ - treset(); - xresettitle(); - xloadcols(); - break; - case '=': /* DECPAM -- Application keypad */ - term.mode |= MODE_APPKEYPAD; - break; - case '>': /* DECPNM -- Normal keypad */ - term.mode &= ~MODE_APPKEYPAD; - break; - case '7': /* DECSC -- Save Cursor */ - tcursor(CURSOR_SAVE); - break; - case '8': /* DECRC -- Restore Cursor */ - tcursor(CURSOR_LOAD); - break; - case '\\': /* ST -- String Terminator */ - if(term.esc & ESC_STR_END) - strhandle(); - break; - default: - fprintf(stderr, "erresc: unknown sequence ESC 0x%02X '%c'\n", - (uchar) ascii, isprint(ascii)? ascii:'.'); - break; - } + /* sequence already finished */ } term.esc = 0; /* @@ -2591,13 +2795,16 @@ tputc(char *c, int len) { 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); @@ -2615,18 +2822,20 @@ tputc(char *c, int len) { } } -int +void tresize(int col, int row) { - int i; + int i, j; int minrow = MIN(row, term.row); int mincol = MIN(col, term.col); int slide = term.c.y - row + 1; bool *bp; - Line *orig; TCursor c; - if(col < 1 || row < 1) - return 0; + if(col < 1 || row < 1) { + fprintf(stderr, + "tresize: error resizing to %dx%d\n", col, row); + return; + } /* free unneeded rows */ i = 0; @@ -2651,19 +2860,26 @@ tresize(int col, int row) { /* resize to new height */ term.line = xrealloc(term.line, row * sizeof(Line)); term.alt = xrealloc(term.alt, row * sizeof(Line)); + term.hist = xrealloc(term.hist, histsize * sizeof(Line)); term.dirty = xrealloc(term.dirty, row * sizeof(*term.dirty)); term.tabs = xrealloc(term.tabs, col * sizeof(*term.tabs)); + for(i = 0; i < histsize; i++) { + term.hist[i] = xrealloc(term.hist[i], col * sizeof(Glyph)); + for(j = mincol; j < col; j++) { + term.hist[i][j] = term.c.attr; + memcpy(term.hist[i][j].c, " ", 2); + } + } + /* resize each row to new width, zero-pad if needed */ for(i = 0; i < minrow; i++) { - term.dirty[i] = 1; term.line[i] = xrealloc(term.line[i], col * sizeof(Glyph)); term.alt[i] = xrealloc(term.alt[i], col * sizeof(Glyph)); } /* allocate any new rows */ for(/* i == minrow */; i < row; i++) { - term.dirty[i] = 1; term.line[i] = xmalloc(col * sizeof(Glyph)); term.alt[i] = xmalloc(col * sizeof(Glyph)); } @@ -2683,10 +2899,9 @@ tresize(int col, int row) { tsetscroll(0, row-1); /* make use of the LIMIT in tmoveto */ tmoveto(term.c.x, term.c.y); - /* Clearing both screens */ - orig = term.line; + /* Clearing both screens (it makes dirty all lines) */ c = term.c; - do { + for(i = 0; i < 2; i++) { if(mincol < col && 0 < minrow) { tclearregion(mincol, 0, col - 1, minrow - 1); } @@ -2695,10 +2910,8 @@ tresize(int col, int row) { } tswapscreen(); tcursor(CURSOR_LOAD); - } while(orig != term.line); + } term.c = c; - - return (slide > 0); } void @@ -2899,6 +3112,7 @@ xloadfonts(char *fontstr, double fontsize) { FcPattern *pattern; FcResult r_sz, r_psz; double fontval; + float ceilf(float); if(fontstr[0] == '-') { pattern = XftXlfdParse(fontstr, False, False); @@ -2909,7 +3123,7 @@ xloadfonts(char *fontstr, double fontsize) { 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); @@ -2929,6 +3143,7 @@ xloadfonts(char *fontstr, double fontsize) { FcPatternAddDouble(pattern, FC_PIXEL_SIZE, 12); usedfontsize = 12; } + defaultfontsize = usedfontsize; } FcConfigSubstitute(0, pattern, FcMatchPattern); @@ -2941,11 +3156,13 @@ xloadfonts(char *fontstr, double fontsize) { FcPatternGetDouble(dc.font.match->pattern, FC_PIXEL_SIZE, 0, &fontval); usedfontsize = fontval; + if(fontsize == 0) + defaultfontsize = fontval; } /* Setting character width and height. */ - xw.cw = CEIL(dc.font.width * cwscale); - xw.ch = CEIL(dc.font.height * chscale); + xw.cw = ceilf(dc.font.width * cwscale); + xw.ch = ceilf(dc.font.height * chscale); FcPatternDel(pattern, FC_SLANT); FcPatternAddInteger(pattern, FC_SLANT, FC_SLANT_ITALIC); @@ -2996,10 +3213,29 @@ xunloadfonts(void) { 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 @@ -3042,8 +3278,8 @@ xinit(void) { | 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 @@ -3105,7 +3341,7 @@ void 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; @@ -3163,7 +3399,7 @@ xdraws(char *s, Glyph base, int x, int y, int charlen, int bytelen) { * change basic system colors [0-7] * to bright system colors [8-15] */ - if(BETWEEN(base.fg, 0, 7)) + if(BETWEEN(base.fg, 0, 7) && !(base.mode & ATTR_FAINT)) fg = &dc.col[base.fg + 8]; if(base.mode & ATTR_ITALIC) { @@ -3207,9 +3443,20 @@ xdraws(char *s, Glyph base, int x, int y, int charlen, int bytelen) { bg = temp; } + if(base.mode & ATTR_FAINT && !(base.mode & ATTR_BOLD)) { + colfg.red = fg->color.red / 2; + colfg.green = fg->color.green / 2; + colfg.blue = fg->color.blue / 2; + XftColorAllocValue(xw.dpy, xw.vis, xw.cmap, &colfg, &revfg); + fg = &revfg; + } + if(base.mode & ATTR_BLINK && term.mode & MODE_BLINK) fg = bg; + if(base.mode & ATTR_INVISIBLE) + fg = bg; + /* Intelligent cleaning up of the borders. */ if(x == 0) { xclear(0, (y == 0)? 0 : winy, borderpx, @@ -3252,28 +3499,22 @@ xdraws(char *s, Glyph base, int x, int y, int charlen, int bytelen) { bytelen -= u8cblen; doesexist = XftCharExists(xw.dpy, font->match, unicodep); - if(oneatatime || !doesexist || bytelen <= 0) { - if(oneatatime || bytelen <= 0) { - if(doesexist) { - u8fl++; - u8fblen += u8cblen; - } - } - - if(u8fl > 0) { - XftDrawStringUtf8(xw.draw, fg, - font->match, xp, - winy + font->ascent, - (FcChar8 *)u8fs, - u8fblen); - xp += xw.cw * u8fl; - - } - break; + if(doesexist) { + u8fl++; + u8fblen += u8cblen; + if(!oneatatime && bytelen > 0) + continue; } - u8fl++; - u8fblen += u8cblen; + if(u8fl > 0) { + XftDrawStringUtf8(xw.draw, fg, + font->match, xp, + winy + font->ascent, + (FcChar8 *)u8fs, + u8fblen); + xp += xw.cw * u8fl; + } + break; } if(doesexist) { if(oneatatime) @@ -3283,8 +3524,13 @@ xdraws(char *s, Glyph base, int x, int y, int charlen, int bytelen) { /* 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; } } @@ -3315,8 +3561,8 @@ xdraws(char *s, Glyph base, int x, int y, int charlen, int bytelen) { FcMatchPattern); FcDefaultSubstitute(fcpattern); - fontpattern = FcFontSetMatch(0, fcsets, - FcTrue, fcpattern, &fcres); + fontpattern = FcFontSetMatch(0, fcsets, 1, + fcpattern, &fcres); /* * Overwrite or create the new cache entry. @@ -3324,11 +3570,13 @@ xdraws(char *s, Glyph base, int x, int y, int charlen, int bytelen) { 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++; @@ -3360,6 +3608,11 @@ xdraws(char *s, Glyph base, int x, int y, int charlen, int bytelen) { width, 1); } + if(base.mode & ATTR_STRUCK) { + XftDrawRect(xw.draw, fg, winx, winy + 2 * font->ascent / 3, + width, 1); + } + /* Reset clip to none. */ XftDrawSetClip(xw.draw, 0); } @@ -3389,39 +3642,60 @@ xdrawcursor(void) { xdraws(term.line[oldy][oldx].c, term.line[oldy][oldx], oldx, oldy, width, sl); + if(IS_SET(MODE_HIDE)) + return; + /* 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(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; + } - 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); + 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); + 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 - 1, + xw.cw, 1); + 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, + 1, xw.ch); + break; } - oldx = curx, oldy = term.c.y; + } 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; } @@ -3442,16 +3716,9 @@ xresettitle(void) { } 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 @@ -3481,10 +3748,10 @@ drawregion(int x1, int y1, int x2, int y2) { xtermclear(0, y, term.col, y); term.dirty[y] = 0; - base = term.line[y][0]; + base = TLINE(y)[0]; ic = ib = ox = 0; for(x = x1; x < x2; x++) { - new = term.line[y][x]; + new = TLINE(y)[x]; if(new.mode == ATTR_WDUMMY) continue; if(ena_sel && selected(x, y)) @@ -3507,7 +3774,8 @@ drawregion(int x1, int y1, int x2, int y2) { if(ib > 0) xdraws(buf, base, ox, y, ic, ib); } - xdrawcursor(); + if(term.scr == 0) + xdrawcursor(); } void @@ -3518,7 +3786,7 @@ expose(XEvent *ev) { if(!e->count) xw.state &= ~WIN_REDRAW; } - redraw(0); + redraw(); } void @@ -3721,11 +3989,14 @@ run(void) { int w = xw.w, h = xw.h; fd_set rfd; int xfd = XConnectionNumber(xw.dpy), xev, blinkset = 0, dodraw = 0; - struct timeval drawtimeout, *tv = NULL, now, last, lastblink; + struct timespec drawtimeout, *tv = NULL, now, last, lastblink; + long deltatime; /* Waiting for window mapping */ while(1) { XNextEvent(xw.dpy, &ev); + if(XFilterEvent(&ev, None)) + continue; if(ev.type == ConfigureNotify) { w = ev.xconfigure.width; h = ev.xconfigure.height; @@ -3737,36 +4008,35 @@ run(void) { ttynew(); cresize(w, h); - gettimeofday(&last, NULL); + clock_gettime(CLOCK_MONOTONIC, &last); lastblink = last; for(xev = actionfps;;) { - long deltatime; - FD_ZERO(&rfd); FD_SET(cmdfd, &rfd); FD_SET(xfd, &rfd); - if(select(MAX(xfd, cmdfd)+1, &rfd, NULL, NULL, tv) < 0) { + if(pselect(MAX(xfd, cmdfd)+1, &rfd, NULL, NULL, tv, NULL) < 0) { if(errno == EINTR) continue; die("select failed: %s\n", strerror(errno)); } if(FD_ISSET(cmdfd, &rfd)) { ttyread(); - if(blinktimeout) { - blinkset = tattrset(ATTR_BLINK); - if(!blinkset) - MODBIT(term.mode, 0, MODE_BLINK); - } } - if(FD_ISSET(xfd, &rfd)) + if(FD_ISSET(xfd, &rfd)) { xev = actionfps; + if(blinktimeout) { + lastblink = now; + MODBIT(term.mode, 1, MODE_BLINK); + blinkset = 1; + } + } - gettimeofday(&now, NULL); + clock_gettime(CLOCK_MONOTONIC, &now); drawtimeout.tv_sec = 0; - drawtimeout.tv_usec = (1000/xfps) * 1000; + drawtimeout.tv_nsec = (1000/xfps) * 1E6; tv = &drawtimeout; dodraw = 0; @@ -3801,13 +4071,16 @@ run(void) { if(blinkset) { if(TIMEDIFF(now, lastblink) \ > blinktimeout) { - drawtimeout.tv_usec = 1; + drawtimeout.tv_nsec = 1000; } else { - drawtimeout.tv_usec = (1000 * \ + drawtimeout.tv_nsec = (1E6 * \ (blinktimeout - \ TIMEDIFF(now, lastblink))); } + drawtimeout.tv_sec = \ + drawtimeout.tv_nsec / 1E9; + drawtimeout.tv_nsec %= (long)1E9; } else { tv = NULL; } @@ -3818,9 +4091,9 @@ run(void) { void usage(void) { - die("%s " VERSION " (c) 2010-2014 st engineers\n" \ - "usage: st [-a] [-v] [-c class] [-f font] [-g geometry] [-o file]" \ - " [-t title] [-w windowid] [-e command ...]\n", argv0); + 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); } int @@ -3830,6 +4103,7 @@ main(int argc, char *argv[]) { xw.l = xw.t = 0; xw.isfixed = False; + xw.cursor = 0; ARGBEGIN { case 'a':