X-Git-Url: https://jasonwoof.com/gitweb/?a=blobdiff_plain;f=st.c;h=c4ff67c104d83cfdb22388c85f9dfd563478efeb;hb=784765ca35389a9579976d3e5ec9dc73de4bf4d9;hp=8e25d2334ec9a73703f2c6df117e046e998cbda6;hpb=fbfa1f83eb501e885d1077e95739a7d7cab2397f;p=st.git diff --git a/st.c b/st.c index 8e25d23..c4ff67c 100644 --- a/st.c +++ b/st.c @@ -5,6 +5,7 @@ #include #include #include +#include #include #include #include @@ -59,6 +60,8 @@ #define REDRAW_TIMEOUT (80*1000) /* 80 ms */ +/* macros */ +#define CLEANMASK(mask) (mask & (ShiftMask|ControlMask|Mod1Mask|Mod2Mask|Mod3Mask|Mod4Mask|Mod5Mask)) #define SERRNO strerror(errno) #define MIN(a, b) ((a) < (b) ? (a) : (b)) #define MAX(a, b) ((a) < (b) ? (b) : (a)) @@ -72,6 +75,8 @@ #define X2COL(x) (((x) - BORDER)/xw.cw) #define Y2ROW(y) (((y) - BORDER)/xw.ch) +#define VT102ID "\033[?6c" + enum glyph_attribute { ATTR_NULL = 0, ATTR_REVERSE = 1, @@ -121,6 +126,7 @@ enum escape_state { ESC_STR = 4, /* DSC, OSC, PM, APC */ ESC_ALTCHARSET = 8, ESC_STR_END = 16, /* a final string was encountered */ + ESC_TEST = 32, /* Enter in test mode */ }; enum window_state { @@ -234,6 +240,24 @@ typedef struct { struct timeval tclick2; } Selection; +typedef union { + int i; + unsigned int ui; + float f; + const void *v; +} Arg; + +typedef struct { + unsigned int mod; + KeySym keysym; + void (*func)(const Arg *); + const Arg arg; +} Shortcut; + +/* function definitions used in config.h */ +static void xzoom(const Arg *); + +/* Config.h for applying patches and the configuration. */ #include "config.h" /* Font structure */ @@ -287,7 +311,7 @@ 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(char *, Glyph *, int, int); static void tsetscroll(int, int); static void tswapscreen(void); static void tsetdirt(int, int); @@ -317,6 +341,7 @@ static void unmap(XEvent *); static char *kmap(KeySym, uint); static void kpress(XEvent *); static void cmessage(XEvent *); +static void cresize(int width, int height); static void resize(XEvent *); static void focus(XEvent *); static void brelease(XEvent *); @@ -337,10 +362,10 @@ static int utf8encode(long *, char *); static int utf8size(char *); static int isfullutf8(char *, int); +static ssize_t xwrite(int, char *, size_t); static void *xmalloc(size_t); static void *xrealloc(void *, size_t); static void *xcalloc(size_t nmemb, size_t size); -static char *smstrcat(char *, ...); static void (*handler[LASTEvent])(XEvent *) = { [KeyPress] = kpress, @@ -376,6 +401,23 @@ static char *opt_embed = NULL; static char *opt_class = NULL; static char *opt_font = NULL; +static char *usedfont = NULL; +static int usedfontsize = 0; + +ssize_t +xwrite(int fd, char *s, size_t len) { + size_t aux = len; + + while(len > 0) { + ssize_t r = write(fd, s, len); + if(r < 0) + return r; + len -= r; + s += r; + } + return aux; +} + void * xmalloc(size_t len) { void *p = malloc(len); @@ -404,44 +446,6 @@ xcalloc(size_t nmemb, size_t size) { return p; } -char * -smstrcat(char *src, ...) -{ - va_list fmtargs; - char *ret, *p, *v; - int len, slen, flen; - - len = slen = strlen(src); - - va_start(fmtargs, src); - for(;;) { - v = va_arg(fmtargs, char *); - if(v == NULL) - break; - len += strlen(v); - } - va_end(fmtargs); - - p = ret = xmalloc(len+1); - memmove(p, src, slen); - p += slen; - - va_start(fmtargs, src); - for(;;) { - v = va_arg(fmtargs, char *); - if(v == NULL) - break; - flen = strlen(v); - memmove(p, v, flen); - p += flen; - } - va_end(fmtargs); - - ret[len] = '\0'; - - return ret; -} - int utf8decode(char *s, long *u) { uchar c; @@ -661,7 +665,7 @@ void selcopy(void) { char *str, *ptr, *p; int x, y, bufsize, is_selected = 0, size; - Glyph *gp; + Glyph *gp, *last; if(sel.bx == -1) { str = NULL; @@ -671,11 +675,16 @@ selcopy(void) { /* append every set & selected glyph to the selection */ for(y = 0; y < term.row; y++) { - for(x = 0; x < term.col; x++) { - gp = &term.line[y][x]; + gp = &term.line[y][0]; + last = gp + term.col; + + while(--last >= gp && !(last->state & GLYPH_SET)) + /* nothing */; + for(x = 0; gp <= last; x++, ++gp) { if(!(is_selected = selected(x, y))) continue; + p = (gp->state & GLYPH_SET) ? gp->c : " "; size = utf8size(p); memcpy(ptr, p, size); @@ -861,11 +870,23 @@ void execsh(void) { char **args; char *envshell = getenv("SHELL"); + const struct passwd *pass = getpwuid(getuid()); + char buf[sizeof(long) * 8 + 1]; unsetenv("COLUMNS"); unsetenv("LINES"); unsetenv("TERMCAP"); + if(pass) { + setenv("LOGNAME", pass->pw_name, 1); + setenv("USER", pass->pw_name, 1); + setenv("SHELL", pass->pw_shell, 0); + setenv("HOME", pass->pw_dir, 0); + } + + snprintf(buf, sizeof(buf), "%lu", xw.win); + setenv("WINDOWID", buf, 1); + signal(SIGCHLD, SIG_DFL); signal(SIGHUP, SIG_DFL); signal(SIGINT, SIG_DFL); @@ -875,7 +896,7 @@ execsh(void) { DEFAULT(envshell, SHELL); putenv("TERM="TNAME); - args = opt_cmd ? opt_cmd : (char*[]){envshell, "-i", NULL}; + args = opt_cmd ? opt_cmd : (char *[]){envshell, "-i", NULL}; execvp(args[0], args); exit(EXIT_FAILURE); } @@ -923,13 +944,12 @@ ttynew(void) { cmdfd = m; signal(SIGCHLD, sigchld); 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)); - } + iofd = (!strcmp(opt_io, "-")) ? + STDOUT_FILENO : + open(opt_io, O_WRONLY | O_CREAT, 0666); + if(iofd < 0) { + fprintf(stderr, "Error opening %s:%s\n", + opt_io, strerror(errno)); } } } @@ -1180,8 +1200,8 @@ tmoveto(int x, int y) { } void -tsetchar(char *c) { - char *vt100_0[62] = { /* 0x41 - 0x7e */ +tsetchar(char *c, Glyph *attr, int x, int y) { + static char *vt100_0[62] = { /* 0x41 - 0x7e */ "↑", "↓", "→", "←", "█", "▚", "☃", /* A - G */ 0, 0, 0, 0, 0, 0, 0, 0, /* H - O */ 0, 0, 0, 0, 0, 0, 0, 0, /* P - W */ @@ -1195,17 +1215,17 @@ tsetchar(char *c) { /* * The table is proudly stolen from rxvt. */ - if(term.c.attr.mode & ATTR_GFX) { + if(attr->mode & ATTR_GFX) { if(c[0] >= 0x41 && c[0] <= 0x7e && vt100_0[c[0] - 0x41]) { c = vt100_0[c[0] - 0x41]; } } - 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; + term.dirty[y] = 1; + term.line[y][x] = *attr; + memcpy(term.line[y][x].c, c, UTF_SIZ); + term.line[y][x].state |= GLYPH_SET; } void @@ -1510,6 +1530,10 @@ csihandle(void) { DEFAULT(csiescseq.arg[0], 1); tmoveto(term.c.x, term.c.y+csiescseq.arg[0]); break; + case 'c': /* DA -- Device Attributes */ + if(csiescseq.arg[0] == 0) + ttywrite(VT102ID, sizeof(VT102ID) - 1); + break; case 'C': /* CUF -- Cursor Forward */ case 'a': DEFAULT(csiescseq.arg[0], 1); @@ -1784,53 +1808,86 @@ tputtab(bool forward) { void tputc(char *c, int len) { uchar ascii = *c; - - if(iofd != -1) - write(iofd, c, len); - - switch(ascii) { - case '\t': /* HT */ - tputtab(1); - return; - case '\b': /* BS */ - tmoveto(term.c.x-1, term.c.y); - return; - case '\r': /* CR */ - tmoveto(0, term.c.y); - return; - case '\f': /* LF */ - case '\v': /* VT */ - case '\n': /* LF */ - /* go to first col if the mode is set */ - tnewline(IS_SET(MODE_CRLF)); - return; - case '\a': /* BEL */ - if(term.esc & ESC_STR) + bool control = ascii < '\x20' || ascii == 0177; + + if(iofd != -1) { + if (xwrite(iofd, c, len) < 0) { + fprintf(stderr, "Error writting in %s:%s\n", + opt_io, strerror(errno)); + close(iofd); + iofd = -1; + } + } + /* + * STR sequences must be checked before of anything + * because it can use some control codes as part of the sequence + */ + if(term.esc & ESC_STR) { + switch(ascii) { + case '\033': + term.esc = ESC_START | ESC_STR_END; break; - if(!(xw.state & WIN_FOCUSED)) - xseturgency(1); - return; - case '\033': /* ESC */ - csireset(); - term.esc = ESC_START; - return; - case '\016': /* XXX: SO */ - case '\017': /* XXX: SI */ - break; - case '\032': /* SUB */ - case '\030': /* CAN */ - csireset(); + case '\a': /* backwards compatibility to xterm */ + term.esc = 0; + strhandle(); + break; + default: + strescseq.buf[strescseq.len++] = ascii; + if(strescseq.len+1 >= STR_BUF_SIZ) { + term.esc = 0; + strhandle(); + } + } return; - default: - /* case '\005': ENQ (IGNORED) */ - /* case '\000': NUL (IGNORED) */ - /* case '\021': XON (IGNORED) */ - /* case '\023': XOFF (IGNORED) */ - /* case 0177: DEL (IGNORED) */ - break; } - - if(term.esc & ESC_START) { + /* + * Actions of control codes must be performed as soon they arrive + * because they can be embedded inside a control sequence, and + * they must not cause conflicts with sequences. + */ + if(control) { + switch(ascii) { + case '\t': /* HT */ + tputtab(1); + return; + case '\b': /* BS */ + tmoveto(term.c.x-1, term.c.y); + return; + case '\r': /* CR */ + tmoveto(0, term.c.y); + return; + case '\f': /* LF */ + case '\v': /* VT */ + case '\n': /* LF */ + /* go to first col if the mode is set */ + tnewline(IS_SET(MODE_CRLF)); + return; + case '\a': /* BEL */ + if(!(xw.state & WIN_FOCUSED)) + xseturgency(1); + return; + case '\033': /* ESC */ + csireset(); + term.esc = ESC_START; + return; + case '\016': /* SO */ + term.c.attr.mode |= ATTR_GFX; + return; + case '\017': /* SI */ + term.c.attr.mode &= ~ATTR_GFX; + return; + case '\032': /* SUB */ + case '\030': /* CAN */ + csireset(); + return; + case '\005': /* ENQ (IGNORED) */ + case '\000': /* NUL (IGNORED) */ + case '\021': /* XON (IGNORED) */ + case '\023': /* XOFF (IGNORED) */ + case 0177: /* DEL (IGNORED) */ + return; + } + } else if(term.esc & ESC_START) { if(term.esc & ESC_CSI) { csiescseq.buf[csiescseq.len++] = ascii; if(BETWEEN(ascii, 0x40, 0x7E) @@ -1838,22 +1895,6 @@ tputc(char *c, int len) { term.esc = 0; csiparse(), csihandle(); } - } else if(term.esc & ESC_STR) { - switch(ascii) { - case '\033': - term.esc = ESC_START | ESC_STR_END; - break; - case '\a': /* backwards compatibility to xterm */ - term.esc = 0; - strhandle(); - break; - default: - strescseq.buf[strescseq.len++] = ascii; - if(strescseq.len+1 >= STR_BUF_SIZ) { - term.esc = 0; - strhandle(); - } - } } else if(term.esc & ESC_STR_END) { term.esc = 0; if(ascii == '\\') @@ -1876,11 +1917,25 @@ tputc(char *c, int len) { fprintf(stderr, "esc unhandled charset: ESC ( %c\n", ascii); } term.esc = 0; + } else if(term.esc & ESC_TEST) { + if(ascii == '8') { /* DEC screen alignment test. */ + char E[UTF_SIZ] = "E"; + int x, y; + + for(x = 0; x < term.col; ++x) { + for(y = 0; y < term.row; ++y) + tsetchar(E, &term.c.attr, x, y); + } + } + term.esc = 0; } else { switch(ascii) { case '[': term.esc |= ESC_CSI; break; + case '#': + term.esc |= ESC_TEST; + break; case 'P': /* DCS -- Device Control String */ case '_': /* APC -- Application Program Command */ case '^': /* PM -- Privacy Message */ @@ -1922,6 +1977,10 @@ tputc(char *c, int len) { } term.esc = 0; break; + case 'Z': /* DECID -- Identify Terminal */ + ttywrite(VT102ID, sizeof(VT102ID) - 1); + term.esc = 0; + break; case 'c': /* RIS -- Reset to inital state */ treset(); term.esc = 0; @@ -1952,20 +2011,26 @@ tputc(char *c, int len) { term.esc = 0; } } - } else { - if(sel.bx != -1 && BETWEEN(term.c.y, sel.by, sel.ey)) - sel.bx = -1; - if(ascii >= '\020' || term.c.attr.mode & ATTR_GFX) { - if(IS_SET(MODE_WRAP) && term.c.state & CURSOR_WRAPNEXT) - tnewline(1); /* always go to first col */ - tsetchar(c); - if(term.c.x+1 < term.col) { - tmoveto(term.c.x+1, term.c.y); - } else { - term.c.state |= CURSOR_WRAPNEXT; - } - } + /* + * All characters which forms part of a sequence are not + * printed + */ + return; } + /* + * Display control codes only if we are in graphic mode + */ + if(control && !(term.c.attr.mode & ATTR_GFX)) + return; + if(sel.bx != -1 && BETWEEN(term.c.y, sel.by, sel.ey)) + sel.bx = -1; + if(IS_SET(MODE_WRAP) && term.c.state & CURSOR_WRAPNEXT) + tnewline(1); /* always go to first col */ + tsetchar(c, &term.c.attr, term.c.x, term.c.y); + if(term.c.x+1 < term.col) + tmoveto(term.c.x+1, term.c.y); + else + term.c.state |= CURSOR_WRAPNEXT; } int @@ -2030,7 +2095,8 @@ tresize(int col, int row) { *bp = 1; } /* update terminal size */ - term.col = col, term.row = row; + term.col = col; + term.row = row; /* make use of the LIMIT in tmoveto */ tmoveto(term.c.x, term.c.y); /* reset scrolling region */ @@ -2130,22 +2196,17 @@ xhints(void) { XFree(sizeh); } -void -xinitfont(Font *f, char *fontstr) { - FcPattern *pattern, *match; +int +xloadfont(Font *f, FcPattern *pattern) { + FcPattern *match; FcResult result; - pattern = FcNameParse((FcChar8 *)fontstr); - if(!pattern) - die("st: can't open font %s\n", fontstr); - match = XftFontMatch(xw.dpy, xw.scr, pattern, &result); - FcPatternDestroy(pattern); if(!match) - die("st: can't open font %s\n", fontstr); + return 1; if(!(f->xft_set = XftFontOpenPattern(xw.dpy, match))) { FcPatternDestroy(match); - die("st: can't open font %s.\n", fontstr); + return 1; } f->ascent = f->xft_set->ascent; @@ -2155,27 +2216,68 @@ xinitfont(Font *f, char *fontstr) { f->height = f->xft_set->height; f->width = f->lbearing + f->rbearing; + + return 0; } void -initfonts(char *fontstr) { - char *fstr; +xloadfonts(char *fontstr, int fontsize) { + FcPattern *pattern; + FcResult result; + double fontval; + + pattern = FcNameParse((FcChar8 *)fontstr); + if(!pattern) + die("st: can't open font %s\n", fontstr); + + if(fontsize > 0) { + FcPatternDel(pattern, FC_PIXEL_SIZE); + FcPatternAddDouble(pattern, FC_PIXEL_SIZE, (double)fontsize); + usedfontsize = fontsize; + } else { + result = FcPatternGetDouble(pattern, FC_PIXEL_SIZE, 0, &fontval); + if(result == FcResultMatch) { + usedfontsize = (int)fontval; + } else { + /* + * Default font size is 12, if none given. This is to + * have a known usedfontsize value. + */ + FcPatternAddDouble(pattern, FC_PIXEL_SIZE, 12); + usedfontsize = 12; + } + } - xinitfont(&dc.font, fontstr); + if(xloadfont(&dc.font, pattern)) + die("st: can't open font %s\n", fontstr); + + /* Setting character width and height. */ xw.cw = dc.font.width; xw.ch = dc.font.height; - fstr = smstrcat(fontstr, ":weight=bold", NULL); - xinitfont(&dc.bfont, fstr); - free(fstr); + FcPatternDel(pattern, FC_WEIGHT); + FcPatternAddInteger(pattern, FC_WEIGHT, FC_WEIGHT_BOLD); + if(xloadfont(&dc.bfont, pattern)) + die("st: can't open font %s\n", fontstr); - fstr = smstrcat(fontstr, ":slant=italic,oblique", NULL); - xinitfont(&dc.ifont, fstr); - free(fstr); + FcPatternDel(pattern, FC_SLANT); + FcPatternAddInteger(pattern, FC_SLANT, FC_SLANT_ITALIC); + if(xloadfont(&dc.ibfont, pattern)) + die("st: can't open font %s\n", fontstr); + + FcPatternDel(pattern, FC_WEIGHT); + if(xloadfont(&dc.ifont, pattern)) + die("st: can't open font %s\n", fontstr); - fstr = smstrcat(fontstr, ":weight=bold:slant=italic,oblique", NULL); - xinitfont(&dc.ibfont, fstr); - free(fstr); + FcPatternDestroy(pattern); +} + +void +xzoom(const Arg *arg) +{ + xloadfonts(usedfont, usedfontsize + arg->i); + cresize(0, 0); + draw(); } void @@ -2191,7 +2293,8 @@ xinit(void) { xw.vis = XDefaultVisual(xw.dpy, xw.scr); /* font */ - initfonts((opt_font != NULL)? opt_font : FONT); + usedfont = (opt_font == NULL)? FONT : opt_font; + xloadfonts(usedfont, 0); /* colors */ xw.cmap = XDefaultColormap(xw.dpy, xw.scr); @@ -2293,7 +2396,7 @@ xdraws(char *s, Glyph base, int x, int y, int charlen, int bytelen) { * Those ranges will not be brightened: * 8 - 15 – bright system colors * 196 - 231 – highest 256 color cube - * 252 - 255 – brightest colors in grescale + * 252 - 255 – brightest colors in greyscale */ font = &dc.bfont; } @@ -2338,7 +2441,7 @@ xdraws(char *s, Glyph base, int x, int y, int charlen, int bytelen) { } if(x + charlen >= term.col-1) { xclear(winx + width, (y == 0)? 0 : winy, xw.w, - winy + xw.ch + (y == term.row-1)? xw.h : 0); + (y == term.row-1)? xw.h : (winy + xw.ch)); } if(y == 0) xclear(winx, 0, winx + width, BORDER); @@ -2499,9 +2602,11 @@ xseturgency(int add) { void focus(XEvent *ev) { if(ev->type == FocusIn) { + XSetICFocus(xw.xic); xw.state |= WIN_FOCUSED; xseturgency(0); } else { + XUnsetICFocus(xw.xic); xw.state &= ~WIN_FOCUSED; } } @@ -2527,11 +2632,8 @@ void kpress(XEvent *ev) { XKeyEvent *e = &ev->xkey; KeySym ksym; - char buf[32]; - char *customkey; - int len; - int meta; - int shift; + char buf[32], *customkey; + int len, meta, shift, i; Status status; if (IS_SET(MODE_KBDLOCK)) @@ -2541,7 +2643,17 @@ kpress(XEvent *ev) { shift = e->state & ShiftMask; len = XmbLookupString(xw.xic, e, buf, sizeof(buf), &ksym, &status); - /* 1. custom keys from config.h */ + /* 1. shortcuts */ + for(i = 0; i < LEN(shortcuts); i++) { + if((ksym == shortcuts[i].keysym) + && (CLEANMASK(shortcuts[i].mod) == \ + CLEANMASK(e->state)) + && shortcuts[i].func) { + shortcuts[i].func(&(shortcuts[i].arg)); + } + } + + /* 2. custom keys from config.h */ if((customkey = kmap(ksym, e->state))) { ttywrite(customkey, strlen(customkey)); /* 2. hardcoded (overrides X lookup) */ @@ -2599,14 +2711,15 @@ cmessage(XEvent *e) { } void -resize(XEvent *e) { +cresize(int width, int height) +{ int col, row; - if(e->xconfigure.width == xw.w && e->xconfigure.height == xw.h) - return; + if(width != 0) + xw.w = width; + if(height != 0) + xw.h = height; - xw.w = e->xconfigure.width; - xw.h = e->xconfigure.height; col = (xw.w - 2*BORDER) / xw.cw; row = (xw.h - 2*BORDER) / xw.ch; if(col == term.col && row == term.row) @@ -2618,6 +2731,14 @@ resize(XEvent *e) { } void +resize(XEvent *e) { + if(e->xconfigure.width == xw.w && e->xconfigure.height == xw.h) + return; + + cresize(e->xconfigure.width, e->xconfigure.height); +} + +void run(void) { XEvent ev; fd_set rfd; @@ -2655,7 +2776,7 @@ run(void) { while(XPending(xw.dpy)) { XNextEvent(xw.dpy, &ev); - if(XFilterEvent(&ev, xw.win)) + if(XFilterEvent(&ev, None)) continue; if(handler[ev.type]) (handler[ev.type])(&ev); @@ -2730,9 +2851,10 @@ main(int argc, char *argv[]) { run: setlocale(LC_CTYPE, ""); + XSetLocaleModifiers(""); tnew(80, 24); - ttynew(); xinit(); + ttynew(); selinit(); run(); return 0;