X-Git-Url: https://git.danieliu.xyz/?a=blobdiff_plain;f=st.c;h=7dbd87a5dc96d5b4bf0d40fd55922a8036394868;hb=6524f022f7d1d32f43208254e12f934bf64ed559;hp=fc913340fdd7760179445211d856dfcb22090cf6;hpb=09f5d98251469070b76f3911cc6a83d977871656;p=st.git diff --git a/st.c b/st.c index fc91334..7dbd87a 100644 --- a/st.c +++ b/st.c @@ -63,8 +63,6 @@ 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)) @@ -199,14 +197,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 */ @@ -259,6 +257,7 @@ typedef struct { int ch; /* char height */ int cw; /* char width */ char state; /* focus, redraw, visible */ + int cursor; /* cursor style */ } XWindow; typedef struct { @@ -292,7 +291,7 @@ typedef struct { int x, y; } nb, ne, ob, oe; - char *clip; + char *primary, *clipboard; Atom xtarget; bool alt; struct timespec tclick1; @@ -314,6 +313,7 @@ typedef struct { } Shortcut; /* function definitions used in config.h */ +static void clipcopy(const Arg *); static void clippaste(const Arg *); static void numlock(const Arg *); static void selpaste(const Arg *); @@ -349,7 +349,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); @@ -359,7 +359,7 @@ static void csidump(void); static void csihandle(void); static void csiparse(void); static void csireset(void); -static int eschandle(uchar ascii); +static int eschandle(uchar); static void strdump(void); static void strhandle(void); static void strparse(void); @@ -406,8 +406,9 @@ 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 tstrsequence(uchar); +static inline ushort sixd_to_16bit(int); static void xdraws(char *, Glyph, int, int, int, int); static void xhints(void); static void xclear(int, int, int, int); @@ -452,6 +453,8 @@ static char *getsel(void); static void selcopy(void); static void selscroll(int, int); static void selsnap(int, int *, int *, int); +static int x2col(int); +static int y2row(int); static void getbuttoninfo(XEvent *); static void mousereport(XEvent *); @@ -481,7 +484,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, }; @@ -524,6 +531,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. */ @@ -635,19 +643,20 @@ utf8validate(long *u, size_t i) { return i; } -static void +void selinit(void) { memset(&sel.tclick1, 0, sizeof(sel.tclick1)); memset(&sel.tclick2, 0, sizeof(sel.tclick2)); sel.mode = 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; } -static int +int x2col(int x) { x -= borderpx; x /= xw.cw; @@ -655,7 +664,7 @@ x2col(int x) { return LIMIT(x, 0, term.col-1); } -static int +int y2row(int y) { y -= borderpx; y /= xw.ch; @@ -663,7 +672,7 @@ y2row(int y) { return LIMIT(y, 0, term.row-1); } -static int tlinelen(int y) { +int tlinelen(int y) { int i = term.col; if(term.line[y][i - 1].mode & ATTR_WRAP) @@ -675,7 +684,7 @@ static int tlinelen(int y) { return i; } -static void +void selnormalize(void) { int i; @@ -702,7 +711,7 @@ selnormalize(void) { sel.ne.x = term.col - 1; } -static inline bool +bool selected(int x, int y) { if(sel.type == SEL_RECTANGULAR) return BETWEEN(y, sel.nb.y, sel.ne.y) @@ -986,12 +995,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; } @@ -1026,12 +1040,26 @@ selpaste(const Arg *dummy) { xw.win, CurrentTime); } +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); } @@ -1047,7 +1075,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; @@ -1066,11 +1095,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 */ @@ -1080,16 +1127,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 @@ -1510,7 +1551,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 */ @@ -1826,7 +1868,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); @@ -1948,7 +1990,7 @@ csihandle(void) { char buf[40]; int len; - switch(csiescseq.mode) { + switch(csiescseq.mode[0]) { default: unknown: fprintf(stderr, "erresc: unknown csi "); @@ -2136,6 +2178,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; } } @@ -2200,7 +2255,7 @@ strhandle(void) { * TODO if defaultbg color is changed, borders * are dirty */ - redraw(0); + redraw(); } return; } @@ -2220,12 +2275,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 @@ -2585,7 +2651,6 @@ tputc(char *c, int len) { c = "\357\277\275"; /* UTF_INVALID */ width = 1; } - control = ISCONTROLC1(unicodep); ascii = unicodep; } @@ -2600,7 +2665,7 @@ tputc(char *c, int len) { * character. */ if(term.esc & ESC_STR) { - if(width == 1 && + if(len == 1 && (ascii == '\a' || ascii == 030 || ascii == 032 || ascii == 033 || ISCONTROLC1(unicodep))) { @@ -2673,13 +2738,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); @@ -2792,7 +2860,7 @@ xresize(int col, int row) { xclear(0, 0, xw.w, xw.h); } -static inline ushort +ushort sixd_to_16bit(int x) { return x == 0 ? 0 : 0x3737 + 0x2828 * x; } @@ -2989,7 +3057,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); @@ -3090,7 +3158,7 @@ xzoomabs(const Arg *arg) { xunloadfonts(); xloadfonts(usedfont, arg->i); cresize(0, 0); - redraw(0); + redraw(); xhints(); } @@ -3207,7 +3275,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; @@ -3390,8 +3458,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; } } @@ -3422,8 +3495,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. @@ -3431,11 +3504,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++; @@ -3506,16 +3581,36 @@ xdrawcursor(void) { /* draw the new one */ if(xw.state & WIN_FOCUSED) { - if(IS_SET(MODE_REVERSE)) { - g.mode |= ATTR_REVERSE; - g.fg = defaultcs; - g.bg = defaultfg; - } + 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); + 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; + } } else { XftDrawRect(xw.draw, &dc.col[defaultcs], borderpx + curx * xw.cw, @@ -3555,16 +3650,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 @@ -3631,7 +3719,7 @@ expose(XEvent *ev) { if(!e->count) xw.state &= ~WIN_REDRAW; } - redraw(0); + redraw(); } void @@ -3687,7 +3775,7 @@ focus(XEvent *ev) { } } -static inline bool +bool match(uint mask, uint state) { return mask == XK_ANY_MOD || mask == (state & ~ignoremod); } @@ -3922,6 +4010,9 @@ run(void) { TIMEDIFF(now, lastblink))); } + drawtimeout.tv_sec = \ + drawtimeout.tv_nsec / 1E9; + drawtimeout.tv_nsec %= (long)1E9; } else { tv = NULL; } @@ -3932,7 +4023,7 @@ run(void) { 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); } @@ -3944,6 +4035,7 @@ main(int argc, char *argv[]) { xw.l = xw.t = 0; xw.isfixed = False; + xw.cursor = 0; ARGBEGIN { case 'a':