JasonWoof Got questions, comments, patches, etc.? Contact Jason Woofenden
moved term.hidec in term.c for consistency, put back delay in xbell()
[st.git] / st.c
diff --git a/st.c b/st.c
index deddb0d..9b77a0b 100644 (file)
--- a/st.c
+++ b/st.c
 #include <X11/keysym.h>
 #include <X11/Xutil.h>
 
-#define TNAME "st-256color"
+#if   defined(LINUX)
+ #include <pty.h>
+#elif defined(OPENBSD)
+ #include <util.h>
+#elif defined(FREEBSD)
+ #include <libutil.h>
+#endif
 
 /* Arbitrary sizes */
 #define ESC_TITLE_SIZ 256
@@ -36,6 +42,7 @@
 #define BETWEEN(x, a, b)  ((a) <= (x) && (x) <= (b))
 #define LIMIT(x, a, b)    (x) = (x) < (a) ? (a) : (x) > (b) ? (b) : (x)
 #define ATTRCMP(a, b) ((a).mode != (b).mode || (a).fg != (b).fg || (a).bg != (b).bg)
+#define IS_SET(flag) (term.mode & (flag))
 
 /* Attribute, Cursor, Character state, Terminal mode, Screen draw mode */
 enum { ATTR_NULL=0 , ATTR_REVERSE=1 , ATTR_UNDERLINE=2, ATTR_BOLD=4, ATTR_GFX=8 };
@@ -60,6 +67,7 @@ typedef struct {
        Glyph attr;      /* current char attributes */
        int x;
        int y;
+       char hide;
 } TCursor;
 
 /* CSI Escape sequence structs */
