X-Git-Url: https://jasonwoof.com/gitweb/?a=blobdiff_plain;f=st.c;h=e1934435b6f8aada31ffaed553da12d9c02a2966;hb=ab9c9a4e3af246d0b4c6419bffb02a23fd5c1462;hp=bc17df35d4285aeaae547059e2e44d733a17024f;hpb=189a81caa11a278079fde3e610870e07870d8968;p=st.git diff --git a/st.c b/st.c index bc17df3..e193443 100644 --- a/st.c +++ b/st.c @@ -34,8 +34,12 @@ #endif #define USAGE \ - "st-" VERSION ", (c) 2010-2011 st engineers\n" \ - "usage: st [-t title] [-c class] [-v] [-e command...]\n" + "st " VERSION " (c) 2010-2011 st engineers\n" \ + "usage: st [-t title] [-c class] [-w windowid] [-v] [-e command...]\n" + +/* XEMBED messages */ +#define XEMBED_FOCUS_IN 4 +#define XEMBED_FOCUS_OUT 5 /* Arbitrary sizes */ #define ESC_TITLE_SIZ 256 @@ -43,6 +47,8 @@ #define ESC_ARG_SIZ 16 #define DRAW_BUF_SIZ 1024 #define UTF_SIZ 4 +#define XK_NO_MOD UINT_MAX +#define XK_ANY_MOD 0 #define SERRNO strerror(errno) #define MIN(a, b) ((a) < (b) ? (a) : (b)) @@ -105,6 +111,7 @@ typedef struct { int col; /* nb col */ Line* line; /* screen */ Line* alt; /* alternate screen */ + char* dirty; /* dirtyness of lines */ TCursor c; /* cursor */ int top; /* top scroll limit */ int bot; /* bottom scroll limit */ @@ -120,6 +127,7 @@ typedef struct { Colormap cmap; Window win; Pixmap buf; + Atom xembed; XIM xim; XIC xic; int scr; @@ -196,6 +204,7 @@ static void tsetattr(int*, int); static void tsetchar(char*); static void tsetscroll(int, int); static void tswapscreen(void); +static void tfulldirt(void); static void ttynew(void); static void ttyread(void); @@ -217,6 +226,7 @@ static void visibility(XEvent *); static void unmap(XEvent *); static char* kmap(KeySym, unsigned int state); static void kpress(XEvent *); +static void cmessage(XEvent *); static void resize(XEvent *); static void focus(XEvent *); static void brelease(XEvent *); @@ -229,6 +239,7 @@ static void selinit(void); static inline int selected(int, int); static void selcopy(void); static void selpaste(); +static void selscroll(int, int); static int utf8decode(char *, long *); static int utf8encode(long *, char *); @@ -237,6 +248,7 @@ static int isfullutf8(char *, int); static void (*handler[LASTEvent])(XEvent *) = { [KeyPress] = kpress, + [ClientMessage] = cmessage, [ConfigureNotify] = resize, [VisibilityNotify] = visibility, [UnmapNotify] = unmap, @@ -260,6 +272,7 @@ static pid_t pid; static Selection sel; static char **opt_cmd = NULL; static char *opt_title = NULL; +static char *opt_embed = NULL; static char *opt_class = NULL; int @@ -450,6 +463,9 @@ bpress(XEvent *e) { if(IS_SET(MODE_MOUSE)) mousereport(e); else if(e->xbutton.button == Button1) { + if(sel.bx != -1) + for(int i=sel.b.y; i<=sel.e.y; i++) + term.dirty[i] = 1; sel.mode = 1; sel.ex = sel.bx = X2COL(e->xbutton.x); sel.ey = sel.by = Y2ROW(e->xbutton.y); @@ -483,8 +499,7 @@ selcopy(void) { void selnotify(XEvent *e) { - unsigned long nitems; - unsigned long ofs, rem; + unsigned long nitems, ofs, rem; int format; unsigned char *data; Atom type; @@ -571,6 +586,7 @@ brelease(XEvent *e) { else if(e->xbutton.button == Button1) { sel.mode = 0; getbuttoninfo(e, NULL, &sel.ex, &sel.ey); + term.dirty[sel.ey] = 1; if(sel.bx == sel.ex && sel.by == sel.ey) { struct timeval now; sel.bx = -1; @@ -615,7 +631,9 @@ bmotion(XEvent *e) { if(oldey != sel.ey || oldex != sel.ex) { int starty = MIN(oldey, sel.ey); int endy = MAX(oldey, sel.ey); - drawregion(0, (starty > 0 ? starty : 0), term.col, (sel.ey < term.row ? endy+1 : term.row)); + for(int i=starty; i<=endy; i++) + term.dirty[i] = 1; + draw(); } } } @@ -739,6 +757,14 @@ ttyresize(int x, int y) { } void +tfulldirt(void) +{ + int i; + for(i = 0; i < term.row; i++) + term.dirty[i] = 1; +} + +void tcursor(int mode) { static TCursor c; @@ -767,9 +793,12 @@ tnew(int col, int row) { term.row = row, term.col = col; term.line = malloc(term.row * sizeof(Line)); term.alt = malloc(term.row * sizeof(Line)); - for(row = 0 ; row < term.row; row++) { + term.dirty = malloc(term.row * sizeof(*term.dirty)); + + for(row = 0; row < term.row; row++) { term.line[row] = malloc(term.col * sizeof(Glyph)); term.alt [row] = malloc(term.col * sizeof(Glyph)); + term.dirty[row] = 0; } /* setup screen */ treset(); @@ -781,6 +810,7 @@ tswapscreen(void) { term.line = term.alt; term.alt = tmp; term.mode ^= MODE_ALTSCREEN; + tfulldirt(); } void @@ -796,7 +826,12 @@ tscrolldown(int orig, int n) { temp = term.line[i]; term.line[i] = term.line[i-n]; term.line[i-n] = temp; + + term.dirty[i] = 1; + term.dirty[i-n] = 1; } + + selscroll(orig, n); } void @@ -811,6 +846,34 @@ tscrollup(int orig, int n) { temp = term.line[i]; term.line[i] = term.line[i+n]; term.line[i+n] = temp; + + term.dirty[i] = 1; + term.dirty[i+n] = 1; + } + + selscroll(orig, -n); +} + +void +selscroll(int orig, int n) { + if(sel.bx == -1) + return; + + if(BETWEEN(sel.by, orig, term.bot) || BETWEEN(sel.ey, orig, term.bot)) { + if((sel.by += n) > term.bot || (sel.ey += n) < term.top) { + sel.bx = -1; + return; + } + if(sel.by < term.top) { + sel.by = term.top; + sel.bx = 0; + } + if(sel.ey > term.bot) { + sel.ey = term.bot; + sel.ex = term.col; + } + sel.b.y = sel.by, sel.b.x = sel.bx; + sel.e.y = sel.ey, sel.e.x = sel.ex; } } @@ -859,6 +922,7 @@ tmoveto(int x, int y) { void tsetchar(char *c) { + term.dirty[term.c.y] = 1; term.line[term.c.y][term.c.x] = term.c.attr; memcpy(term.line[term.c.y][term.c.x].c, c, UTF_SIZ); term.line[term.c.y][term.c.x].state |= GLYPH_SET; @@ -878,9 +942,11 @@ tclearregion(int x1, int y1, int x2, int y2) { LIMIT(y1, 0, term.row-1); LIMIT(y2, 0, term.row-1); - for(y = y1; y <= y2; y++) + for(y = y1; y <= y2; y++) { + term.dirty[y] = 1; for(x = x1; x <= x2; x++) term.line[y][x].state = 0; + } } void @@ -888,6 +954,8 @@ tdeletechar(int n) { int src = term.c.x + n; int dst = term.c.x; int size = term.col - src; + + term.dirty[term.c.y] = 1; if(src >= term.col) { tclearregion(term.c.x, term.c.y, term.col-1, term.c.y); @@ -903,6 +971,8 @@ tinsertblank(int n) { int dst = src + n; int size = term.col - dst; + term.dirty[term.c.y] = 1; + if(dst >= term.col) { tclearregion(term.c.x, term.c.y, term.col-1, term.c.y); return; @@ -1068,6 +1138,7 @@ csihandle(void) { break; /* XXX: (CSI n I) CHT -- Cursor Forward Tabulation tab stops */ case 'J': /* ED -- Clear screen */ + sel.bx = -1; switch(escseq.arg[0]) { case 0: /* below */ tclearregion(term.c.x, term.c.y, term.col-1, term.c.y); @@ -1373,6 +1444,8 @@ tputc(char *c) { } } } else { + if(sel.bx != -1 && BETWEEN(term.c.y, sel.by, sel.ey)) + sel.bx = -1; switch(ascii) { case '\t': tputtab(); @@ -1440,9 +1513,11 @@ tresize(int col, int row) { /* resize to new height */ term.line = realloc(term.line, row * sizeof(Line)); term.alt = realloc(term.alt, row * sizeof(Line)); + term.dirty = realloc(term.dirty, row * sizeof(*term.dirty)); /* resize each row to new width, zero-pad if needed */ for(i = 0; i < minrow; i++) { + term.dirty[i] = 1; term.line[i] = realloc(term.line[i], col * sizeof(Glyph)); term.alt[i] = realloc(term.alt[i], col * sizeof(Glyph)); for(x = mincol; x < col; x++) { @@ -1453,6 +1528,7 @@ tresize(int col, int row) { /* allocate any new rows */ for(/* i == minrow */; i < row; i++) { + term.dirty[i] = 1; term.line[i] = calloc(col, sizeof(Glyph)); term.alt [i] = calloc(col, sizeof(Glyph)); } @@ -1463,6 +1539,7 @@ tresize(int col, int row) { tmoveto(term.c.x, term.c.y); /* reset scrolling region */ tsetscroll(0, row-1); + return (slide > 0); } @@ -1502,7 +1579,7 @@ xloadcols(void) { XColor color; unsigned long white = WhitePixel(xw.dpy, xw.scr); - for(i = 0; i < 16; i++) { + for(i = 0; i < LEN(colorname); i++) { if(!XAllocNamedColor(xw.dpy, xw.cmap, colorname[i], &color, &color)) { dc.col[i] = white; fprintf(stderr, "Could not allocate color '%s'\n", colorname[i]); @@ -1607,6 +1684,7 @@ void xinit(void) { XSetWindowAttributes attrs; Cursor cursor; + Window parent; if(!(xw.dpy = XOpenDisplay(NULL))) die("Can't open display\n"); @@ -1624,8 +1702,8 @@ xinit(void) { xloadcols(); /* window - default size */ - xw.bufh = 24 * xw.ch; - xw.bufw = 80 * xw.cw; + xw.bufh = term.row * xw.ch; + xw.bufw = term.col * xw.cw; xw.h = xw.bufh + 2*BORDER; xw.w = xw.bufw + 2*BORDER; @@ -1634,10 +1712,12 @@ xinit(void) { attrs.bit_gravity = NorthWestGravity; attrs.event_mask = FocusChangeMask | KeyPressMask | ExposureMask | VisibilityChangeMask | StructureNotifyMask - | ButtonMotionMask | ButtonPressMask | ButtonReleaseMask; + | ButtonMotionMask | ButtonPressMask | ButtonReleaseMask + | EnterWindowMask | LeaveWindowMask; attrs.colormap = xw.cmap; - xw.win = XCreateWindow(xw.dpy, XRootWindow(xw.dpy, xw.scr), 0, 0, + parent = opt_embed ? strtol(opt_embed, NULL, 0) : XRootWindow(xw.dpy, xw.scr); + xw.win = XCreateWindow(xw.dpy, parent, 0, 0, xw.w, xw.h, 0, XDefaultDepth(xw.dpy, xw.scr), InputOutput, XDefaultVisual(xw.dpy, xw.scr), CWBackPixel | CWBorderPixel | CWBitGravity | CWEventMask @@ -1661,6 +1741,8 @@ xinit(void) { &(XColor){.red = 0xffff, .green = 0xffff, .blue = 0xffff}, &(XColor){.red = 0x0000, .green = 0x0000, .blue = 0x0000}); + xw.xembed = XInternAtom(xw.dpy, "_XEMBED", False); + XStoreName(xw.dpy, xw.win, opt_title ? opt_title : "st"); XMapWindow(xw.dpy, xw.win); xhints(); @@ -1748,8 +1830,11 @@ drawregion(int x1, int y1, int x2, int y2) { if(!(xw.state & WIN_VISIBLE)) return; - xclear(x1, y1, x2-1, y2-1); for(y = y1; y < y2; y++) { + if(!term.dirty[y]) + continue; + xclear(0, y, term.col, y); + term.dirty[y] = 0; base = term.line[y][0]; ic = ib = ox = 0; for(x = x1; x < x2; x++) { @@ -1757,7 +1842,7 @@ drawregion(int x1, int y1, int x2, int y2) { if(sel.bx != -1 && *(new.c) && selected(x, y)) new.mode ^= ATTR_REVERSE; if(ib > 0 && (!(new.state & GLYPH_SET) || ATTRCMP(base, new) || - ib >= DRAW_BUF_SIZ-UTF_SIZ)) { + ib >= DRAW_BUF_SIZ-UTF_SIZ)) { xdraws(buf, base, ox, y, ic, ib); ic = ib = 0; } @@ -1776,7 +1861,7 @@ drawregion(int x1, int y1, int x2, int y2) { xdraws(buf, base, ox, y, ic, ib); } xdrawcursor(); - XCopyArea(xw.dpy, xw.buf, xw.win, dc.gc, 0, 0, xw.bufw, xw.bufh, BORDER, BORDER); + XCopyArea(xw.dpy, xw.buf, xw.win, dc.gc, x1*xw.cw, y1*xw.ch, x2*xw.cw, y2*xw.ch, BORDER, BORDER); } void @@ -1828,9 +1913,12 @@ focus(XEvent *ev) { char* kmap(KeySym k, unsigned int state) { int i; - for(i = 0; i < LEN(key); i++) - if(key[i].k == k && (key[i].mask == 0 || key[i].mask & state)) + state &= ~Mod2Mask; + for(i = 0; i < LEN(key); i++) { + unsigned int mask = key[i].mask; + if(key[i].k == k && ((state & mask) == mask || (mask == XK_NO_MOD && !state))) return (char*)key[i].s; + } return NULL; } @@ -1885,6 +1973,21 @@ kpress(XEvent *ev) { } void +cmessage(XEvent *e) { + /* See xembed specs + http://standards.freedesktop.org/xembed-spec/xembed-spec-latest.html */ + if (e->xclient.message_type == xw.xembed && e->xclient.format == 32) { + if (e->xclient.data.l[1] == XEMBED_FOCUS_IN) { + xw.state |= WIN_FOCUSED; + xseturgency(0); + } else if (e->xclient.data.l[1] == XEMBED_FOCUS_OUT) { + xw.state &= ~WIN_FOCUSED; + } + draw(); + } +} + +void resize(XEvent *e) { int col, row; @@ -1944,6 +2047,9 @@ main(int argc, char *argv[]) { case 'c': if(++i < argc) opt_class = argv[i]; break; + case 'w': + if(++i < argc) opt_embed = argv[i]; + break; case 'e': /* eat every remaining arguments */ if(++i < argc) opt_cmd = &argv[i];