X-Git-Url: https://jasonwoof.com/gitweb/?a=blobdiff_plain;f=st.c;h=a0bd69fd042bae8a7df68d8cecca8fdb69b66cc4;hb=eb360e5fd151107ca8088a9c1aad33aa373c4fb0;hp=63c3f853d15711f11c54b534ecf60ec752b6d1f3;hpb=b1d383a0a344e35dad1064e129f70d52bddfd7b2;p=st.git diff --git a/st.c b/st.c index 63c3f85..a0bd69f 100644 --- a/st.c +++ b/st.c @@ -25,6 +25,9 @@ #include #include #include +#include +#define Glyph Glyph_ +#define Font Font_ #if defined(__linux) #include @@ -48,13 +51,11 @@ #define ESC_ARG_SIZ 16 #define STR_BUF_SIZ 256 #define STR_ARG_SIZ 16 -#define DRAW_BUF_SIZ 1024 +#define DRAW_BUF_SIZ 20*1024 #define UTF_SIZ 4 #define XK_NO_MOD UINT_MAX #define XK_ANY_MOD 0 -#define SELECT_TIMEOUT (20*1000) /* 20 ms */ -#define DRAW_TIMEOUT (20*1000) /* 20 ms */ #define REDRAW_TIMEOUT (80*1000) /* 80 ms */ #define SERRNO strerror(errno) @@ -76,6 +77,8 @@ enum glyph_attribute { ATTR_UNDERLINE = 2, ATTR_BOLD = 4, ATTR_GFX = 8, + ATTR_ITALIC = 16, + ATTR_BLINK = 32, }; enum cursor_movement { @@ -107,7 +110,8 @@ enum term_mode { MODE_MOUSEBTN = 32, MODE_MOUSEMOTION = 64, MODE_MOUSE = 32|64, - MODE_REVERSE = 128 + MODE_REVERSE = 128, + MODE_KBDLOCK = 256 }; enum escape_state { @@ -195,15 +199,17 @@ typedef struct { Atom xembed; XIM xim; XIC xic; + XftDraw *xft_draw; + Visual *vis; int scr; - Bool isfixed; /* is fixed geometry? */ + bool isfixed; /* is fixed geometry? */ int fx, fy, fw, fh; /* fixed geometry */ + int tw, th; /* tty width and height */ int w; /* window width */ int h; /* window height */ int ch; /* char height */ int cw; /* char width */ char state; /* focus, redraw, visible */ - struct timeval lastdraw; } XWindow; typedef struct { @@ -212,7 +218,6 @@ typedef struct { char s[ESC_BUF_SIZ]; } Key; - /* TODO: use better name for vars... */ typedef struct { int mode; @@ -228,17 +233,22 @@ typedef struct { #include "config.h" +/* Font structure */ +typedef struct { + int ascent; + int descent; + short lbearing; + short rbearing; + XFontSet set; + XftFont* xft_set; +} Font; + /* Drawing Context */ typedef struct { ulong col[LEN(colorname) < 256 ? 256 : LEN(colorname)]; + XftColor xft_col[LEN(colorname) < 256 ? 256 : LEN(colorname)]; GC gc; - struct { - int ascent; - int descent; - short lbearing; - short rbearing; - XFontSet set; - } font, bfont; + Font font, bfont, ifont, ibfont; } DC; static void die(const char*, ...); @@ -248,7 +258,6 @@ static void drawregion(int, int, int, int); static void execsh(void); static void sigchld(int); static void run(void); -static bool last_draw_too_old(void); static void csidump(void); static void csihandle(void); @@ -269,7 +278,7 @@ static void tmoveto(int, int); static void tnew(int, int); static void tnewline(int); static void tputtab(bool); -static void tputc(char*); +static void tputc(char*, int); static void treset(void); static int tresize(int, int); static void tscrollup(int, int); @@ -284,16 +293,16 @@ static void tfulldirt(void); static void ttynew(void); static void ttyread(void); -static void ttyresize(int, int); +static void ttyresize(void); static void ttywrite(const char *, size_t); static void xdraws(char *, Glyph, int, int, int, int); static void xhints(void); static void xclear(int, int, int, int); -static void xcopy(); static void xdrawcursor(void); static void xinit(void); static void xloadcols(void); +static void xresettitle(void); static void xseturgency(int); static void xsetsel(char*); static void xresize(int, int); @@ -310,12 +319,13 @@ static void brelease(XEvent *); static void bpress(XEvent *); static void bmotion(XEvent *); static void selnotify(XEvent *); +static void selclear(XEvent *); static void selrequest(XEvent *); static void selinit(void); static inline bool selected(int, int); static void selcopy(void); -static void selpaste(); +static void selpaste(void); static void selscroll(int, int); static int utf8decode(char *, long *); @@ -323,6 +333,10 @@ static int utf8encode(long *, char *); static int utf8size(char *); static int isfullutf8(char *, int); +static void *xmalloc(size_t); +static void *xrealloc(void *, size_t); +static void *xcalloc(size_t nmemb, size_t size); + static void (*handler[LASTEvent])(XEvent *) = { [KeyPress] = kpress, [ClientMessage] = cmessage, @@ -335,6 +349,7 @@ static void (*handler[LASTEvent])(XEvent *) = { [MotionNotify] = bmotion, [ButtonPress] = bpress, [ButtonRelease] = brelease, + [SelectionClear] = selclear, [SelectionNotify] = selnotify, [SelectionRequest] = selrequest, }; @@ -348,13 +363,36 @@ static STREscape strescseq; static int cmdfd; static pid_t pid; static Selection sel; -static FILE *fileio; +static int iofd = -1; static char **opt_cmd = NULL; static char *opt_io = NULL; static char *opt_title = NULL; static char *opt_embed = NULL; static char *opt_class = NULL; +void * +xmalloc(size_t len) { + void *p = malloc(len); + if(!p) + die("Out of memory\n"); + return p; +} + +void * +xrealloc(void *p, size_t len) { + if((p = realloc(p, len)) == NULL) + die("Out of memory\n"); + return p; +} + +void * +xcalloc(size_t nmemb, size_t size) { + void *p = calloc(nmemb, size); + if(!p) + die("Out of memory\n"); + return p; +} + int utf8decode(char *s, long *u) { uchar c; @@ -543,8 +581,11 @@ bpress(XEvent *e) { if(IS_SET(MODE_MOUSE)) mousereport(e); else if(e->xbutton.button == Button1) { - if(sel.bx != -1) + if(sel.bx != -1) { + sel.bx = -1; tsetdirt(sel.b.y, sel.e.y); + draw(); + } sel.mode = 1; sel.ex = sel.bx = X2COL(e->xbutton.x); sel.ey = sel.by = Y2ROW(e->xbutton.y); @@ -561,19 +602,22 @@ selcopy(void) { else { bufsize = (term.col+1) * (sel.e.y-sel.b.y+1) * UTF_SIZ; - ptr = str = malloc(bufsize); + ptr = str = xmalloc(bufsize); /* append every set & selected glyph to the selection */ for(y = 0; y < term.row; y++) { for(x = 0; x < term.col; x++) { - is_selected = selected(x, y); - if((term.line[y][x].state & GLYPH_SET) && is_selected) { - int size = utf8size(term.line[y][x].c); - memcpy(ptr, term.line[y][x].c, size); - ptr += size; - } + int size; + char *p; + Glyph *gp = &term.line[y][x]; + + if(!(is_selected = selected(x, y))) + continue; + p = (gp->state & GLYPH_SET) ? gp->c : " "; + size = utf8size(p); + memcpy(ptr, p, size); + ptr += size; } - /* \n at the end of every selected line except for the last one */ if(is_selected && y < sel.e.y) *ptr++ = '\n'; @@ -611,6 +655,13 @@ selpaste() { XConvertSelection(xw.dpy, XA_PRIMARY, sel.xtarget, XA_PRIMARY, xw.win, CurrentTime); } +void selclear(XEvent *e) { + if(sel.bx == -1) + return; + sel.bx = -1; + tsetdirt(sel.b.y, sel.e.y); +} + void selrequest(XEvent *e) { XSelectionRequestEvent *xsre; @@ -658,8 +709,6 @@ xsetsel(char *str) { clipboard = XInternAtom(xw.dpy, "CLIPBOARD", 0); XSetSelectionOwner(xw.dpy, clipboard, xw.win, CurrentTime); - - XFlush(xw.dpy); } void @@ -702,7 +751,6 @@ brelease(XEvent *e) { } memcpy(&sel.tclick2, &sel.tclick1, sizeof(struct timeval)); gettimeofday(&sel.tclick1, NULL); - draw(); } void @@ -719,7 +767,6 @@ bmotion(XEvent *e) { int starty = MIN(oldey, sel.ey); int endy = MAX(oldey, sel.ey); tsetdirt(starty, endy); - draw(); } } } @@ -739,6 +786,17 @@ execsh(void) { char **args; char *envshell = getenv("SHELL"); + unsetenv("COLUMNS"); + unsetenv("LINES"); + unsetenv("TERMCAP"); + + signal(SIGCHLD, SIG_DFL); + signal(SIGHUP, SIG_DFL); + signal(SIGINT, SIG_DFL); + signal(SIGQUIT, SIG_DFL); + signal(SIGTERM, SIG_DFL); + signal(SIGALRM, SIG_DFL); + DEFAULT(envshell, SHELL); putenv("TERM="TNAME); args = opt_cmd ? opt_cmd : (char*[]){envshell, "-i", NULL}; @@ -785,9 +843,15 @@ ttynew(void) { close(s); cmdfd = m; signal(SIGCHLD, sigchld); - if(opt_io && !(fileio = fopen(opt_io, "w"))) { - fprintf(stderr, "Error opening %s:%s\n", - opt_io, strerror(errno)); + if(opt_io) { + if(!strcmp(opt_io, "-")) { + iofd = STDOUT_FILENO; + } else { + if((iofd = open(opt_io, O_WRONLY | O_CREAT, 0666)) < 0) { + fprintf(stderr, "Error opening %s:%s\n", + opt_io, strerror(errno)); + } + } } } } @@ -820,7 +884,7 @@ ttyread(void) { while(buflen >= UTF_SIZ || isfullutf8(ptr,buflen)) { charsize = utf8decode(ptr, &utf8c); utf8encode(&utf8c, s); - tputc(s); + tputc(s, charsize); ptr += charsize; buflen -= charsize; } @@ -836,14 +900,13 @@ ttywrite(const char *s, size_t n) { } void -ttyresize(int x, int y) { +ttyresize(void) { struct winsize w; w.ws_row = term.row; w.ws_col = term.col; - w.ws_xpixel = xw.w; - w.ws_ypixel = xw.h; - w.ws_xpixel = w.ws_ypixel = 0; + w.ws_xpixel = xw.tw; + w.ws_ypixel = xw.th; if(ioctl(cmdfd, TIOCSWINSZ, &w) < 0) fprintf(stderr, "Couldn't set window size: %s\n", SERRNO); } @@ -878,7 +941,7 @@ tcursor(int mode) { void treset(void) { - unsigned i; + uint i; term.c = (TCursor){{ .mode = ATTR_NULL, .fg = DefaultFG, @@ -890,6 +953,7 @@ treset(void) { term.tabs[i] = 1; term.top = 0, term.bot = term.row - 1; term.mode = MODE_WRAP; + tclearregion(0, 0, term.col-1, term.row-1); } @@ -897,14 +961,14 @@ void tnew(int col, int row) { /* set screen size */ term.row = row, term.col = col; - term.line = malloc(term.row * sizeof(Line)); - term.alt = malloc(term.row * sizeof(Line)); - term.dirty = malloc(term.row * sizeof(*term.dirty)); - term.tabs = malloc(term.col * sizeof(*term.tabs)); + 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] = malloc(term.col * sizeof(Glyph)); - term.alt [row] = malloc(term.col * sizeof(Glyph)); + term.line[row] = xmalloc(term.col * sizeof(Glyph)); + term.alt [row] = xmalloc(term.col * sizeof(Glyph)); term.dirty[row] = 0; } memset(term.tabs, 0, term.col * sizeof(*term.tabs)); @@ -1112,31 +1176,39 @@ 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); + 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; case 1: term.c.attr.mode |= ATTR_BOLD; break; - case 3: /* enter standout (highlight) mode TODO: make it italic */ - term.c.attr.mode |= ATTR_REVERSE; + case 3: /* enter standout (highlight) */ + term.c.attr.mode |= ATTR_ITALIC; break; case 4: term.c.attr.mode |= ATTR_UNDERLINE; break; + case 5: + term.c.attr.mode |= ATTR_BLINK; + break; case 7: term.c.attr.mode |= ATTR_REVERSE; break; + case 21: case 22: term.c.attr.mode &= ~ATTR_BOLD; break; - case 23: /* leave standout (highlight) mode TODO: make it italic */ - term.c.attr.mode &= ~ATTR_REVERSE; + case 23: /* leave standout (highlight) mode */ + term.c.attr.mode &= ~ATTR_ITALIC; break; case 24: term.c.attr.mode &= ~ATTR_UNDERLINE; break; + case 25: + term.c.attr.mode &= ~ATTR_BLINK; + break; case 27: term.c.attr.mode &= ~ATTR_REVERSE; break; @@ -1176,7 +1248,7 @@ tsetattr(int *attr, int l) { else if(BETWEEN(attr[i], 90, 97)) term.c.attr.fg = attr[i] - 90 + 8; else if(BETWEEN(attr[i], 100, 107)) - term.c.attr.fg = attr[i] - 100 + 8; + term.c.attr.bg = attr[i] - 100 + 8; else fprintf(stderr, "erresc(default): gfx attr %d unknown\n", attr[i]), csidump(); break; @@ -1208,7 +1280,8 @@ tsetmode(bool priv, bool set, int *args, int narg) { for(lim = args + narg; args < lim; ++args) { if(priv) { switch(*args) { - case 1: + break; + case 1: /* DECCKM -- Cursor key */ MODBIT(term.mode, set, MODE_APPKEYPAD); break; case 5: /* DECSCNM -- Reverve video */ @@ -1217,12 +1290,14 @@ tsetmode(bool priv, bool set, int *args, int narg) { if(mode != term.mode) redraw(); break; - case 7: + case 6: /* XXX: DECOM -- Origin */ + break; + case 7: /* DECAWM -- Auto wrap */ MODBIT(term.mode, set, MODE_WRAP); break; - case 20: - MODBIT(term.mode, set, MODE_CRLF); + case 8: /* XXX: DECARM -- Auto repeat */ break; + case 0: /* Error (IGNORED) */ case 12: /* att610 -- Start blinking cursor (IGNORED) */ break; case 25: @@ -1250,6 +1325,12 @@ tsetmode(bool priv, bool set, int *args, int narg) { tcursor((set) ? CURSOR_SAVE : CURSOR_LOAD); break; default: + /* case 2: DECANM -- ANSI/VT52 (NOT SUPPOURTED) */ + /* case 3: DECCOLM -- Column (NOT SUPPORTED) */ + /* case 4: DECSCLM -- Scroll (NOT SUPPORTED) */ + /* case 18: DECPFF -- Printer feed (NOT SUPPORTED) */ + /* case 19: DECPEX -- Printer extent (NOT SUPPORTED) */ + /* case 42: DECNRCM -- National characters (NOT SUPPORTED) */ fprintf(stderr, "erresc: unknown private set/reset mode %d\n", *args); @@ -1257,9 +1338,19 @@ tsetmode(bool priv, bool set, int *args, int narg) { } } else { switch(*args) { - case 4: + case 0: /* Error (IGNORED) */ + break; + case 2: /* KAM -- keyboard action */ + MODBIT(term.mode, set, MODE_KBDLOCK); + break; + case 4: /* IRM -- Insertion-replacement */ MODBIT(term.mode, set, MODE_INSERT); break; + case 12: /* XXX: SRM -- Send/Receive */ + break; + case 20: /* LNM -- Linefeed/new line */ + MODBIT(term.mode, set, MODE_CRLF); + break; default: fprintf(stderr, "erresc: unknown set/reset mode %d\n", @@ -1488,6 +1579,9 @@ strhandle(void) { break; } break; + case 'k': /* old title set compatibility */ + XStoreName(xw.dpy, xw.win, strescseq.buf); + break; case 'P': /* DSC -- Device Control String */ case '_': /* APC -- Application Program Command */ case '^': /* PM -- Privacy Message */ @@ -1530,7 +1624,7 @@ strreset(void) { void tputtab(bool forward) { - unsigned x = term.c.x; + uint x = term.c.x; if(forward) { if(x == term.col) @@ -1547,11 +1641,11 @@ tputtab(bool forward) { } void -tputc(char *c) { +tputc(char *c, int len) { char ascii = *c; - if(fileio) - putc(ascii, fileio); + if(iofd != -1) + write(iofd, c, len); if(term.esc & ESC_START) { if(term.esc & ESC_CSI) { @@ -1601,6 +1695,7 @@ tputc(char *c) { 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; @@ -1633,6 +1728,8 @@ tputc(char *c) { case 'c': /* RIS -- Reset to inital state */ treset(); term.esc = 0; + xclear(0, 0, xw.w, xw.h); + xresettitle(); break; case '=': /* DECPAM -- Application keypad */ term.mode |= MODE_APPKEYPAD; @@ -1663,6 +1760,8 @@ tputc(char *c) { if(sel.bx != -1 && BETWEEN(term.c.y, sel.by, sel.ey)) sel.bx = -1; switch(ascii) { + case '\0': /* padding character, do nothing */ + break; case '\t': tputtab(1); break; @@ -1727,16 +1826,16 @@ 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)); - term.tabs = realloc(term.tabs, col * sizeof(*term.tabs)); + term.line = xrealloc(term.line, row * sizeof(Line)); + term.alt = xrealloc(term.alt, row * sizeof(Line)); + term.dirty = xrealloc(term.dirty, row * sizeof(*term.dirty)); + term.tabs = xrealloc(term.tabs, col * sizeof(*term.tabs)); /* 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)); + 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; @@ -1746,8 +1845,8 @@ 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)); + term.line[i] = xcalloc(col, sizeof(Glyph)); + term.alt [i] = xcalloc(col, sizeof(Glyph)); } if(col > term.col) { bool *bp = term.tabs + term.col; @@ -1770,49 +1869,51 @@ tresize(int col, int row) { void xresize(int col, int row) { - xw.w = MAX(1, 2*BORDER + col * xw.cw); - xw.h = MAX(1, 2*BORDER + row * xw.ch); + xw.tw = MAX(1, 2*BORDER + col * xw.cw); + xw.th = MAX(1, 2*BORDER + row * xw.ch); + + XftDrawChange(xw.xft_draw, xw.buf); } void xloadcols(void) { int i, r, g, b; - XColor color; + XRenderColor xft_color = { .alpha = 0 }; ulong white = WhitePixel(xw.dpy, xw.scr); /* load colors [0-15] colors and [256-LEN(colorname)[ (config.h) */ for(i = 0; i < LEN(colorname); i++) { if(!colorname[i]) continue; - if(!XAllocNamedColor(xw.dpy, xw.cmap, colorname[i], &color, &color)) { + if(!XftColorAllocName(xw.dpy, xw.vis, xw.cmap, colorname[i], &dc.xft_col[i])) { dc.col[i] = white; fprintf(stderr, "Could not allocate color '%s'\n", colorname[i]); } else - dc.col[i] = color.pixel; + dc.col[i] = dc.xft_col[i].pixel; } /* load colors [16-255] ; same colors as xterm */ for(i = 16, r = 0; r < 6; r++) for(g = 0; g < 6; g++) for(b = 0; b < 6; b++) { - color.red = r == 0 ? 0 : 0x3737 + 0x2828 * r; - color.green = g == 0 ? 0 : 0x3737 + 0x2828 * g; - color.blue = b == 0 ? 0 : 0x3737 + 0x2828 * b; - if(!XAllocColor(xw.dpy, xw.cmap, &color)) { + xft_color.red = r == 0 ? 0 : 0x3737 + 0x2828 * r; + xft_color.green = g == 0 ? 0 : 0x3737 + 0x2828 * g; + xft_color.blue = b == 0 ? 0 : 0x3737 + 0x2828 * b; + if(!XftColorAllocValue(xw.dpy, xw.vis, xw.cmap, &xft_color, &dc.xft_col[i])) { dc.col[i] = white; fprintf(stderr, "Could not allocate color %d\n", i); } else - dc.col[i] = color.pixel; + dc.col[i] = dc.xft_col[i].pixel; i++; } for(r = 0; r < 24; r++, i++) { - color.red = color.green = color.blue = 0x0808 + 0x0a0a * r; - if(!XAllocColor(xw.dpy, xw.cmap, &color)) { + xft_color.red = xft_color.green = xft_color.blue = 0x0808 + 0x0a0a * r; + if(!XftColorAllocValue(xw.dpy, xw.vis, xw.cmap, &xft_color, &dc.xft_col[i])) { dc.col[i] = white; fprintf(stderr, "Could not allocate color %d\n", i); } else - dc.col[i] = color.pixel; + dc.col[i] = dc.xft_col[i].pixel; } } @@ -1849,48 +1950,25 @@ xhints(void) { XFree(sizeh); } -XFontSet -xinitfont(char *fontstr) { - XFontSet set; - char *def, **missing; - int n; - - missing = NULL; - set = XCreateFontSet(xw.dpy, fontstr, &missing, &n, &def); - if(missing) { - while(n--) - fprintf(stderr, "st: missing fontset: %s\n", missing[n]); - XFreeStringList(missing); - } - return set; -} - void -xgetfontinfo(XFontSet set, int *ascent, int *descent, short *lbearing, short *rbearing) { - XFontStruct **xfonts; - char **font_names; - int i, n; +xinitfont(Font *f, char *fontstr) { + f->xft_set = XftFontOpenName(xw.dpy, xw.scr, fontstr); - *ascent = *descent = *lbearing = *rbearing = 0; - n = XFontsOfFontSet(set, &xfonts, &font_names); - for(i = 0; i < n; i++) { - *ascent = MAX(*ascent, (*xfonts)->ascent); - *descent = MAX(*descent, (*xfonts)->descent); - *lbearing = MAX(*lbearing, (*xfonts)->min_bounds.lbearing); - *rbearing = MAX(*rbearing, (*xfonts)->max_bounds.rbearing); - xfonts++; - } + if(!f->xft_set) + die("st: can't open font %s.\n", fontstr); + + f->ascent = f->xft_set->ascent; + f->descent = f->xft_set->descent; + f->lbearing = 0; + f->rbearing = f->xft_set->max_advance_width; } void -initfonts(char *fontstr, char *bfontstr) { - if((dc.font.set = xinitfont(fontstr)) == NULL || - (dc.bfont.set = xinitfont(bfontstr)) == NULL) - die("Can't load font %s\n", dc.font.set ? BOLDFONT : FONT); - xgetfontinfo(dc.font.set, &dc.font.ascent, &dc.font.descent, - &dc.font.lbearing, &dc.font.rbearing); - xgetfontinfo(dc.bfont.set, &dc.bfont.ascent, &dc.bfont.descent, - &dc.bfont.lbearing, &dc.bfont.rbearing); +initfonts(char *fontstr, char *bfontstr, char *ifontstr, char *ibfontstr) { + xinitfont(&dc.font, fontstr); + xinitfont(&dc.bfont, bfontstr); + xinitfont(&dc.ifont, ifontstr); + xinitfont(&dc.ibfont, ibfontstr); } void @@ -1898,11 +1976,23 @@ xinit(void) { XSetWindowAttributes attrs; Cursor cursor; Window parent; - int sw, sh; + int sw, sh, major, minor; if(!(xw.dpy = XOpenDisplay(NULL))) die("Can't open display\n"); xw.scr = XDefaultScreen(xw.dpy); + xw.vis = XDefaultVisual(xw.dpy, xw.scr); + + /* font */ + initfonts(FONT, BOLDFONT, ITALICFONT, ITALICBOLDFONT); + + /* XXX: Assuming same size for bold font */ + xw.cw = dc.font.rbearing - dc.font.lbearing; + xw.ch = dc.font.ascent + dc.font.descent; + + /* colors */ + xw.cmap = XDefaultColormap(xw.dpy, xw.scr); + xloadcols(); /* adjust fixed window geometry */ if(xw.isfixed) { @@ -1923,35 +2013,29 @@ xinit(void) { xw.fy = 0; } - /* font */ - initfonts(FONT, BOLDFONT); - - /* XXX: Assuming same size for bold font */ - xw.cw = dc.font.rbearing - dc.font.lbearing; - xw.ch = dc.font.ascent + dc.font.descent; - - /* colors */ - xw.cmap = XDefaultColormap(xw.dpy, xw.scr); - xloadcols(); - attrs.background_pixel = dc.col[DefaultBG]; attrs.border_pixel = dc.col[DefaultBG]; attrs.bit_gravity = NorthWestGravity; attrs.event_mask = FocusChangeMask | KeyPressMask | ExposureMask | VisibilityChangeMask | StructureNotifyMask - | ButtonMotionMask | ButtonPressMask | ButtonReleaseMask - | EnterWindowMask | LeaveWindowMask; + | ButtonMotionMask | ButtonPressMask | ButtonReleaseMask; attrs.colormap = xw.cmap; parent = opt_embed ? strtol(opt_embed, NULL, 0) : XRootWindow(xw.dpy, xw.scr); xw.win = XCreateWindow(xw.dpy, parent, xw.fx, xw.fy, xw.w, xw.h, 0, XDefaultDepth(xw.dpy, xw.scr), InputOutput, - XDefaultVisual(xw.dpy, xw.scr), + xw.vis, CWBackPixel | CWBorderPixel | CWBitGravity | CWEventMask | CWColormap, &attrs); + + /* double buffering */ + if(!XdbeQueryExtension(xw.dpy, &major, &minor)) + die("Xdbe extension is not present\n"); xw.buf = XdbeAllocateBackBufferName(xw.dpy, xw.win, XdbeCopied); + /* Xft rendering context */ + xw.xft_draw = XftDrawCreate(xw.dpy, xw.buf, xw.vis, xw.cmap); /* input methods */ xw.xim = XOpenIM(xw.dpy, NULL, NULL, NULL); @@ -1970,7 +2054,7 @@ xinit(void) { xw.xembed = XInternAtom(xw.dpy, "_XEMBED", False); - XStoreName(xw.dpy, xw.win, opt_title ? opt_title : "st"); + xresettitle(); XMapWindow(xw.dpy, xw.win); xhints(); XSync(xw.dpy, 0); @@ -1980,7 +2064,8 @@ void xdraws(char *s, Glyph base, int x, int y, int charlen, int bytelen) { int fg = base.fg, bg = base.bg, temp; int winx = BORDER+x*xw.cw, winy = BORDER+y*xw.ch + dc.font.ascent, width = charlen*xw.cw; - XFontSet fontset = dc.font.set; + Font *font = &dc.font; + XGlyphInfo extents; int i; /* only switch default fg/bg if term is in RV mode */ @@ -1996,9 +2081,14 @@ xdraws(char *s, Glyph base, int x, int y, int charlen, int bytelen) { if(base.mode & ATTR_BOLD) { fg += 8; - fontset = dc.bfont.set; + font = &dc.bfont; } + if(base.mode & ATTR_ITALIC) + font = &dc.ifont; + if(base.mode & (ATTR_ITALIC|ATTR_ITALIC)) + font = &dc.ibfont; + XSetBackground(xw.dpy, dc.gc, dc.col[bg]); XSetForeground(xw.dpy, dc.gc, dc.col[fg]); @@ -2012,20 +2102,15 @@ xdraws(char *s, Glyph base, int x, int y, int charlen, int bytelen) { } } - XmbDrawImageString(xw.dpy, xw.buf, fontset, dc.gc, winx, winy, s, bytelen); + XftTextExtentsUtf8(xw.dpy, font->xft_set, (FcChar8 *)s, bytelen, &extents); + width = extents.xOff; + XftDrawRect(xw.xft_draw, &dc.xft_col[bg], winx, winy - font->ascent, width, xw.ch); + XftDrawStringUtf8(xw.xft_draw, &dc.xft_col[fg], font->xft_set, winx, winy, (FcChar8 *)s, bytelen); if(base.mode & ATTR_UNDERLINE) XDrawLine(xw.dpy, xw.buf, dc.gc, winx, winy+1, winx+width-1, winy+1); } -/* copy buffer pixmap to screen pixmap */ -void -xcopy() { - XdbeSwapInfo swpinfo[1] = {{xw.win, XdbeCopied}}; - XdbeSwapBuffers(xw.dpy, swpinfo, 1); - -} - void xdrawcursor(void) { static int oldx = 0; @@ -2046,8 +2131,6 @@ xdrawcursor(void) { } else xclear(oldx, oldy, oldx, oldy); - xcopy(oldx, oldy, 1, 1); - /* draw the new one */ if(!(term.c.state & CURSOR_HIDE)) { if(!(xw.state & WIN_FOCUSED)) @@ -2060,23 +2143,30 @@ xdrawcursor(void) { xdraws(g.c, g, term.c.x, term.c.y, 1, sl); oldx = term.c.x, oldy = term.c.y; } +} - xcopy(term.c.x, term.c.y, 1, 1); +void +xresettitle(void) { + XStoreName(xw.dpy, xw.win, opt_title ? opt_title : "st"); } void redraw(void) { struct timespec tv = {0, REDRAW_TIMEOUT * 1000}; + + xclear(0, 0, xw.w, xw.h); tfulldirt(); draw(); + XSync(xw.dpy, False); /* necessary for a good tput flash */ nanosleep(&tv, NULL); } void draw() { + XdbeSwapInfo swpinfo[1] = {{xw.win, XdbeCopied}}; + drawregion(0, 0, term.col, term.row); - xcopy(); - gettimeofday(&xw.lastdraw, NULL); + XdbeSwapBuffers(xw.dpy, swpinfo, 1); } void @@ -2131,7 +2221,6 @@ expose(XEvent *ev) { if(!e->count) xw.state &= ~WIN_REDRAW; } - xcopy(); } void @@ -2164,7 +2253,6 @@ focus(XEvent *ev) { xseturgency(0); } else xw.state &= ~WIN_FOCUSED; - draw(); } char* @@ -2190,6 +2278,8 @@ kpress(XEvent *ev) { int shift; Status status; + if (IS_SET(MODE_KBDLOCK)) + return; meta = e->state & Mod1Mask; shift = e->state & ShiftMask; len = XmbLookupString(xw.xic, e, buf, sizeof(buf), &ksym, &status); @@ -2240,7 +2330,6 @@ cmessage(XEvent *e) { } else if(e->xclient.data.l[1] == XEMBED_FOCUS_OUT) { xw.state &= ~WIN_FOCUSED; } - draw(); } } @@ -2257,47 +2346,48 @@ resize(XEvent *e) { row = (xw.h - 2*BORDER) / xw.ch; if(col == term.col && row == term.row) return; - if(tresize(col, row)) - draw(); - xresize(col, row); - ttyresize(col, row); -} -bool -last_draw_too_old(void) { - struct timeval now; - gettimeofday(&now, NULL); - return TIMEDIFF(now, xw.lastdraw) >= DRAW_TIMEOUT/1000; + xclear(0, 0, xw.w, xw.h); + tresize(col, row); + xresize(col, row); + ttyresize(); } void run(void) { XEvent ev; fd_set rfd; - int xfd = XConnectionNumber(xw.dpy); - struct timeval timeout = {0}; - bool stuff_to_print = 0; + int xfd = XConnectionNumber(xw.dpy), i; + struct timeval drawtimeout, *tv = NULL; - for(;;) { + for(i = 0;; i++) { FD_ZERO(&rfd); FD_SET(cmdfd, &rfd); FD_SET(xfd, &rfd); - timeout.tv_sec = 0; - timeout.tv_usec = SELECT_TIMEOUT; - if(select(MAX(xfd, cmdfd)+1, &rfd, NULL, NULL, &timeout) < 0) { + if(select(MAX(xfd, cmdfd)+1, &rfd, NULL, NULL, tv) < 0) { if(errno == EINTR) continue; die("select failed: %s\n", SERRNO); } - if(FD_ISSET(cmdfd, &rfd)) { + + /* + * Stop after a certain number of reads so the user does not + * feel like the system is stuttering. + */ + if(i < 1000 && FD_ISSET(cmdfd, &rfd)) { ttyread(); - stuff_to_print = 1; - } - if(stuff_to_print && last_draw_too_old()) { - stuff_to_print = 0; - draw(); + /* + * Just wait a bit so it isn't disturbing the + * user and the system is able to write something. + */ + drawtimeout.tv_sec = 0; + drawtimeout.tv_usec = 5; + tv = &drawtimeout; + continue; } + i = 0; + tv = NULL; while(XPending(xw.dpy)) { XNextEvent(xw.dpy, &ev); @@ -2306,13 +2396,16 @@ run(void) { if(handler[ev.type]) (handler[ev.type])(&ev); } + + draw(); + XFlush(xw.dpy); } } int main(int argc, char *argv[]) { int i, bitm, xr, yr; - unsigned int wr, hr; + uint wr, hr; xw.fw = xw.fh = xw.fx = xw.fy = 0; xw.isfixed = False;