@@ -79,7 +87,6 @@ typedef struct {
        int col;        /* nb col */
        Line* line; /* screen */
        TCursor c;      /* cursor */
-       char hidec;
        int top;        /* top    scroll limit */
        int bot;        /* bottom scroll limit */
        int mode;       /* terminal mode flags */
@@ -107,8 +114,6 @@ typedef struct {
        char s[ESC_BUF_SIZ];
 } Key;
 
-#include "config.h"
-
 /* Drawing Context */
 typedef struct {
        unsigned long col[256];
@@ -117,6 +122,8 @@ typedef struct {
        GC gc;
 } DC;
 
+#include "config.h"
+
 static void die(const char *errstr, ...);
 static void draw(int);
 static void execsh(void);
@@ -130,7 +137,6 @@ static void csireset(void);
 
 static void tclearregion(int, int, int, int);
 static void tcursor(int);
-static void tmovecursor(int);
 static void tdeletechar(int);
 static void tdeleteline(int);
 static void tinsertblank(int);
@@ -138,11 +144,11 @@ static void tinsertblankline(int);
 static void tmoveto(int, int);
 static void tnew(int, int);
 static void tnewline(void);
+static void tputtab(void);
 static void tputc(char);
 static void tputs(char*, int);
 static void treset(void);
 static void tresize(int, int);
-static void tscroll(void);
 static void tscrollup(int);
 static void tscrolldown(int);
 static void tsetattr(int*, int);
@@ -154,6 +160,9 @@ static void ttyread(void);
 static void ttyresize(int, int);
 static void ttywrite(const char *, size_t);
 
+static void xbell(void);
+static void xdraws(char *, Glyph, int, int, int);
+static void xhints(void);
 static void xclear(int, int, int, int);
 static void xcursor(int);
 static void xinit(void);
@@ -212,17 +221,16 @@ die(const char *errstr, ...) {
 void
 execsh(void) {
        char *args[3] = {getenv("SHELL"), "-i", NULL};
-       DEFAULT(args[0], "/bin/sh"); /* default shell if getenv() failed */
+       DEFAULT(args[0], "/bin/sh"); /* if getenv() failed */
        putenv("TERM=" TNAME);
        execvp(args[0], args);
 }
 
 void
-xbell(void) { /* visual bell */
-       XRectangle r = { BORDER, BORDER, xw.bufw, xw.bufh };
+xbell(void) {
        XSetForeground(xw.dis, dc.gc, dc.col[BellCol]);
-       XFillRectangles(xw.dis, xw.win, dc.gc, &r, 1);
-       /* usleep(30000); */
+       XFillRectangle(xw.dis, xw.win, dc.gc, BORDER, BORDER, xw.bufw, xw.bufh);
+       usleep(BellTime);
        draw(SCREEN_REDRAW);
 }
 
@@ -240,19 +248,12 @@ sigchld(int a) {
 void
 ttynew(void) {
        int m, s;
-       char *pts;
-
-       if((m = posix_openpt(O_RDWR | O_NOCTTY)) < 0)
-               die("openpt failed: %s\n", SERRNO);
-       if(grantpt(m) < 0)
-               die("grandpt failed: %s\n", SERRNO);
-       if(unlockpt(m) < 0)
-               die("unlockpt failed: %s\n", SERRNO);
-       if(!(pts = ptsname(m)))
-               die("ptsname failed: %s\n", SERRNO);
-       if((s = open(pts, O_RDWR | O_NOCTTY)) < 0)
-               die("Couldn't open slave: %s\n", SERRNO);
-       fcntl(s, F_SETFL, O_NDELAY);
+       
+       /* seems to work fine on linux, openbsd and freebsd */
+       struct winsize w = {term.row, term.col, 0, 0};
+       if(openpty(&m, &s, NULL, NULL, &w) < 0)
+               die("openpty failed: %s\n", SERRNO);
+
        switch(pid = fork()) {
        case -1:
                die("fork failed\n");
@@ -263,7 +264,9 @@ ttynew(void) {
                dup2(s, STDOUT_FILENO);
                dup2(s, STDERR_FILENO);
                if(ioctl(s, TIOCSCTTY, NULL) < 0)
-                       die("ioctl TTIOCSTTY failed: %s\n", SERRNO);
+                       die("ioctl TIOCSCTTY failed: %s\n", SERRNO);
+               close(s);
+               close(m);
                execsh();
                break;
        default:
@@ -321,44 +324,26 @@ tcursor(int mode) {
 
 void
 treset(void) {
-       term.c.attr.mode = ATTR_NULL;
-       term.c.attr.fg = DefaultFG;
-       term.c.attr.bg = DefaultBG;
-       term.c.x = term.c.y = 0;
-       term.hidec = 0;
+       term.c = (TCursor){{
+               .mode = ATTR_NULL, 
+               .fg = DefaultFG, 
+               .bg = DefaultBG
+       }, .x = 0, .y = 0, .hide = 0};
+       
        term.top = 0, term.bot = term.row - 1;
        term.mode = MODE_WRAP;
        tclearregion(0, 0, term.col-1, term.row-1);
 }
 
 void
-tnew(int col, int row) {   /* screen size */
+tnew(int col, int row) {
+       /* set screen size */
        term.row = row, term.col = col;
-       term.top = 0, term.bot = term.row - 1;
-       /* mode */
-       term.mode = MODE_WRAP;
-       /* cursor */
-       term.c.attr.mode = ATTR_NULL;
-       term.c.attr.fg = DefaultFG;
-       term.c.attr.bg = DefaultBG;
-       term.c.x = term.c.y = 0;
-       term.hidec = 0;
-       /* allocate screen */
-       term.line = calloc(term.row, sizeof(Line));
+       term.line = malloc(term.row * sizeof(Line));
        for(row = 0 ; row < term.row; row++)
-               term.line[row] = calloc(term.col, sizeof(Glyph));
-}
-
-/* TODO: Replace with scrollup/scolldown */
-void
-tscroll(void) {
-       Line temp = term.line[term.top];
-       int i;
-
-       for(i = term.top; i < term.bot; i++)
-               term.line[i] = term.line[i+1];
-       memset(temp, 0, sizeof(Glyph) * term.col);
-       term.line[term.bot] = temp;
+               term.line[row] = malloc(term.col * sizeof(Glyph));
+       /* setup screen */
+       treset();
 }
 
 void
@@ -398,7 +383,7 @@ void
 tnewline(void) {
        int y = term.c.y + 1;
        if(y > term.bot)
-               tscroll(), y = term.bot;
+               tscrollup(1), y = term.bot;
        tmoveto(0, y);
 }
 
@@ -433,37 +418,6 @@ tmoveto(int x, int y) {
 }
 
 void
-tmovecursor(int dir) {
-       int xf = term.c.x, yf = term.c.y;
-       
-       switch(dir) {
-       case CURSOR_UP:
-               yf--;
-               break;
-       case CURSOR_DOWN:
-               yf++;
-               break;
-       case CURSOR_LEFT:
-               xf--;
-               if(term.mode & MODE_WRAP && xf < 0) {
-                       xf = term.col-1, yf--;
-                       if(yf < term.top)
-                               yf = term.top, xf = 0;
-               }
-               break;
-       case CURSOR_RIGHT:
-               xf++;
-               if(term.mode & MODE_WRAP && xf >= term.col) {
-                       xf = 0, yf++;
-                       if(yf > term.bot)
-                               yf = term.bot, tscroll();
-               }
-               break;
-       }
-       tmoveto(xf, yf);
-}
-       
-void
 tsetchar(char c) {
        term.line[term.c.y][term.c.x] = term.c.attr;
        term.line[term.c.y][term.c.x].c = c;
@@ -499,21 +453,21 @@ tdeletechar(int n) {
                return;
        }
        memmove(&term.line[term.c.y][dst], &term.line[term.c.y][src], size * sizeof(Glyph));
-       tclearregion(term.col-size, term.c.y, term.col-1, term.c.y);
+       tclearregion(term.col-n, term.c.y, term.col-1, term.c.y);
 }
 
 void
 tinsertblank(int n) {
        int src = term.c.x;
        int dst = src + n;
-       int size = term.col - n - src;
+       int size = term.col - dst;
 
        if(dst >= term.col) {
                tclearregion(term.c.x, term.c.y, term.col-1, term.c.y);
                return;
        }
        memmove(&term.line[term.c.y][dst], &term.line[term.c.y][src], size * sizeof(Glyph));
-       tclearregion(src, term.c.y, dst, term.c.y);
+       tclearregion(src, term.c.y, dst - 1, term.c.y);
 }
 
 void
@@ -756,7 +710,7 @@ csihandle(void) {
                        case 12: /* att610 -- Stop blinking cursor (IGNORED) */
                                break;
                        case 25:
-                               term.hidec = 1;
+                               term.c.hide = 1;
                                break;
                        case 1048: /* XXX: no alt. screen to erase/save */
                        case 1049:
@@ -805,7 +759,7 @@ csihandle(void) {
                        case 12: /* att610 -- Start blinking cursor (IGNORED) */
                                break;
                        case 25:
-                               term.hidec = 0;
+                               term.c.hide = 0;
                                break;
                        case 1048: 
                        case 1049: /* XXX: no alt. screen to erase/save */
@@ -864,12 +818,7 @@ csireset(void) {
 void
 tputtab(void) {
        int space = TAB - term.c.x % TAB;
-       
-       if(term.c.x + space >= term.col)
-               space--;
-       
-       for(; space > 0; space--)
-               tmovecursor(CURSOR_RIGHT);
+       tmoveto(term.c.x + space, term.c.y);
 }
 
 void
@@ -975,7 +924,7 @@ tputc(char c) {
                        tputtab();
                        break;
                case '\b':
-                       tmovecursor(CURSOR_LEFT);
+                       tmoveto(term.c.x-1, term.c.y);
                        break;
                case '\r':
                        tmoveto(0, term.c.y);
@@ -992,7 +941,10 @@ tputc(char c) {
                        break;
                default:
                        tsetchar(c);
-                       tmovecursor(CURSOR_RIGHT);
+                       if(term.c.x+1 < term.col) {
+                               tmoveto(term.c.x+1, term.c.y);
+                       } else if(IS_SET(MODE_WRAP))
+                               tnewline();
                        break;
                }
        }
@@ -1007,23 +959,28 @@ tputs(char *s, int len) {
 void
 tresize(int col, int row) {
        int i;
-       Line *line;
        int minrow = MIN(row, term.row);
        int mincol = MIN(col, term.col);
 
        if(col < 1 || row < 1)
                return;
-       /* alloc */
-       line = calloc(row, sizeof(Line));
-       for(i = 0 ; i < row; i++)
-               line[i] = calloc(col, sizeof(Glyph));
-       /* copy */
-       for(i = 0 ; i < minrow; i++)
-               memcpy(line[i], term.line[i], mincol * sizeof(Glyph));
-       /* free */
-       for(i = 0; i < term.row; i++)
+
+       /* free uneeded rows */
+       for(i = row; i < term.row; i++)
                free(term.line[i]);
-       free(term.line);
+
+       /* resize to new height */
+       term.line = realloc(term.line, row * sizeof(Line));
+
+       /* resize each row to new width, zero-pad if needed */
+       for(i = 0; i < minrow; i++) {
+               term.line[i] = realloc(term.line[i], col * sizeof(Glyph));
+               memset(term.line[i] + mincol, 0, (col - mincol) * sizeof(Glyph));
+       }
+
+       /* allocate any new rows */
+       for(/* i == minrow */; i < row; i++)
+               term.line[i] = calloc(col, sizeof(Glyph));
        
        LIMIT(term.c.x, 0, col-1);
        LIMIT(term.c.y, 0, row-1);
@@ -1031,7 +988,6 @@ tresize(int col, int row) {
        LIMIT(term.bot, 0, row-1);
        
        term.bot = row-1;
-       term.line = line;
        term.col = col, term.row = row;
 }
 
@@ -1102,10 +1058,9 @@ xhints(void)
 
 void
 xinit(void) {
-       xw.dis = XOpenDisplay(NULL);
-       xw.scr = XDefaultScreen(xw.dis);
-       if(!xw.dis)
+       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)))
@@ -1175,7 +1130,7 @@ xcursor(int mode) {
        
        if(term.line[term.c.y][term.c.x].state & GLYPH_SET)
                g.c = term.line[term.c.y][term.c.x].c;
-
+       
        /* remove the old cursor */
        if(term.line[oldy][oldx].state & GLYPH_SET)
                xdraws(&term.line[oldy][oldx].c, term.line[oldy][oldx], oldx, oldy, 1);
@@ -1210,7 +1165,7 @@ draw(int dummy) {
                        if(term.line[y][x].state & GLYPH_SET)
                                xdrawc(x, y, term.line[y][x]);
 
-       if(!term.hidec)
+       if(!term.c.hide)
                xcursor(CURSOR_DRAW);
        XCopyArea(xw.dis, xw.buf, xw.win, dc.gc, 0, 0, xw.bufw, xw.bufh, BORDER, BORDER);
        XFlush(xw.dis);
@@ -1243,7 +1198,7 @@ draw(int redraw_all) {
                }
                xdraws(buf, base, ox, y, i);
        }
-       xcursor(term.hidec ? CURSOR_HIDE : CURSOR_DRAW);
+       xcursor(term.c.hide ? CURSOR_HIDE : CURSOR_DRAW);
        XCopyArea(xw.dis, xw.buf, xw.win, dc.gc, 0, 0, xw.bufw, xw.bufh, BORDER, BORDER);
        XFlush(xw.dis);
 }
@@ -1291,7 +1246,7 @@ kpress(XEvent *ev) {
                case XK_Down:
                case XK_Left:
                case XK_Right:
-                       sprintf(buf, "\033%c%c", term.mode & MODE_APPKEYPAD ? 'O' : '[', "DACB"[ksym - XK_Left]);
+                       sprintf(buf, "\033%c%c", IS_SET(MODE_APPKEYPAD) ? 'O' : '[', "DACB"[ksym - XK_Left]);
                        ttywrite(buf, 3);
                        break;
                case XK_Insert:
@@ -1358,7 +1313,7 @@ run(void) {
 int
 main(int argc, char *argv[]) {
        if(argc == 2 && !strncmp("-v", argv[1], 3))
-               die("st-" VERSION ", © 2009 st engineers\n");
+               die("st-" VERSION ", (c) 2010 st engineers\n");
        else if(argc != 1)
                die("usage: st [-v]\n");
        setlocale(LC_CTYPE, "");