diff -r 94c886b859a1 config.def.h --- a/config.def.h Sun Oct 31 20:29:22 2010 +0100 +++ b/config.def.h Sun Nov 07 16:55:36 2010 +0100 @@ -54,12 +54,12 @@ }; /* Line drawing characters (sometime specific to each font...) */ -static char gfx[] = { +static wchar_t gfx[] = { ['`'] = 0x01, ['a'] = 0x02, - ['f'] = 'o', - ['g'] = '+', - ['i'] = '#', + ['f'] = L'o', + ['g'] = L'+', + ['i'] = L'#', ['j'] = 0x0B, ['k'] = 0x0C, ['l'] = 0x0D, diff -r 94c886b859a1 st.c --- a/st.c Sun Oct 31 20:29:22 2010 +0100 +++ b/st.c Sun Nov 07 16:55:36 2010 +0100 @@ -9,6 +9,8 @@ #include #include #include +#include +#include #include #include #include @@ -62,7 +64,7 @@ enum { WIN_VISIBLE=1, WIN_REDRAW=2, WIN_FOCUSED=4 }; typedef struct { - char c; /* character code */ + wchar_t c; /* character code */ char mode; /* attribute flags */ int fg; /* foreground */ int bg; /* background */ @@ -81,12 +83,12 @@ /* CSI Escape sequence structs */ /* ESC '[' [[ [] [;]] ] */ typedef struct { - char buf[ESC_BUF_SIZ]; /* raw string */ + wchar_t 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; + wchar_t mode; } CSIEscape; /* Internal representation of the screen */ @@ -100,7 +102,7 @@ int bot; /* bottom scroll limit */ int mode; /* terminal mode flags */ int esc; /* escape state flags */ - char title[ESC_TITLE_SIZ]; + wchar_t title[ESC_TITLE_SIZ]; int titlelen; } Term; @@ -127,11 +129,19 @@ char s[ESC_BUF_SIZ]; } Key; +typedef struct { + XFontSet fs; + short lbearing; + short rbearing; + int ascent; + int descent; +} FontInfo; + /* Drawing Context */ typedef struct { unsigned long col[256]; - XFontStruct* font; - XFontStruct* bfont; + FontInfo font; + FontInfo bfont; GC gc; } DC; @@ -141,7 +151,7 @@ int bx, by; int ex, ey; struct {int x, y;} b, e; - char *clip; + wchar_t *clip; } Selection; #include "config.h" @@ -167,14 +177,14 @@ static void tnew(int, int); static void tnewline(int); static void tputtab(void); -static void tputc(char); -static void tputs(char*, int); +static void tputc(wchar_t); +static void tputs(wchar_t*, int); static void treset(void); static int tresize(int, int); static void tscrollup(int, int); static void tscrolldown(int, int); static void tsetattr(int*, int); -static void tsetchar(char); +static void tsetchar(wchar_t); static void tsetscroll(int, int); static void tswapscreen(void); @@ -183,7 +193,7 @@ static void ttyresize(int, int); static void ttywrite(const char *, size_t); -static void xdraws(char *, Glyph, int, int, int); +static void xdraws(wchar_t *, Glyph, int, int, int); static void xhints(void); static void xclear(int, int, int, int); static void xdrawcursor(void); @@ -266,19 +276,19 @@ sel.ey = sel.by = e->xbutton.y/xw.ch; } -static char *getseltext() { - char *str, *ptr; +static wchar_t *getseltext() { + wchar_t *str, *ptr; int ls, x, y, sz; if(sel.bx == -1) return NULL; sz = (term.col+1) * (sel.e.y-sel.b.y+1); - ptr = str = malloc(sz); + ptr = str = malloc(sz*sizeof(wchar_t)); for(y = 0; y < term.row; y++) { for(x = 0; x < term.col; x++) if(term.line[y][x].state & GLYPH_SET && (ls = selected(x, y))) *ptr = term.line[y][x].c, ptr++; if(ls) - *ptr = '\n', ptr++; + *ptr = L'\n', ptr++; } *ptr = 0; return str; @@ -357,7 +367,7 @@ } else if(xsre->target == XA_STRING) { res = XChangeProperty(xsre->display, xsre->requestor, xsre->property, xsre->target, 8, PropModeReplace, (unsigned char *) sel.clip, - strlen(sel.clip)); + wcslen(sel.clip)); switch(res) { case BadAlloc: case BadAtom: @@ -381,7 +391,7 @@ } } -static void selcopy(char *str) { +static void selcopy(wchar_t *str) { /* register the selection for both the clipboard and the primary */ Atom clipboard; int res; @@ -527,11 +537,19 @@ ttyread(void) { char buf[BUFSIZ]; int ret; - - if((ret = read(cmdfd, buf, LEN(buf))) < 0) + size_t nelem; + + if((ret = read(cmdfd, buf, sizeof(buf)-1)) < 0) die("Couldn't read from shell: %s\n", SERRNO); - else - tputs(buf, ret); + buf[ret] = 0; + if((nelem = mbstowcs(NULL, buf, 0)) == -1) + puts("bad multibyte"); + else { + wchar_t *wbuf = malloc(nelem*sizeof(wchar_t)); + (void) mbstowcs(wbuf, buf, nelem); + tputs(wbuf, nelem); + free(wbuf); + } } void @@ -640,18 +658,18 @@ void csiparse(void) { /* int noarg = 1; */ - char *p = escseq.buf; + wchar_t *p = escseq.buf; escseq.narg = 0; - if(*p == '?') + if(*p == L'?') escseq.priv = 1, p++; - + while(p < escseq.buf+escseq.len) { - while(isdigit(*p)) { + while(iswdigit(*p)) { escseq.arg[escseq.narg] *= 10; - escseq.arg[escseq.narg] += *p++ - '0'/*, noarg = 0 */; + escseq.arg[escseq.narg] += *p++ - L'0'/*, noarg = 0 */; } - if(*p == ';' && escseq.narg+1 < ESC_ARG_SIZ) + if(*p == L';' && escseq.narg+1 < ESC_ARG_SIZ) escseq.narg++, p++; else { escseq.mode = *p; @@ -671,7 +689,7 @@ } void -tsetchar(char c) { +tsetchar(wchar_t c) { term.line[term.c.y][term.c.x] = term.c.attr; term.line[term.c.y][term.c.x].c = c; term.line[term.c.y][term.c.x].state |= GLYPH_SET; @@ -838,49 +856,49 @@ csidump(); /* die(""); */ break; - case '@': /* ICH -- Insert blank char */ + case L'@': /* ICH -- Insert blank char */ DEFAULT(escseq.arg[0], 1); tinsertblank(escseq.arg[0]); break; - case 'A': /* CUU -- Cursor Up */ - case 'e': + case L'A': /* CUU -- Cursor Up */ + case L'e': DEFAULT(escseq.arg[0], 1); tmoveto(term.c.x, term.c.y-escseq.arg[0]); break; - case 'B': /* CUD -- Cursor Down */ + case L'B': /* CUD -- Cursor Down */ DEFAULT(escseq.arg[0], 1); tmoveto(term.c.x, term.c.y+escseq.arg[0]); break; - case 'C': /* CUF -- Cursor Forward */ - case 'a': + case L'C': /* CUF -- Cursor Forward */ + case L'a': DEFAULT(escseq.arg[0], 1); tmoveto(term.c.x+escseq.arg[0], term.c.y); break; - case 'D': /* CUB -- Cursor Backward */ + case L'D': /* CUB -- Cursor Backward */ DEFAULT(escseq.arg[0], 1); tmoveto(term.c.x-escseq.arg[0], term.c.y); break; - case 'E': /* CNL -- Cursor Down and first col */ + case L'E': /* CNL -- Cursor Down and first col */ DEFAULT(escseq.arg[0], 1); tmoveto(0, term.c.y+escseq.arg[0]); break; - case 'F': /* CPL -- Cursor Up and first col */ + case L'F': /* CPL -- Cursor Up and first col */ DEFAULT(escseq.arg[0], 1); tmoveto(0, term.c.y-escseq.arg[0]); break; - case 'G': /* CHA -- Move to */ - case '`': /* XXX: HPA -- same? */ + case L'G': /* CHA -- Move to */ + case L'`': /* XXX: HPA -- same? */ DEFAULT(escseq.arg[0], 1); tmoveto(escseq.arg[0]-1, term.c.y); break; - case 'H': /* CUP -- Move to */ - case 'f': /* XXX: HVP -- same? */ + case L'H': /* CUP -- Move to */ + case L'f': /* XXX: HVP -- same? */ DEFAULT(escseq.arg[0], 1); DEFAULT(escseq.arg[1], 1); tmoveto(escseq.arg[1]-1, escseq.arg[0]-1); break; /* XXX: (CSI n I) CHT -- Cursor Forward Tabulation tab stops */ - case 'J': /* ED -- Clear screen */ + case L'J': /* ED -- Clear screen */ switch(escseq.arg[0]) { case 0: /* below */ tclearregion(term.c.x, term.c.y, term.col-1, term.c.y); @@ -899,7 +917,7 @@ goto unknown; } break; - case 'K': /* EL -- Clear line */ + case L'K': /* EL -- Clear line */ switch(escseq.arg[0]) { case 0: /* right */ tclearregion(term.c.x, term.c.y, term.col-1, term.c.y); @@ -912,19 +930,19 @@ break; } break; - case 'S': /* SU -- Scroll line up */ + case L'S': /* SU -- Scroll line up */ DEFAULT(escseq.arg[0], 1); tscrollup(term.top, escseq.arg[0]); break; - case 'T': /* SD -- Scroll line down */ + case L'T': /* SD -- Scroll line down */ DEFAULT(escseq.arg[0], 1); tscrolldown(term.top, escseq.arg[0]); break; - case 'L': /* IL -- Insert blank lines */ + case L'L': /* IL -- Insert blank lines */ DEFAULT(escseq.arg[0], 1); tinsertblankline(escseq.arg[0]); break; - case 'l': /* RM -- Reset Mode */ + case L'l': /* RM -- Reset Mode */ if(escseq.priv) { switch(escseq.arg[0]) { case 1: @@ -967,24 +985,24 @@ } } break; - case 'M': /* DL -- Delete lines */ + case L'M': /* DL -- Delete lines */ DEFAULT(escseq.arg[0], 1); tdeleteline(escseq.arg[0]); break; - case 'X': /* ECH -- Erase char */ + case L'X': /* ECH -- Erase char */ DEFAULT(escseq.arg[0], 1); tclearregion(term.c.x, term.c.y, term.c.x + escseq.arg[0], term.c.y); break; - case 'P': /* DCH -- Delete char */ + case L'P': /* DCH -- Delete char */ DEFAULT(escseq.arg[0], 1); tdeletechar(escseq.arg[0]); break; /* XXX: (CSI n Z) CBT -- Cursor Backward Tabulation tab stops */ - case 'd': /* VPA -- Move to */ + case L'd': /* VPA -- Move to */ DEFAULT(escseq.arg[0], 1); tmoveto(term.c.x, escseq.arg[0]-1); break; - case 'h': /* SM -- Set terminal mode */ + case L'h': /* SM -- Set terminal mode */ if(escseq.priv) { switch(escseq.arg[0]) { case 1: @@ -1028,10 +1046,10 @@ } }; break; - case 'm': /* SGR -- Terminal attribute (color) */ + case L'm': /* SGR -- Terminal attribute (color) */ tsetattr(escseq.arg, escseq.narg); break; - case 'r': /* DECSTBM -- Set Scrolling Region */ + case L'r': /* DECSTBM -- Set Scrolling Region */ if(escseq.priv) goto unknown; else { @@ -1041,10 +1059,10 @@ tmoveto(0, 0); } break; - case 's': /* DECSC -- Save cursor position (ANSI.SYS) */ + case L's': /* DECSC -- Save cursor position (ANSI.SYS) */ tcursor(CURSOR_SAVE); break; - case 'u': /* DECRC -- Restore cursor position (ANSI.SYS) */ + case L'u': /* DECRC -- Restore cursor position (ANSI.SYS) */ tcursor(CURSOR_LOAD); break; } @@ -1074,7 +1092,7 @@ } void -tputc(char c) { +tputc(wchar_t c) { if(term.esc & ESC_START) { if(term.esc & ESC_CSI) { escseq.buf[escseq.len++] = c; @@ -1084,106 +1102,116 @@ } /* TODO: handle other OSC */ } else if(term.esc & ESC_OSC) { - if(c == ';') { + if(c == L';') { term.titlelen = 0; term.esc = ESC_START | ESC_TITLE; } } else if(term.esc & ESC_TITLE) { - if(c == '\a' || term.titlelen+1 >= ESC_TITLE_SIZ) { + if(c == L'\a' || term.titlelen+1 >= ESC_TITLE_SIZ) { + size_t nbytes; + term.esc = 0; - term.title[term.titlelen] = '\0'; - XStoreName(xw.dis, xw.win, term.title); + term.title[term.titlelen] = L'\0'; + if((nbytes = wcstombs(NULL, term.title, 0)) == (size_t) -1) + puts("bad title"); + else { + char *buf = malloc(nbytes+1); + wcstombs(buf, term.title, nbytes); + buf[nbytes] = '0'; + XStoreName(xw.dis, xw.win, buf); + free(buf); + } } else { term.title[term.titlelen++] = c; } } else if(term.esc & ESC_ALTCHARSET) { switch(c) { - case '0': /* Line drawing crap */ + case L'0': /* Line drawing crap */ term.c.attr.mode |= ATTR_GFX; break; - case 'B': /* Back to regular text */ + case L'B': /* Back to regular text */ term.c.attr.mode &= ~ATTR_GFX; break; default: - printf("esc unhandled charset: ESC ( %c\n", c); + printf("esc unhandled charset: ESC ( %lc\n", c); } term.esc = 0; } else { switch(c) { - case '[': + case L'[': term.esc |= ESC_CSI; break; - case ']': + case L']': term.esc |= ESC_OSC; break; - case '(': + case L'(': term.esc |= ESC_ALTCHARSET; break; - case 'D': /* IND -- Linefeed */ + case L'D': /* IND -- Linefeed */ if(term.c.y == term.bot) tscrollup(term.top, 1); else tmoveto(term.c.x, term.c.y+1); term.esc = 0; break; - case 'E': /* NEL -- Next line */ + case L'E': /* NEL -- Next line */ tnewline(1); /* always go to first col */ term.esc = 0; break; - case 'M': /* RI -- Reverse index */ + case L'M': /* RI -- Reverse index */ if(term.c.y == term.top) tscrolldown(term.top, 1); else tmoveto(term.c.x, term.c.y-1); term.esc = 0; break; - case 'c': /* RIS -- Reset to inital state */ + case L'c': /* RIS -- Reset to inital state */ treset(); term.esc = 0; break; - case '=': /* DECPAM -- Application keypad */ + case L'=': /* DECPAM -- Application keypad */ term.mode |= MODE_APPKEYPAD; term.esc = 0; break; - case '>': /* DECPNM -- Normal keypad */ + case L'>': /* DECPNM -- Normal keypad */ term.mode &= ~MODE_APPKEYPAD; term.esc = 0; break; - case '7': /* DECSC -- Save Cursor */ + case L'7': /* DECSC -- Save Cursor */ tcursor(CURSOR_SAVE); term.esc = 0; break; - case '8': /* DECRC -- Restore Cursor */ + case L'8': /* DECRC -- Restore Cursor */ tcursor(CURSOR_LOAD); term.esc = 0; break; default: - fprintf(stderr, "erresc: unknown sequence ESC 0x%02X '%c'\n", c, isprint(c)?c:'.'); + fprintf(stderr, "erresc: unknown sequence ESC 0x%02X '%lc'\n", c, iswprint(c)?c:'.'); term.esc = 0; } } } else { switch(c) { - case '\t': + case L'\t': tputtab(); break; - case '\b': + case L'\b': tmoveto(term.c.x-1, term.c.y); break; - case '\r': + case L'\r': tmoveto(0, term.c.y); break; - case '\f': - case '\v': - case '\n': + case L'\f': + case L'\v': + case L'\n': /* go to first col if the mode is set */ tnewline(IS_SET(MODE_CRLF)); break; - case '\a': + case L'\a': if(!(xw.state & WIN_FOCUSED)) xseturgency(1); break; - case '\033': + case L'\033': csireset(); term.esc = ESC_START; break; @@ -1201,7 +1229,7 @@ } void -tputs(char *s, int len) { +tputs(wchar_t *s, int len) { for(; len > 0; len--) tputc(*s++); } @@ -1358,20 +1386,51 @@ } void +xsetfontinfo(FontInfo *fi) +{ + XFontStruct **xfonts; + int fnum; + int i; + char **fontnames; + + fi->lbearing = 0; + fi->rbearing = 0; + fi->ascent = 0; + fi->descent = 0; + fnum = XFontsOfFontSet(fi->fs, &xfonts, &fontnames); + for(i=0; iascent < (*xfonts)->ascent) + fi->ascent = (*xfonts)->ascent; + if(fi->descent < (*xfonts)->descent) + fi->descent = (*xfonts)->descent; + if(fi->rbearing < (*xfonts)->max_bounds.rbearing) + fi->rbearing = (*xfonts)->max_bounds.rbearing; + if(fi->lbearing < (*xfonts)->min_bounds.lbearing) + fi->lbearing = (*xfonts)->min_bounds.lbearing; + } +} + +void xinit(void) { XSetWindowAttributes attrs; + char **mc; + char *ds; + int nmc; if(!(xw.dis = XOpenDisplay(NULL))) die("Can't open display\n"); xw.scr = XDefaultScreen(xw.dis); /* font */ - if(!(dc.font = XLoadQueryFont(xw.dis, FONT)) || !(dc.bfont = XLoadQueryFont(xw.dis, BOLDFONT))) - die("Can't load font %s\n", dc.font ? BOLDFONT : FONT); + if ((dc.font.fs = XCreateFontSet(xw.dis, FONT, &mc, &nmc, &ds)) == NULL || + (dc.bfont.fs = XCreateFontSet(xw.dis, BOLDFONT, &mc, &nmc, &ds)) == NULL) + die("Can't load font %s\n", dc.font.fs ? BOLDFONT : FONT); + xsetfontinfo(&dc.font); + xsetfontinfo(&dc.bfont); /* XXX: Assuming same size for bold font */ - xw.cw = dc.font->max_bounds.rbearing - dc.font->min_bounds.lbearing; - xw.ch = dc.font->ascent + dc.font->descent; + xw.cw = dc.font.rbearing - dc.font.lbearing; + xw.ch = dc.font.ascent + dc.font.descent; /* colors */ xw.cmap = XDefaultColormap(xw.dis, xw.scr); @@ -1415,9 +1474,9 @@ } void -xdraws(char *s, Glyph base, int x, int y, int len) { +xdraws(wchar_t *s, Glyph base, int x, int y, int len) { unsigned long xfg, xbg; - int winx = x*xw.cw, winy = y*xw.ch + dc.font->ascent, width = len*xw.cw; + int winx = x*xw.cw, winy = y*xw.ch + dc.font.ascent, width = len*xw.cw; int i; if(base.mode & ATTR_REVERSE) @@ -1430,15 +1489,14 @@ if(base.mode & ATTR_GFX) for(i = 0; i < len; i++) { - char c = gfx[(unsigned int)s[i] % 256]; + wchar_t c = gfx[(unsigned int)s[i] % 256]; if(c) s[i] = c; else if(s[i] > 0x5f) s[i] -= 0x5f; } - - XSetFont(xw.dis, dc.gc, base.mode & ATTR_BOLD ? dc.bfont->fid : dc.font->fid); - XDrawImageString(xw.dis, xw.buf, dc.gc, winx, winy, s, len); + + XwcDrawImageString(xw.dis, xw.buf, base.mode & ATTR_BOLD ? dc.bfont.fs : dc.font.fs, dc.gc, winx, winy, s, len); if(base.mode & ATTR_UNDERLINE) XDrawLine(xw.dis, xw.buf, dc.gc, winx, winy+1, winx+width-1, winy+1); @@ -1448,7 +1506,7 @@ xdrawcursor(void) { static int oldx = 0; static int oldy = 0; - Glyph g = {' ', ATTR_NULL, DefaultBG, DefaultCS, 0}; + Glyph g = {L' ', ATTR_NULL, DefaultBG, DefaultCS, 0}; LIMIT(oldx, 0, term.col-1); LIMIT(oldy, 0, term.row-1); @@ -1501,7 +1559,7 @@ draw(int redraw_all) { int i, x, y, ox; Glyph base, new; - char buf[DRAW_BUF_SIZ]; + wchar_t buf[DRAW_BUF_SIZ]; if(!(xw.state & WIN_VISIBLE)) return;