X-Git-Url: https://jasonwoof.com/gitweb/?a=blobdiff_plain;f=st.c;h=71e5b83d96f406bcf8c46ad48489c02d0360c7a0;hb=b596d6ba3c50bc379adc298a4e2ba7c122b116ab;hp=c938ff43a65363dd957c354801c89194a24d4648;hpb=b1813b14d956978ae8af37166b3cccc3a7b4b720;p=st.git diff --git a/st.c b/st.c index c938ff4..71e5b83 100644 --- a/st.c +++ b/st.c @@ -85,6 +85,7 @@ enum glyph_attribute { ATTR_GFX = 8, ATTR_ITALIC = 16, ATTR_BLINK = 32, + ATTR_WRAP = 64, }; enum cursor_movement { @@ -98,11 +99,6 @@ enum cursor_state { CURSOR_ORIGIN = 2 }; -enum glyph_state { - GLYPH_SET = 1, - GLYPH_DIRTY = 2 -}; - enum term_mode { MODE_WRAP = 1, MODE_INSERT = 2, @@ -140,6 +136,11 @@ enum selection_type { SEL_RECTANGULAR = 2 }; +enum selection_snap { + SNAP_WORD = 1, + SNAP_LINE = 2 +}; + /* bit macro */ #undef B0 enum { B0=1, B1=2, B2=4, B3=8, B4=16, B5=32, B6=64, B7=128 }; @@ -154,7 +155,6 @@ typedef struct { uchar mode; /* attribute flags */ ushort fg; /* foreground */ ushort bg; /* background */ - uchar state; /* state flags */ } Glyph; typedef Glyph *Line; @@ -238,6 +238,7 @@ typedef struct { typedef struct { int mode; int type; + int snap; int bx, by; int ex, ey; struct { @@ -310,7 +311,7 @@ static void strhandle(void); static void strparse(void); static void strreset(void); -static void tclearregion(int, int, int, int, int); +static void tclearregion(int, int, int, int); static void tcursor(int); static void tdeletechar(int); static void tdeleteline(int); @@ -378,6 +379,7 @@ static void selinit(void); static inline bool selected(int, int); static void selcopy(void); static void selscroll(int, int); +static void selsnap(int, int *, int *, int); static int utf8decode(char *, long *); static int utf8encode(long *, char *); @@ -664,6 +666,23 @@ selected(int x, int y) { } void +selsnap(int mode, int *x, int *y, int direction) { + switch(mode) { + case SNAP_WORD: + while(*x > 0 && *x < term.col-1 + && term.line[*y][*x + direction].c[0] != ' ') { + *x += direction; + } + break; + case SNAP_LINE: + *x = (direction < 0) ? 0 : term.col - 1; + break; + default: + break; + } +} + +void getbuttoninfo(XEvent *e) { int type; uint state = e->xbutton.state &~Button1Mask; @@ -673,6 +692,15 @@ getbuttoninfo(XEvent *e) { sel.ex = x2col(e->xbutton.x); sel.ey = y2row(e->xbutton.y); + if (sel.by < sel.ey + || (sel.by == sel.ey && sel.bx < sel.ex)) { + selsnap(sel.snap, &sel.bx, &sel.by, -1); + selsnap(sel.snap, &sel.ex, &sel.ey, +1); + } else { + selsnap(sel.snap, &sel.ex, &sel.ey, -1); + selsnap(sel.snap, &sel.bx, &sel.by, +1); + } + sel.b.x = sel.by < sel.ey ? sel.bx : sel.ex; sel.b.y = MIN(sel.by, sel.ey); sel.e.x = sel.by < sel.ey ? sel.ex : sel.bx; @@ -736,9 +764,14 @@ mousereport(XEvent *e) { void bpress(XEvent *e) { + struct timeval now; + if(IS_SET(MODE_MOUSE)) { mousereport(e); } else if(e->xbutton.button == Button1) { + gettimeofday(&now, NULL); + + /* Clear previous selection, logically and visually. */ if(sel.bx != -1) { sel.bx = -1; tsetdirt(sel.b.y, sel.e.y); @@ -748,6 +781,38 @@ bpress(XEvent *e) { sel.type = SEL_REGULAR; sel.ex = sel.bx = x2col(e->xbutton.x); sel.ey = sel.by = y2row(e->xbutton.y); + + /* + * Snap handling. + * If user clicks are fasst enough (e.g. below timeouts), + * we ignore if his hand slipped left or down and accidentally + * selected more; we are just snapping to whatever we're + * snapping. + */ + if(TIMEDIFF(now, sel.tclick2) <= tripleclicktimeout) { + sel.snap = SNAP_LINE; + } else if(TIMEDIFF(now, sel.tclick1) <= doubleclicktimeout) { + sel.snap = SNAP_WORD; + } else { + sel.snap = 0; + } + selsnap(sel.snap, &sel.bx, &sel.by, -1); + selsnap(sel.snap, &sel.ex, &sel.ey, 1); + sel.b.x = sel.bx; + sel.b.y = sel.by; + sel.e.x = sel.ex; + sel.e.y = sel.ey; + + /* + * Draw selection, unless it's regular and we don't want to + * make clicks visible + */ + if (sel.snap != 0) { + tsetdirt(sel.b.y, sel.e.y); + draw(); + } + sel.tclick2 = sel.tclick1; + sel.tclick1 = now; } else if(e->xbutton.button == Button4) { ttywrite("\031", 1); } else if(e->xbutton.button == Button5) { @@ -757,8 +822,8 @@ bpress(XEvent *e) { void selcopy(void) { - char *str, *ptr, *p; - int x, y, bufsize, is_selected = 0, size; + char *str, *ptr; + int x, y, bufsize, size; Glyph *gp, *last; if(sel.bx == -1) { @@ -769,28 +834,33 @@ selcopy(void) { /* append every set & selected glyph to the selection */ for(y = sel.b.y; y < sel.e.y + 1; y++) { - is_selected = 0; gp = &term.line[y][0]; last = gp + term.col; - while(--last >= gp && !(last->state & GLYPH_SET)) + while(--last >= gp && !(selected(last - gp, y) && \ + strcmp(last->c, " ") != 0)) /* nothing */; for(x = 0; gp <= last; x++, ++gp) { - if(!selected(x, y)) { + if(!selected(x, y)) continue; - } else { - is_selected = 1; - } - p = (gp->state & GLYPH_SET) ? gp->c : " "; - size = utf8size(p); - memcpy(ptr, p, size); + size = utf8size(gp->c); + memcpy(ptr, gp->c, size); ptr += size; } - /* \n at the end of every selected line except for the last one */ - if(is_selected && y < sel.e.y) - *ptr++ = '\r'; + + /* + * Copy and pasting of line endings is inconsistent + * in the inconsistent terminal and GUI world. + * The best solution seems like to produce '\n' when + * something is copied from st and convert '\n' to + * '\r', when something to be pasted is received by + * st. + * FIXME: Fix the computer world. + */ + if(y < sel.e.y && !((gp-1)->mode & ATTR_WRAP)) + *ptr++ = '\n'; } *ptr = 0; } @@ -801,7 +871,7 @@ void selnotify(XEvent *e) { ulong nitems, ofs, rem; int format; - uchar *data; + uchar *data, *last, *repl; Atom type; ofs = 0; @@ -812,7 +882,21 @@ selnotify(XEvent *e) { fprintf(stderr, "Clipboard allocation failed\n"); return; } - ttywrite((const char *) data, nitems * format / 8); + + /* + * As seen in selcopy: + * Line endings are inconsistent in the terminal and GUI world + * copy and pasting. When receiving some selection data, + * replace all '\n' with '\r'. + * FIXME: Fix the computer world. + */ + repl = data; + last = data + nitems * format / 8; + while((repl = memchr(repl, '\n', last - repl))) { + *repl++ = '\r'; + } + + ttywrite((const char *)data, nitems * format / 8); XFree(data); /* number of 32-bit chunks returned */ ofs += nitems * format / 32; @@ -893,8 +977,6 @@ xsetsel(char *str) { void brelease(XEvent *e) { - struct timeval now; - if(IS_SET(MODE_MOUSE)) { mousereport(e); return; @@ -908,37 +990,10 @@ brelease(XEvent *e) { term.dirty[sel.ey] = 1; if(sel.bx == sel.ex && sel.by == sel.ey) { sel.bx = -1; - gettimeofday(&now, NULL); - - if(TIMEDIFF(now, sel.tclick2) <= tripleclicktimeout) { - /* triple click on the line */ - sel.b.x = sel.bx = 0; - sel.e.x = sel.ex = term.col; - sel.b.y = sel.e.y = sel.ey; - selcopy(); - } else if(TIMEDIFF(now, sel.tclick1) <= doubleclicktimeout) { - /* double click to select word */ - sel.bx = sel.ex; - while(sel.bx > 0 && term.line[sel.ey][sel.bx-1].state & GLYPH_SET && - term.line[sel.ey][sel.bx-1].c[0] != ' ') { - sel.bx--; - } - sel.b.x = sel.bx; - while(sel.ex < term.col-1 && term.line[sel.ey][sel.ex+1].state & GLYPH_SET && - term.line[sel.ey][sel.ex+1].c[0] != ' ') { - sel.ex++; - } - sel.e.x = sel.ex; - sel.b.y = sel.e.y = sel.ey; - selcopy(); - } } else { selcopy(); } } - - memcpy(&sel.tclick2, &sel.tclick1, sizeof(struct timeval)); - gettimeofday(&sel.tclick1, NULL); } void @@ -1164,30 +1219,17 @@ treset(void) { term.bot = term.row - 1; term.mode = MODE_WRAP; - tclearregion(0, 0, term.col-1, term.row-1, 0); + tclearregion(0, 0, term.col-1, term.row-1); tmoveto(0, 0); tcursor(CURSOR_SAVE); } void tnew(int col, int row) { - /* set screen size */ - term.row = row; - term.col = col; - term.line = xmalloc(term.row * sizeof(Line)); - term.alt = xmalloc(term.row * sizeof(Line)); - term.dirty = xmalloc(term.row * sizeof(*term.dirty)); - term.tabs = xmalloc(term.col * sizeof(*term.tabs)); - - for(row = 0; row < term.row; row++) { - term.line[row] = xmalloc(term.col * sizeof(Glyph)); - term.alt [row] = xmalloc(term.col * sizeof(Glyph)); - term.dirty[row] = 0; - } - + memset(&term, 0, sizeof(Term)); + tresize(col, row); term.numlock = 1; - memset(term.tabs, 0, term.col * sizeof(*term.tabs)); - /* setup screen */ + treset(); } @@ -1208,7 +1250,7 @@ tscrolldown(int orig, int n) { LIMIT(n, 0, term.bot-orig+1); - tclearregion(0, term.bot-n+1, term.col-1, term.bot, 0); + tclearregion(0, term.bot-n+1, term.col-1, term.bot); for(i = term.bot; i >= orig+n; i--) { temp = term.line[i]; @@ -1228,7 +1270,7 @@ tscrollup(int orig, int n) { Line temp; LIMIT(n, 0, term.bot-orig+1); - tclearregion(0, orig, term.col-1, orig+n-1, 0); + tclearregion(0, orig, term.col-1, orig+n-1); for(i = orig; i <= term.bot-n; i++) { temp = term.line[i]; @@ -1362,11 +1404,10 @@ tsetchar(char *c, Glyph *attr, int x, int y) { 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 -tclearregion(int x1, int y1, int x2, int y2, int bce) { +tclearregion(int x1, int y1, int x2, int y2) { int x, y, temp; if(x1 > x2) @@ -1382,13 +1423,8 @@ tclearregion(int x1, int y1, int x2, int y2, int bce) { for(y = y1; y <= y2; y++) { term.dirty[y] = 1; for(x = x1; x <= x2; x++) { - if(bce) { - term.line[y][x] = term.c.attr; - memcpy(term.line[y][x].c, " ", 2); - term.line[y][x].state |= GLYPH_SET; - } else { - term.line[y][x].state = 0; - } + term.line[y][x] = term.c.attr; + memcpy(term.line[y][x].c, " ", 2); } } } @@ -1402,13 +1438,13 @@ tdeletechar(int n) { term.dirty[term.c.y] = 1; if(src >= term.col) { - tclearregion(term.c.x, term.c.y, term.col-1, term.c.y, 0); + tclearregion(term.c.x, term.c.y, term.col-1, term.c.y); return; } memmove(&term.line[term.c.y][dst], &term.line[term.c.y][src], size * sizeof(Glyph)); - tclearregion(term.col-n, term.c.y, term.col-1, term.c.y, 0); + tclearregion(term.col-n, term.c.y, term.col-1, term.c.y); } void @@ -1420,13 +1456,13 @@ tinsertblank(int n) { term.dirty[term.c.y] = 1; if(dst >= term.col) { - tclearregion(term.c.x, term.c.y, term.col-1, term.c.y, 0); + tclearregion(term.c.x, term.c.y, term.col-1, term.c.y); return; } memmove(&term.line[term.c.y][dst], &term.line[term.c.y][src], size * sizeof(Glyph)); - tclearregion(src, term.c.y, dst - 1, term.c.y, 0); + tclearregion(src, term.c.y, dst - 1, term.c.y); } void @@ -1452,8 +1488,9 @@ 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_REVERSE | ATTR_UNDERLINE \ + | ATTR_BOLD | ATTR_ITALIC \ + | ATTR_BLINK); term.c.attr.fg = defaultfg; term.c.attr.bg = defaultbg; break; @@ -1622,7 +1659,7 @@ tsetmode(bool priv, bool set, int *args, int narg) { alt = IS_SET(MODE_ALTSCREEN); if(alt) { tclearregion(0, 0, term.col-1, - term.row-1, 0); + term.row-1); } if(set ^ alt) /* set is always 1 or 0 */ tswapscreen(); @@ -1741,19 +1778,19 @@ csihandle(void) { sel.bx = -1; switch(csiescseq.arg[0]) { case 0: /* below */ - tclearregion(term.c.x, term.c.y, term.col-1, term.c.y, 1); + tclearregion(term.c.x, term.c.y, term.col-1, term.c.y); if(term.c.y < term.row-1) { tclearregion(0, term.c.y+1, term.col-1, - term.row-1, 1); + term.row-1); } break; case 1: /* above */ if(term.c.y > 1) - tclearregion(0, 0, term.col-1, term.c.y-1, 1); - tclearregion(0, term.c.y, term.c.x, term.c.y, 1); + tclearregion(0, 0, term.col-1, term.c.y-1); + tclearregion(0, term.c.y, term.c.x, term.c.y); break; case 2: /* all */ - tclearregion(0, 0, term.col-1, term.row-1, 1); + tclearregion(0, 0, term.col-1, term.row-1); break; default: goto unknown; @@ -1763,13 +1800,13 @@ csihandle(void) { switch(csiescseq.arg[0]) { case 0: /* right */ tclearregion(term.c.x, term.c.y, term.col-1, - term.c.y, 1); + term.c.y); break; case 1: /* left */ - tclearregion(0, term.c.y, term.c.x, term.c.y, 1); + tclearregion(0, term.c.y, term.c.x, term.c.y); break; case 2: /* all */ - tclearregion(0, term.c.y, term.col-1, term.c.y, 1); + tclearregion(0, term.c.y, term.col-1, term.c.y); break; } break; @@ -1795,7 +1832,7 @@ csihandle(void) { case 'X': /* ECH -- Erase char */ DEFAULT(csiescseq.arg[0], 1); tclearregion(term.c.x, term.c.y, - term.c.x + csiescseq.arg[0] - 1, term.c.y, 1); + term.c.x + csiescseq.arg[0] - 1, term.c.y); break; case 'P': /* DCH -- Delete char */ DEFAULT(csiescseq.arg[0], 1); @@ -2236,8 +2273,10 @@ tputc(char *c, int len) { return; if(sel.bx != -1 && BETWEEN(term.c.y, sel.by, sel.ey)) sel.bx = -1; - if(IS_SET(MODE_WRAP) && term.c.state & CURSOR_WRAPNEXT) - tnewline(1); /* always go to first col */ + if(IS_SET(MODE_WRAP) && (term.c.state & CURSOR_WRAPNEXT)) { + term.line[term.c.y][term.c.x].mode |= ATTR_WRAP; + tnewline(1); + } if(IS_SET(MODE_INSERT) && term.c.x+1 < term.col) { memmove(&term.line[term.c.y][term.c.x+1], @@ -2255,11 +2294,12 @@ tputc(char *c, int len) { int tresize(int col, int row) { - int i, x; + int i; int minrow = MIN(row, term.row); int mincol = MIN(col, term.col); int slide = term.c.y - row + 1; bool *bp; + Line *orig; if(col < 1 || row < 1) return 0; @@ -2295,10 +2335,6 @@ tresize(int col, int row) { term.dirty[i] = 1; term.line[i] = xrealloc(term.line[i], col * sizeof(Glyph)); term.alt[i] = xrealloc(term.alt[i], col * sizeof(Glyph)); - for(x = mincol; x < col; x++) { - term.line[i][x].state = 0; - term.alt[i][x].state = 0; - } } /* allocate any new rows */ @@ -2323,6 +2359,17 @@ 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; + do { + if(mincol < col && 0 < minrow) { + tclearregion(mincol, 0, col - 1, minrow - 1); + } + if(0 < col && minrow < row) { + tclearregion(0, minrow, col - 1, row - 1); + } + tswapscreen(); + } while(orig != term.line); return (slide > 0); } @@ -2335,10 +2382,8 @@ xresize(int col, int row) { XFreePixmap(xw.dpy, xw.buf); xw.buf = XCreatePixmap(xw.dpy, xw.win, xw.w, xw.h, DefaultDepth(xw.dpy, xw.scr)); - XSetForeground(xw.dpy, dc.gc, dc.col[IS_SET(MODE_REVERSE) ? defaultfg : defaultbg].pixel); - XFillRectangle(xw.dpy, xw.buf, dc.gc, 0, 0, xw.w, xw.h); - XftDrawChange(xw.draw, xw.buf); + xclear(0, 0, xw.w, xw.h); } static inline ushort @@ -2926,33 +2971,47 @@ void xdrawcursor(void) { static int oldx = 0, oldy = 0; int sl; - Glyph g = {{' '}, ATTR_NULL, defaultbg, defaultcs, 0}; + Glyph g = {{' '}, ATTR_NULL, defaultbg, defaultcs}; LIMIT(oldx, 0, term.col-1); LIMIT(oldy, 0, term.row-1); - if(term.line[term.c.y][term.c.x].state & GLYPH_SET) - memcpy(g.c, term.line[term.c.y][term.c.x].c, UTF_SIZ); + memcpy(g.c, term.line[term.c.y][term.c.x].c, UTF_SIZ); /* remove the old cursor */ - if(term.line[oldy][oldx].state & GLYPH_SET) { - sl = utf8size(term.line[oldy][oldx].c); - xdraws(term.line[oldy][oldx].c, term.line[oldy][oldx], oldx, - oldy, 1, sl); - } else { - xtermclear(oldx, oldy, oldx, oldy); - } + sl = utf8size(term.line[oldy][oldx].c); + xdraws(term.line[oldy][oldx].c, term.line[oldy][oldx], oldx, + oldy, 1, sl); /* draw the new one */ if(!(IS_SET(MODE_HIDE))) { - if(!(xw.state & WIN_FOCUSED)) - g.bg = defaultucs; - - if(IS_SET(MODE_REVERSE)) - g.mode |= ATTR_REVERSE, g.fg = defaultcs, g.bg = defaultfg; + if(xw.state & WIN_FOCUSED) { + if(IS_SET(MODE_REVERSE)) { + g.mode |= ATTR_REVERSE; + g.fg = defaultcs; + g.bg = defaultfg; + } - sl = utf8size(g.c); - xdraws(g.c, g, term.c.x, term.c.y, 1, sl); + sl = utf8size(g.c); + xdraws(g.c, g, term.c.x, term.c.y, 1, sl); + } else { + XftDrawRect(xw.draw, &dc.col[defaultcs], + borderpx + term.c.x * xw.cw, + borderpx + term.c.y * xw.ch, + xw.cw - 1, 1); + XftDrawRect(xw.draw, &dc.col[defaultcs], + borderpx + term.c.x * xw.cw, + borderpx + term.c.y * xw.ch, + 1, xw.ch - 1); + XftDrawRect(xw.draw, &dc.col[defaultcs], + borderpx + (term.c.x + 1) * xw.cw - 1, + borderpx + term.c.y * xw.ch, + 1, xw.ch - 1); + XftDrawRect(xw.draw, &dc.col[defaultcs], + borderpx + term.c.x * xw.cw, + borderpx + (term.c.y + 1) * xw.ch - 1, + xw.cw, 1); + } oldx = term.c.x, oldy = term.c.y; } } @@ -3020,23 +3079,20 @@ drawregion(int x1, int y1, int x2, int y2) { new = term.line[y][x]; if(ena_sel && *(new.c) && selected(x, y)) new.mode ^= ATTR_REVERSE; - if(ib > 0 && (!(new.state & GLYPH_SET) - || ATTRCMP(base, new) + if(ib > 0 && (ATTRCMP(base, new) || ib >= DRAW_BUF_SIZ-UTF_SIZ)) { xdraws(buf, base, ox, y, ic, ib); ic = ib = 0; } - if(new.state & GLYPH_SET) { - if(ib == 0) { - ox = x; - base = new; - } - - sl = utf8size(new.c); - memcpy(buf+ib, new.c, sl); - ib += sl; - ++ic; + if(ib == 0) { + ox = x; + base = new; } + + sl = utf8size(new.c); + memcpy(buf+ib, new.c, sl); + ib += sl; + ++ic; } if(ib > 0) xdraws(buf, base, ox, y, ic, ib); @@ -3332,7 +3388,8 @@ main(int argc, char *argv[]) { break; case 'e': /* eat all remaining arguments */ - opt_cmd = &argv[1]; + if(argc > 1) + opt_cmd = &argv[1]; goto run; case 'f': opt_font = EARGF(usage()); @@ -3376,6 +3433,8 @@ run: xinit(); ttynew(); selinit(); + if(xw.isfixed) + cresize(xw.h, xw.w); run(); return 0;