Hi, As some background, I've been using st on an Arm device (Genesi Efika MX) which has relatively low specs compared to your average x86/amd64 computer. st should be ideal because of how small it is compared to xterm or rxvt but the reality is that its rendering is quite a bit slower. Under heavy processor load, such as building a package, I can literally watch the terminal window refreshing (ie I can see a line traveling down the window, above which the window is refreshed and below which the window is dirty...."tearing").
I've changed the code to use the X Double Buffer Extension (Xdbe). Instead of rendering to a Pixbuf and then copying that Pixbuf to the window, which is a relatively slow operation, it now renders to an XdbeBackBuffer and then simply swaps buffers with the window. The result is tear-free and apparently faster rendering even on an 800mhz Arm cpu. A side effect is that the code is a bit shorter as a result too. For example, xresize() does almost nothing now, since the XdbeBackBuffer is automatically resized with the window (see the DBE manpage), so several Xlib calls can be removed from that function. The provided diff is against the default branch, however I also got it working for the xft branch. I can provide a diff for that too if you want but the only extra difference is that any Xft draw-related calls should work on xw.buf rather than xw.win (including the one in xresize()). LIBS in config.mk should have -lXext appended. I forgot to include that in the patch. A few caveats: 1) my C skills are a bit rusty, 2) I'm unfamiliar with Mercurial, 3) I'm completely new to Xlib. So, while it Works For Me (tm), you might want to test it a bit and let me know if I messed up something. I'm going to keep hacking on it to try to improve further the speed if I can. Cheers, Brandon Invergo
diff -r e1414acbe547 st.c --- a/st.c Mon Feb 27 12:48:13 2012 +0100 +++ b/st.c Fri Apr 20 09:10:28 2012 +0200 @@ -24,6 +24,7 @@ #include <X11/Xutil.h> #include <X11/cursorfont.h> #include <X11/keysym.h> +#include <X11/extensions/Xdbe.h> #if defined(__linux) #include <pty.h> @@ -178,7 +179,7 @@ Display* dpy; Colormap cmap; Window win; - Pixmap buf; + XdbeBackBuffer buf; Atom xembed; XIM xim; XIC xic; @@ -270,7 +271,7 @@ static void xdraws(char *, Glyph, int, int, int, int); static void xhints(void); static void xclear(int, int, int, int); -static void xcopy(int, int, int, int); +static void xcopy(); static void xdrawcursor(void); static void xinit(void); static void xloadcols(void); @@ -1620,32 +1621,8 @@ void xresize(int col, int row) { - Pixmap newbuf; - int oldw, oldh; - - oldw = xw.bufw; - oldh = xw.bufh; xw.bufw = MAX(1, col * xw.cw); xw.bufh = MAX(1, row * xw.ch); - newbuf = XCreatePixmap(xw.dpy, xw.win, xw.bufw, xw.bufh, XDefaultDepth(xw.dpy, xw.scr)); - XCopyArea(xw.dpy, xw.buf, newbuf, dc.gc, 0, 0, xw.bufw, xw.bufh, 0, 0); - XFreePixmap(xw.dpy, xw.buf); - XSetForeground(xw.dpy, dc.gc, dc.col[DefaultBG]); - if(xw.bufw > oldw) - XFillRectangle(xw.dpy, newbuf, dc.gc, oldw, 0, - xw.bufw-oldw, MIN(xw.bufh, oldh)); - else if(xw.bufw < oldw && (BORDER > 0 || xw.w > xw.bufw)) - XClearArea(xw.dpy, xw.win, BORDER+xw.bufw, BORDER, - xw.w-xw.bufh-BORDER, BORDER+MIN(xw.bufh, oldh), - False); - if(xw.bufh > oldh) - XFillRectangle(xw.dpy, newbuf, dc.gc, 0, oldh, - xw.bufw, xw.bufh-oldh); - else if(xw.bufh < oldh && (BORDER > 0 || xw.h > xw.bufh)) - XClearArea(xw.dpy, xw.win, BORDER, BORDER+xw.bufh, - xw.w-2*BORDER, xw.h-xw.bufh-BORDER, - False); - xw.buf = newbuf; } void @@ -1801,7 +1778,7 @@ CWBackPixel | CWBorderPixel | CWBitGravity | CWEventMask | CWColormap, &attrs); - xw.buf = XCreatePixmap(xw.dpy, xw.win, xw.bufw, xw.bufh, XDefaultDepth(xw.dpy, xw.scr)); + xw.buf = XdbeAllocateBackBufferName(xw.dpy, xw.win, XdbeCopied); /* input methods */ @@ -1871,10 +1848,10 @@ /* copy buffer pixmap to screen pixmap */ void -xcopy(int x, int y, int cols, int rows) { - int src_x = x*xw.cw, src_y = y*xw.ch, src_w = cols*xw.cw, src_h = rows*xw.ch; - int dst_x = BORDER+src_x, dst_y = BORDER+src_y; - XCopyArea(xw.dpy, xw.buf, xw.win, dc.gc, src_x, src_y, src_w, src_h, dst_x, dst_y); +xcopy() { + XdbeSwapInfo swpinfo[1] = {{xw.win, XdbeCopied}}; + XdbeSwapBuffers(xw.dpy, swpinfo, 1); + } void @@ -1918,6 +1895,7 @@ void draw() { drawregion(0, 0, term.col, term.row); + xcopy(); gettimeofday(&xw.lastdraw, NULL); } @@ -1959,7 +1937,6 @@ } if(ib > 0) xdraws(buf, base, ox, y, ic, ib); - xcopy(0, y, term.col, 1); } xdrawcursor(); } @@ -1968,13 +1945,10 @@ expose(XEvent *ev) { XExposeEvent *e = &ev->xexpose; if(xw.state & WIN_REDRAW) { - if(!e->count) { + if(!e->count) xw.state &= ~WIN_REDRAW; - xcopy(0, 0, term.col, term.row); - } - } else - XCopyArea(xw.dpy, xw.buf, xw.win, dc.gc, e->x-BORDER, e->y-BORDER, - e->width, e->height, e->x, e->y); + } + xcopy(); } void