Xinqi Bao's Git
1 /* See LICENSE for licence details. */
13 die(const char *errstr
, ...) {
17 vfprintf(stderr
, errstr
, ap
);
24 char *args
[3] = {SHELL
, "-i", NULL
};
25 putenv("TERM=" TNAME
);
30 xbell(void) { /* visual bell */
31 XRectangle r
= { 0, 0, xw
.w
, xw
.h
};
32 XSetForeground(xw
.dis
, dc
.gc
, dc
.col
[BellCol
]);
33 XFillRectangles(xw
.dis
, xw
.win
, dc
.gc
, &r
, 1);
44 if((m
= posix_openpt(O_RDWR
| O_NOCTTY
)) < 0)
50 if((pts
= ptsname(m
)) == NULL
)
52 if((s
= open(pts
, O_RDWR
| O_NOCTTY
)) < 0)
54 fcntl(s
, F_SETFL
, O_NDELAY
);
55 switch(pid
= fork()) {
60 setsid(); /* create a new process group */
61 dup2(s
, STDIN_FILENO
);
62 dup2(s
, STDOUT_FILENO
);
63 dup2(s
, STDERR_FILENO
);
64 if(ioctl(s
, TIOCSCTTY
, NULL
) < 0)
65 die("slave TTIOCSTTY");
77 fprintf(stderr
, " %02x %c ", c
, isprint(c
)?c
:'.');
79 fprintf(stderr
, "\n");
84 char buf
[BUFSIZ
] = {0};
87 switch(ret
= read(cmdfd
, buf
, BUFSIZ
)) {
88 case -1: /* error or exit */
89 /* XXX: be more precise */
98 ttywrite(char *s
, size_t n
) {
99 if(write(cmdfd
, s
, n
) == -1)
100 die("write error on tty.");
104 ttyresize(int x
, int y
) {
109 w
.ws_xpixel
= w
.ws_ypixel
= 0;
110 if(ioctl(cmdfd
, TIOCSWINSZ
, &w
) < 0)
111 fprintf(stderr
, "Couldn't set window size: %m\n");
127 else if(BETWEEN(c
, 0x40, 0x7E))
138 x
= term
.c
.x
, y
= term
.c
.y
;
139 else if(mode
== CSload
)
144 tnew(int col
, int row
) { /* screen size */
145 term
.row
= row
, term
.col
= col
;
146 term
.top
= 0, term
.bot
= term
.row
- 1;
150 term
.c
.attr
.mode
= ATnone
;
151 term
.c
.attr
.fg
= DefaultFG
;
152 term
.c
.attr
.bg
= DefaultBG
;
153 term
.c
.x
= term
.c
.y
= 0;
155 /* allocate screen */
156 term
.line
= calloc(term
.row
, sizeof(Line
));
157 for(row
= 0 ; row
< term
.row
; row
++)
158 term
.line
[row
] = calloc(term
.col
, sizeof(Glyph
));
163 Line temp
= term
.line
[term
.top
];
166 for(i
= term
.top
; i
< term
.bot
; i
++)
167 term
.line
[i
] = term
.line
[i
+1];
168 memset(temp
, 0, sizeof(Glyph
) * term
.col
);
169 term
.line
[term
.bot
] = temp
;
175 int y
= term
.c
.y
+ 1;
178 tscroll(), y
= term
.bot
;
185 escseq
.buf
[escseq
.len
++] = c
;
186 if(escfinal(c
) || escseq
.len
>= ESCSIZ
) {
187 escparse(), eschandle();
196 char *p
= escseq
.buf
;
199 switch(escseq
.pre
= *p
++) {
202 escseq
.priv
= 1, p
++;
204 while(p
< escseq
.buf
+escseq
.len
) {
206 escseq
.arg
[escseq
.narg
] *= 10;
207 escseq
.arg
[escseq
.narg
] += *(p
++) - '0'/*, noarg = 0 */;
219 /* XXX: graphic character set */
225 tmoveto(int x
, int y
) {
226 term
.c
.x
= x
< 0 ? 0 : x
>= term
.col
? term
.col
-1 : x
;
227 term
.c
.y
= y
< 0 ? 0 : y
>= term
.row
? term
.row
-1 : y
;
232 int xf
= term
.c
.x
, yf
= term
.c
.y
;
244 xf
= term
.col
-1, yf
--;
246 yf
= term
.top
, xf
= 0;
254 yf
= term
.bot
, tscroll();
263 term
.line
[term
.c
.y
][term
.c
.x
] = term
.c
.attr
;
264 term
.line
[term
.c
.y
][term
.c
.x
].c
= c
;
265 term
.line
[term
.c
.y
][term
.c
.x
].state
|= CRset
| CRupdate
;
269 tclearregion(int x1
, int y1
, int x2
, int y2
) {
272 LIMIT(x1
, 0, term
.col
-1);
273 LIMIT(x2
, 0, term
.col
-1);
274 LIMIT(y1
, 0, term
.row
-1);
275 LIMIT(y2
, 0, term
.row
-1);
277 /* XXX: could be optimized */
278 for(x
= x1
; x
<= x2
; x
++)
279 for(y
= y1
; y
<= y2
; y
++)
280 memset(&term
.line
[y
][x
], 0, sizeof(Glyph
));
282 xclear(x1
, y1
, x2
, y2
);
287 int src
= term
.c
.x
+ n
;
289 int size
= term
.col
- src
;
291 if(src
>= term
.col
) {
292 tclearregion(term
.c
.x
, term
.c
.y
, term
.col
-1, term
.c
.y
);
295 memmove(&term
.line
[term
.c
.y
][dst
], &term
.line
[term
.c
.y
][src
], size
* sizeof(Glyph
));
296 tclearregion(term
.col
-size
, term
.c
.y
, term
.col
-1, term
.c
.y
);
300 tinsertblank(int n
) {
303 int size
= term
.col
- n
- src
;
305 if(dst
>= term
.col
) {
306 tclearregion(term
.c
.x
, term
.c
.y
, term
.col
-1, term
.c
.y
);
309 memmove(&term
.line
[term
.c
.y
][dst
], &term
.line
[term
.c
.y
][src
], size
* sizeof(Glyph
));
310 tclearregion(src
, term
.c
.y
, dst
, term
.c
.y
);
314 tinsertblankline (int n
) {
319 if(term
.c
.y
> term
.bot
)
321 else if(term
.c
.y
< term
.top
)
323 if(term
.c
.y
+ n
>= bot
) {
324 tclearregion(0, term
.c
.y
, term
.col
-1, bot
);
327 for(i
= bot
; i
>= term
.c
.y
+n
; i
--) {
328 /* swap deleted line <-> blanked line */
329 blank
= term
.line
[i
];
330 term
.line
[i
] = term
.line
[i
-n
];
331 term
.line
[i
-n
] = blank
;
333 memset(blank
, 0, term
.col
* sizeof(Glyph
));
344 if(term
.c
.y
> term
.bot
)
346 else if(term
.c
.y
< term
.top
)
348 if(term
.c
.y
+ n
>= bot
) {
349 tclearregion(0, term
.c
.y
, term
.col
-1, bot
);
352 for(i
= term
.c
.y
; i
<= bot
-n
; i
++) {
353 /* swap deleted line <-> blanked line */
354 blank
= term
.line
[i
];
355 term
.line
[i
] = term
.line
[i
+n
];
356 term
.line
[i
+n
] = blank
;
358 memset(blank
, 0, term
.col
* sizeof(Glyph
));
363 tsetattr(int *attr
, int l
) {
366 for(i
= 0; i
< l
; i
++) {
369 memset(&term
.c
.attr
, 0, sizeof(term
.c
.attr
));
370 term
.c
.attr
.fg
= DefaultFG
;
371 term
.c
.attr
.bg
= DefaultBG
;
374 term
.c
.attr
.mode
|= ATbold
;
377 term
.c
.attr
.mode
|= ATunderline
;
380 term
.c
.attr
.mode
|= ATreverse
;
383 term
.c
.hidden
= CShide
;
386 term
.c
.attr
.mode
&= ~ATbold
;
389 term
.c
.attr
.mode
&= ~ATunderline
;
392 term
.c
.attr
.mode
&= ~ATreverse
;
395 term
.c
.attr
.fg
= DefaultFG
;
398 term
.c
.attr
.fg
= DefaultBG
;
401 if(BETWEEN(attr
[i
], 30, 37))
402 term
.c
.attr
.fg
= attr
[i
] - 30;
403 else if(BETWEEN(attr
[i
], 40, 47))
404 term
.c
.attr
.bg
= attr
[i
] - 40;
411 tsetscroll(int t
, int b
) {
414 LIMIT(t
, 0, term
.row
-1);
415 LIMIT(b
, 0, term
.row
-1);
431 switch(escseq
.mode
) {
432 case '@': /* Insert <n> blank char */
433 DEFAULT(escseq
.arg
[0], 1);
434 tinsertblank(escseq
.arg
[0]);
436 case 'A': /* Cursor <n> Up */
438 DEFAULT(escseq
.arg
[0], 1);
439 tmoveto(term
.c
.x
, term
.c
.y
-escseq
.arg
[0]);
441 case 'B': /* Cursor <n> Down */
442 DEFAULT(escseq
.arg
[0], 1);
443 tmoveto(term
.c
.x
, term
.c
.y
+escseq
.arg
[0]);
445 case 'C': /* Cursor <n> Forward */
447 DEFAULT(escseq
.arg
[0], 1);
448 tmoveto(term
.c
.x
+escseq
.arg
[0], term
.c
.y
);
450 case 'D': /* Cursor <n> Backward */
451 DEFAULT(escseq
.arg
[0], 1);
452 tmoveto(term
.c
.x
-escseq
.arg
[0], term
.c
.y
);
454 case 'E': /* Cursor <n> Down and first col */
455 DEFAULT(escseq
.arg
[0], 1);
456 tmoveto(0, term
.c
.y
+escseq
.arg
[0]);
458 case 'F': /* Cursor <n> Up and first col */
459 DEFAULT(escseq
.arg
[0], 1);
460 tmoveto(0, term
.c
.y
-escseq
.arg
[0]);
462 case 'G': /* Move to <col> */
464 DEFAULT(escseq
.arg
[0], 1);
465 tmoveto(escseq
.arg
[0]-1, term
.c
.y
);
467 case 'H': /* Move to <row> <col> */
469 DEFAULT(escseq
.arg
[0], 1);
470 DEFAULT(escseq
.arg
[1], 1);
471 tmoveto(escseq
.arg
[1]-1, escseq
.arg
[0]-1);
473 case 'J': /* Clear screen */
474 switch(escseq
.arg
[0]) {
476 tclearregion(term
.c
.x
, term
.c
.y
, term
.col
-1, term
.row
-1);
479 tclearregion(0, 0, term
.c
.x
, term
.c
.y
);
482 tclearregion(0, 0, term
.col
-1, term
.row
-1);
486 case 'K': /* Clear line */
487 switch(escseq
.arg
[0]) {
489 tclearregion(term
.c
.x
, term
.c
.y
, term
.col
-1, term
.c
.y
);
492 tclearregion(0, term
.c
.y
, term
.c
.x
, term
.c
.y
);
495 tclearregion(0, term
.c
.y
, term
.col
-1, term
.c
.y
);
499 case 'L': /* Insert <n> blank lines */
500 DEFAULT(escseq
.arg
[0], 1);
501 tinsertblankline(escseq
.arg
[0]);
504 if(escseq
.priv
&& escseq
.arg
[0] == 25)
507 case 'M': /* Delete <n> lines */
508 DEFAULT(escseq
.arg
[0], 1);
509 tdeleteline(escseq
.arg
[0]);
511 case 'P': /* Delete <n> char */
512 DEFAULT(escseq
.arg
[0], 1);
513 tdeletechar(escseq
.arg
[0]);
515 case 'd': /* Move to <row> */
516 DEFAULT(escseq
.arg
[0], 1);
517 tmoveto(term
.c
.x
, escseq
.arg
[0]-1);
519 case 'h': /* Set terminal mode */
520 if(escseq
.priv
&& escseq
.arg
[0] == 25)
523 case 'm': /* Terminal attribute (color) */
524 tsetattr(escseq
.arg
, escseq
.narg
);
530 DEFAULT(escseq
.arg
[0], 1);
531 DEFAULT(escseq
.arg
[1], term
.row
);
532 tsetscroll(escseq
.arg
[0]-1, escseq
.arg
[1]-1);
535 case 's': /* Save cursor position */
538 case 'u': /* Load cursor position */
550 printf("rawbuf : %s\n", escseq
.buf
);
551 printf("prechar : %c\n", escseq
.pre
);
552 printf("private : %c\n", escseq
.priv
? '?' : ' ');
553 printf("narg : %d\n", escseq
.narg
);
555 for(i
= 0; i
< escseq
.narg
; i
++)
556 printf("\targ %d = %d\n", i
, escseq
.arg
[i
]);
558 printf("mode : %c\n", escseq
.mode
);
563 memset(&escseq
, 0, sizeof(escseq
));
568 int space
= TAB
- term
.c
.x
% TAB
;
570 if(term
.c
.x
+ space
>= term
.col
)
573 for(; space
> 0; space
--)
579 static int inesc
= 0;
583 /* start of escseq */
585 escreset(), inesc
= 1;
601 tmoveto(0, term
.c
.y
);
613 tputs(char *s
, int len
) {
614 for(; len
> 0; len
--)
623 for(row
= 0; row
< term
.row
; row
++) {
624 for(col
= 0; col
< term
.col
; col
++) {
625 if(col
== term
.c
.x
&& row
== term
.c
.y
)
628 c
= term
.line
[row
][col
];
629 putchar(c
.state
& CRset
? c
.c
: '.');
637 tresize(int col
, int row
) {
640 int minrow
= MIN(row
, term
.row
);
641 int mincol
= MIN(col
, term
.col
);
643 if(col
< 1 || row
< 1)
646 line
= calloc(row
, sizeof(Line
));
647 for(i
= 0 ; i
< row
; i
++)
648 line
[i
] = calloc(col
, sizeof(Glyph
));
650 for(i
= 0 ; i
< minrow
; i
++)
651 memcpy(line
[i
], term
.line
[i
], mincol
* sizeof(Glyph
));
653 for(i
= 0; i
< term
.row
; i
++)
657 LIMIT(term
.c
.x
, 0, col
-1);
658 LIMIT(term
.c
.y
, 0, row
-1);
659 LIMIT(term
.top
, 0, row
-1);
660 LIMIT(term
.bot
, 0, row
-1);
664 term
.col
= col
, term
.row
= row
;
668 xgetcol(const char *s
) {
670 Colormap cmap
= DefaultColormap(xw
.dis
, xw
.scr
);
672 if(!XAllocNamedColor(xw
.dis
, cmap
, s
, &color
, &color
)) {
673 color
.pixel
= WhitePixel(xw
.dis
, xw
.scr
);
674 fprintf(stderr
, "Could not allocate color '%s'\n", s
);
681 xclear(int x1
, int y1
, int x2
, int y2
) {
682 XClearArea(xw
.dis
, xw
.win
,
683 x1
* xw
.cw
, y1
* xw
.ch
,
684 (x2
-x1
+1) * xw
.cw
, (y2
-y1
+1) * xw
.ch
,
691 int srcy
= (term
.top
+1) * xw
.ch
;
692 int dsty
= term
.top
* xw
.ch
;
693 int height
= (term
.bot
-term
.top
) * xw
.ch
;
696 XCopyArea(xw
.dis
, xw
.win
, xw
.win
, dc
.gc
, 0, srcy
, xw
.w
, height
, 0, dsty
);
697 xclear(0, term
.bot
, term
.col
-1, term
.bot
);
706 unsigned long valuemask
;
710 char *args
[] = {NULL
};
713 xw
.dis
= XOpenDisplay(NULL
);
714 xw
.scr
= XDefaultScreen(xw
.dis
);
716 die("can not open display");
719 if(!(dc
.font
= XLoadQueryFont(xw
.dis
, FONT
)))
720 die("can not find font " FONT
);
722 xw
.cw
= dc
.font
->max_bounds
.rbearing
- dc
.font
->min_bounds
.lbearing
;
723 xw
.ch
= dc
.font
->ascent
+ dc
.font
->descent
+ LINESPACE
;
726 for(i
= 0; i
< LEN(colorname
); i
++)
727 dc
.col
[i
] = xgetcol(colorname
[i
]);
729 term
.c
.attr
.fg
= DefaultFG
;
730 term
.c
.attr
.bg
= DefaultBG
;
731 term
.c
.attr
.mode
= ATnone
;
733 xw
.h
= term
.row
* xw
.ch
;
734 xw
.w
= term
.col
* xw
.cw
;
735 /* XXX: this BORDER is useless after the first resize, handle it in xdraws() */
736 xw
.win
= XCreateSimpleWindow(xw
.dis
, XRootWindow(xw
.dis
, xw
.scr
), 0, 0,
741 values
.foreground
= XWhitePixel(xw
.dis
, xw
.scr
);
742 values
.font
= dc
.font
->fid
;
743 valuemask
= GCForeground
| GCFont
;
744 dc
.gc
= XCreateGC(xw
.dis
, xw
.win
, valuemask
, &values
);
745 XMapWindow(xw
.dis
, xw
.win
);
747 chint
.res_name
= TNAME
, chint
.res_class
= TNAME
;
748 wmhint
.input
= 1, wmhint
.flags
= InputHint
;
749 shint
.height_inc
= xw
.ch
, shint
.width_inc
= xw
.cw
;
750 shint
.height
= xw
.h
, shint
.width
= xw
.w
;
751 shint
.flags
= PSize
| PResizeInc
;
752 XSetWMProperties(xw
.dis
, xw
.win
, NULL
, NULL
, &args
[0], 0, &shint
, &wmhint
, &chint
);
753 XStoreName(xw
.dis
, xw
.win
, TNAME
);
758 xdrawc(int x
, int y
, Glyph g
) {
759 XRectangle r
= { x
* xw
.cw
, y
* xw
.ch
, xw
.cw
, xw
.ch
};
760 unsigned long xfg
, xbg
;
763 if(g
.mode
& ATreverse
)
764 xfg
= dc
.col
[g
.bg
], xbg
= dc
.col
[g
.fg
];
766 xfg
= dc
.col
[g
.fg
], xbg
= dc
.col
[g
.bg
];
768 XSetForeground(xw
.dis
, dc
.gc
, xbg
);
769 XFillRectangles(xw
.dis
, xw
.win
, dc
.gc
, &r
, 1);
771 XSetForeground(xw
.dis
, dc
.gc
, xfg
);
772 XDrawString(xw
.dis
, xw
.win
, dc
.gc
, r
.x
, r
.y
+dc
.font
->ascent
, &(g
.c
), 1);
773 if(g
.mode
& ATbold
) /* XXX: bold hack (draw again at x+1) */
774 XDrawString(xw
.dis
, xw
.win
, dc
.gc
, r
.x
+1, r
.y
+dc
.font
->ascent
, &(g
.c
), 1);
776 if(g
.mode
& ATunderline
) {
777 r
.y
+= dc
.font
->ascent
+ 1;
778 XDrawLine(xw
.dis
, xw
.win
, dc
.gc
, r
.x
, r
.y
, r
.x
+r
.width
-1, r
.y
);
786 Glyph g
= {' ', ATnone
, DefaultBG
, DefaultCS
, 0};
788 LIMIT(oldx
, 0, term
.col
-1);
789 LIMIT(oldy
, 0, term
.row
-1);
791 if(term
.line
[term
.c
.y
][term
.c
.x
].state
& CRset
)
792 g
.c
= term
.line
[term
.c
.y
][term
.c
.x
].c
;
793 /* remove the old cursor */
794 if(term
.line
[oldy
][oldx
].state
& CRset
)
795 xdrawc(oldx
, oldy
, term
.line
[oldy
][oldx
]);
796 else xclear(oldx
, oldy
, oldx
, oldy
); /* XXX: maybe a bug */
797 if(mode
== CSdraw
&& !term
.c
.hidden
) {
798 xdrawc(term
.c
.x
, term
.c
.y
, g
);
799 oldx
= term
.c
.x
, oldy
= term
.c
.y
;
805 draw(int redraw_all
) {
810 XClearWindow(xw
.dis
, xw
.win
);
811 /* XXX: drawing could be optimised */
812 for(y
= 0; y
< term
.row
; y
++) {
813 for(x
= 0; x
< term
.col
; x
++) {
814 changed
= term
.line
[y
][x
].state
& CRupdate
;
815 set
= term
.line
[y
][x
].state
& CRset
;
816 if((changed
&& set
) || (redraw_all
&& set
)) {
817 term
.line
[y
][x
].state
&= ~CRupdate
;
818 xdrawc(x
, y
, term
.line
[y
][x
]);
826 kpress(XKeyEvent
*e
) {
833 meta
= e
->state
& Mod1Mask
;
834 shift
= e
->state
& ShiftMask
;
835 len
= XLookupString(e
, buf
, sizeof(buf
), &ksym
, NULL
);
837 buf
[sizeof(buf
)-1] = '\0';
845 fprintf(stderr
, "errkey: %d\n", (int)ksym
);
851 sprintf(buf
, "\033[%c", "DACB"[ksym
- XK_Left
]);
854 case XK_Delete
: ttywrite(KEYDELETE
, sizeof(KEYDELETE
)-1); break;
855 case XK_Home
: ttywrite(KEYHOME
, sizeof(KEYHOME
)-1); break;
856 case XK_End
: ttywrite(KEYEND
, sizeof(KEYEND
) -1); break;
857 case XK_Prior
: ttywrite(KEYPREV
, sizeof(KEYPREV
)-1); break;
858 case XK_Next
: ttywrite(KEYNEXT
, sizeof(KEYNEXT
)-1); break;
860 /* XXX: paste X clipboard */
870 col
= e
->xconfigure
.width
/ xw
.cw
;
871 row
= e
->xconfigure
.height
/ xw
.ch
;
873 if(term
.col
!= col
|| term
.row
!= row
) {
876 xw
.w
= e
->xconfigure
.width
;
877 xw
.h
= e
->xconfigure
.height
;
888 int xfd
= XConnectionNumber(xw
.dis
);
891 XSelectInput(xw
.dis
, xw
.win
, ExposureMask
| KeyPressMask
| StructureNotifyMask
);
892 XResizeWindow(xw
.dis
, xw
.win
, xw
.w
, xw
.h
); /* seems to fix the resize bug in wmii */
899 ret
= select(MAX(xfd
, cmdfd
)+1, &rfd
, NULL
, NULL
, NULL
);
902 fprintf(stderr
, "select: %m\n");
906 if(FD_ISSET(xfd
, &rfd
)) {
907 while(XPending(xw
.dis
)) {
908 XNextEvent(xw
.dis
, &ev
);
918 case ConfigureNotify
:
924 if(FD_ISSET(cmdfd
, &rfd
)) {
932 main(int argc
, char *argv
[]) {
933 if(argc
== 2 && !strncmp("-v", argv
[1], 3))
934 die("st-" VERSION
", © 2009 st engineers\n");
936 die("usage: st [-v]\n");
937 setlocale(LC_CTYPE
, "");