LC_CTYPE=en_US.UTF-8 should be set for UTF-8, it has some bugs and
sometimes doesn't recognise good UTF-8 string.
http://img51.imageshack.us/img51/4591/201011072157261024x768s.png
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 <stdio.h>
#include <stdlib.h>
#include <string.h>
+#include <wchar.h>
+#include <wctype.h>
#include <signal.h>
#include <sys/ioctl.h>
#include <sys/select.h>
@@ -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 '[' [[ [<priv>] <arg> [;]] <mode>] */
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 <n> blank char */
+ case L'@': /* ICH -- Insert <n> blank char */
DEFAULT(escseq.arg[0], 1);
tinsertblank(escseq.arg[0]);
break;
- case 'A': /* CUU -- Cursor <n> Up */
- case 'e':
+ case L'A': /* CUU -- Cursor <n> Up */
+ case L'e':
DEFAULT(escseq.arg[0], 1);
tmoveto(term.c.x, term.c.y-escseq.arg[0]);
break;
- case 'B': /* CUD -- Cursor <n> Down */
+ case L'B': /* CUD -- Cursor <n> Down */
DEFAULT(escseq.arg[0], 1);
tmoveto(term.c.x, term.c.y+escseq.arg[0]);
break;
- case 'C': /* CUF -- Cursor <n> Forward */
- case 'a':
+ case L'C': /* CUF -- Cursor <n> Forward */
+ case L'a':
DEFAULT(escseq.arg[0], 1);
tmoveto(term.c.x+escseq.arg[0], term.c.y);
break;
- case 'D': /* CUB -- Cursor <n> Backward */
+ case L'D': /* CUB -- Cursor <n> Backward */
DEFAULT(escseq.arg[0], 1);
tmoveto(term.c.x-escseq.arg[0], term.c.y);
break;
- case 'E': /* CNL -- Cursor <n> Down and first col */
+ case L'E': /* CNL -- Cursor <n> Down and first col */
DEFAULT(escseq.arg[0], 1);
tmoveto(0, term.c.y+escseq.arg[0]);
break;
- case 'F': /* CPL -- Cursor <n> Up and first col */
+ case L'F': /* CPL -- Cursor <n> Up and first col */
DEFAULT(escseq.arg[0], 1);
tmoveto(0, term.c.y-escseq.arg[0]);
break;
- case 'G': /* CHA -- Move to <col> */
- case '`': /* XXX: HPA -- same? */
+ case L'G': /* CHA -- Move to <col> */
+ case L'`': /* XXX: HPA -- same? */
DEFAULT(escseq.arg[0], 1);
tmoveto(escseq.arg[0]-1, term.c.y);
break;
- case 'H': /* CUP -- Move to <row> <col> */
- case 'f': /* XXX: HVP -- same? */
+ case L'H': /* CUP -- Move to <row> <col> */
+ 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 <n> 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 <n> line up */
+ case L'S': /* SU -- Scroll <n> line up */
DEFAULT(escseq.arg[0], 1);
tscrollup(term.top, escseq.arg[0]);
break;
- case 'T': /* SD -- Scroll <n> line down */
+ case L'T': /* SD -- Scroll <n> line down */
DEFAULT(escseq.arg[0], 1);
tscrolldown(term.top, escseq.arg[0]);
break;
- case 'L': /* IL -- Insert <n> blank lines */
+ case L'L': /* IL -- Insert <n> 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 <n> lines */
+ case L'M': /* DL -- Delete <n> lines */
DEFAULT(escseq.arg[0], 1);
tdeleteline(escseq.arg[0]);
break;
- case 'X': /* ECH -- Erase <n> char */
+ case L'X': /* ECH -- Erase <n> 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 <n> char */
+ case L'P': /* DCH -- Delete <n> char */
DEFAULT(escseq.arg[0], 1);
tdeletechar(escseq.arg[0]);
break;
/* XXX: (CSI n Z) CBT -- Cursor Backward Tabulation <n> tab stops */
- case 'd': /* VPA -- Move to <row> */
+ case L'd': /* VPA -- Move to <row> */
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; i<fnum; i++,xfonts++,fontnames++) {
+ if(fi->ascent < (*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;