1 /* See LICENSE for licence details. */ 
   2 #define _XOPEN_SOURCE 600 
  14 #include <sys/ioctl.h> 
  15 #include <sys/select.h> 
  18 #include <sys/types.h> 
  22 #include <X11/Xatom.h> 
  24 #include <X11/Xutil.h> 
  25 #include <X11/cursorfont.h> 
  26 #include <X11/keysym.h> 
  27 #include <X11/extensions/Xdbe.h> 
  28 #include <X11/Xft/Xft.h> 
  34 #elif defined(__OpenBSD__) || defined(__NetBSD__) || defined(__APPLE__) 
  36 #elif defined(__FreeBSD__) || defined(__DragonFly__) 
  41         "st " VERSION " (c) 2010-2012 st engineers\n" \ 
  42         "usage: st [-t title] [-c class] [-g geometry]" \ 
  43         " [-w windowid] [-v] [-f file] [-e command...]\n" 
  46 #define XEMBED_FOCUS_IN  4 
  47 #define XEMBED_FOCUS_OUT 5 
  50 #define ESC_BUF_SIZ   256 
  51 #define ESC_ARG_SIZ   16 
  52 #define STR_BUF_SIZ   256 
  53 #define STR_ARG_SIZ   16 
  54 #define DRAW_BUF_SIZ  20*1024 
  56 #define XK_NO_MOD     UINT_MAX 
  59 #define REDRAW_TIMEOUT (80*1000) /* 80 ms */ 
  61 #define SERRNO strerror(errno) 
  62 #define MIN(a, b)  ((a) < (b) ? (a) : (b)) 
  63 #define MAX(a, b)  ((a) < (b) ? (b) : (a)) 
  64 #define LEN(a)     (sizeof(a) / sizeof(a[0])) 
  65 #define DEFAULT(a, b)     (a) = (a) ? (a) : (b) 
  66 #define BETWEEN(x, a, b)  ((a) <= (x) && (x) <= (b)) 
  67 #define LIMIT(x, a, b)    (x) = (x) < (a) ? (a) : (x) > (b) ? (b) : (x) 
  68 #define ATTRCMP(a, b) ((a).mode != (b).mode || (a).fg != (b).fg || (a).bg != (b).bg) 
  69 #define IS_SET(flag) (term.mode & (flag)) 
  70 #define TIMEDIFF(t1, t2) ((t1.tv_sec-t2.tv_sec)*1000 + (t1.tv_usec-t2.tv_usec)/1000) 
  71 #define X2COL(x) (((x) - BORDER)/xw.cw) 
  72 #define Y2ROW(y) (((y) - BORDER)/xw.ch) 
  74 enum glyph_attribute 
{ 
  84 enum cursor_movement 
{ 
 111         MODE_MOUSEMOTION 
= 64, 
 120         ESC_STR 
= 4, /* DSC, OSC, PM, APC */ 
 122         ESC_STR_END    
= 16, /* a final string was encountered */ 
 133 enum { B0
=1, B1
=2, B2
=4, B3
=8, B4
=16, B5
=32, B6
=64, B7
=128 }; 
 135 typedef unsigned char uchar
; 
 136 typedef unsigned int uint
; 
 137 typedef unsigned long ulong
; 
 138 typedef unsigned short ushort
; 
 141         char c
[UTF_SIZ
];     /* character code */ 
 142         uchar mode
;  /* attribute flags */ 
 143         ushort fg
;   /* foreground  */ 
 144         ushort bg
;   /* background  */ 
 145         uchar state
; /* state flags    */ 
 151         Glyph attr
;      /* current char attributes */ 
 157 /* CSI Escape sequence structs */ 
 158 /* ESC '[' [[ [<priv>] <arg> [;]] <mode>] */ 
 160         char buf
[ESC_BUF_SIZ
]; /* raw string */ 
 161         int len
;               /* raw string length */ 
 163         int arg
[ESC_ARG_SIZ
]; 
 164         int narg
;             /* nb of args */ 
 168 /* STR Escape sequence structs */ 
 169 /* ESC type [[ [<priv>] <arg> [;]] <mode>] ESC '\' */ 
 171         char type
;           /* ESC type ... */ 
 172         char buf
[STR_BUF_SIZ
]; /* raw string */ 
 173         int len
;               /* raw string length */ 
 174         char *args
[STR_ARG_SIZ
]; 
 175         int narg
;             /* nb of args */ 
 178 /* Internal representation of the screen */ 
 180         int row
;        /* nb row */ 
 181         int col
;        /* nb col */ 
 182         Line
* line
;     /* screen */ 
 183         Line
* alt
;      /* alternate screen */ 
 184         bool* dirty
;    /* dirtyness of lines */ 
 185         TCursor c
;      /* cursor */ 
 186         int top
;        /* top    scroll limit */ 
 187         int bot
;        /* bottom scroll limit */ 
 188         int mode
;       /* terminal mode flags */ 
 189         int esc
;        /* escape state flags */ 
 193 /* Purely graphic info */ 
 205         bool isfixed
; /* is fixed geometry? */ 
 206         int fx
, fy
, fw
, fh
; /* fixed geometry */ 
 207         int tw
, th
; /* tty width and height */ 
 208         int w
;  /* window width */ 
 209         int h
;  /* window height */ 
 210         int ch
; /* char height */ 
 211         int cw
; /* char width  */ 
 212         char state
; /* focus, redraw, visible */ 
 221 /* TODO: use better name for vars... */ 
 226         struct {int x
, y
;} b
, e
; 
 230         struct timeval tclick1
; 
 231         struct timeval tclick2
; 
 246 /* Drawing Context */ 
 248         ulong col
[LEN(colorname
) < 256 ? 256 : LEN(colorname
)]; 
 249         XftColor xft_col
[LEN(colorname
) < 256 ? 256 : LEN(colorname
)]; 
 251         Font font
, bfont
, ifont
, ibfont
; 
 254 static void die(const char*, ...); 
 255 static void draw(void); 
 256 static void redraw(void); 
 257 static void drawregion(int, int, int, int); 
 258 static void execsh(void); 
 259 static void sigchld(int); 
 260 static void run(void); 
 262 static void csidump(void); 
 263 static void csihandle(void); 
 264 static void csiparse(void); 
 265 static void csireset(void); 
 266 static void strdump(void); 
 267 static void strhandle(void); 
 268 static void strparse(void); 
 269 static void strreset(void); 
 271 static void tclearregion(int, int, int, int); 
 272 static void tcursor(int); 
 273 static void tdeletechar(int); 
 274 static void tdeleteline(int); 
 275 static void tinsertblank(int); 
 276 static void tinsertblankline(int); 
 277 static void tmoveto(int, int); 
 278 static void tnew(int, int); 
 279 static void tnewline(int); 
 280 static void tputtab(bool); 
 281 static void tputc(char*, int); 
 282 static void treset(void); 
 283 static int tresize(int, int); 
 284 static void tscrollup(int, int); 
 285 static void tscrolldown(int, int); 
 286 static void tsetattr(int*, int); 
 287 static void tsetchar(char*); 
 288 static void tsetscroll(int, int); 
 289 static void tswapscreen(void); 
 290 static void tsetdirt(int, int); 
 291 static void tsetmode(bool, bool, int *, int); 
 292 static void tfulldirt(void); 
 294 static void ttynew(void); 
 295 static void ttyread(void); 
 296 static void ttyresize(void); 
 297 static void ttywrite(const char *, size_t); 
 299 static void xdraws(char *, Glyph
, int, int, int, int); 
 300 static void xhints(void); 
 301 static void xclear(int, int, int, int); 
 302 static void xdrawcursor(void); 
 303 static void xinit(void); 
 304 static void xloadcols(void); 
 305 static void xresettitle(void); 
 306 static void xseturgency(int); 
 307 static void xsetsel(char*); 
 308 static void xresize(int, int); 
 310 static void expose(XEvent 
*); 
 311 static void visibility(XEvent 
*); 
 312 static void unmap(XEvent 
*); 
 313 static char* kmap(KeySym
, uint
); 
 314 static void kpress(XEvent 
*); 
 315 static void cmessage(XEvent 
*); 
 316 static void resize(XEvent 
*); 
 317 static void focus(XEvent 
*); 
 318 static void brelease(XEvent 
*); 
 319 static void bpress(XEvent 
*); 
 320 static void bmotion(XEvent 
*); 
 321 static void selnotify(XEvent 
*); 
 322 static void selclear(XEvent 
*); 
 323 static void selrequest(XEvent 
*); 
 325 static void selinit(void); 
 326 static inline bool selected(int, int); 
 327 static void selcopy(void); 
 328 static void selpaste(void); 
 329 static void selscroll(int, int); 
 331 static int utf8decode(char *, long *); 
 332 static int utf8encode(long *, char *); 
 333 static int utf8size(char *); 
 334 static int isfullutf8(char *, int); 
 336 static void *xmalloc(size_t); 
 337 static void *xrealloc(void *, size_t); 
 338 static void *xcalloc(size_t nmemb
, size_t size
); 
 340 static void (*handler
[LASTEvent
])(XEvent 
*) = { 
 342         [ClientMessage
] = cmessage
, 
 343         [ConfigureNotify
] = resize
, 
 344         [VisibilityNotify
] = visibility
, 
 345         [UnmapNotify
] = unmap
, 
 349         [MotionNotify
] = bmotion
, 
 350         [ButtonPress
] = bpress
, 
 351         [ButtonRelease
] = brelease
, 
 352         [SelectionClear
] = selclear
, 
 353         [SelectionNotify
] = selnotify
, 
 354         [SelectionRequest
] = selrequest
, 
 361 static CSIEscape csiescseq
; 
 362 static STREscape strescseq
; 
 365 static Selection sel
; 
 366 static int iofd 
= -1; 
 367 static char **opt_cmd  
= NULL
; 
 368 static char *opt_io    
= NULL
; 
 369 static char *opt_title 
= NULL
; 
 370 static char *opt_embed 
= NULL
; 
 371 static char *opt_class 
= NULL
; 
 374 xmalloc(size_t len
) { 
 375         void *p 
= malloc(len
); 
 377                 die("Out of memory\n"); 
 382 xrealloc(void *p
, size_t len
) { 
 383         if((p 
= realloc(p
, len
)) == NULL
) 
 384                 die("Out of memory\n"); 
 389 xcalloc(size_t nmemb
, size_t size
) { 
 390         void *p 
= calloc(nmemb
, size
); 
 392                 die("Out of memory\n"); 
 397 utf8decode(char *s
, long *u
) { 
 403         if(~c 
& B7
) { /* 0xxxxxxx */ 
 406         } else if((c 
& (B7
|B6
|B5
)) == (B7
|B6
)) { /* 110xxxxx */ 
 407                 *u 
= c
&(B4
|B3
|B2
|B1
|B0
); 
 409         } else if((c 
& (B7
|B6
|B5
|B4
)) == (B7
|B6
|B5
)) { /* 1110xxxx */ 
 410                 *u 
= c
&(B3
|B2
|B1
|B0
); 
 412         } else if((c 
& (B7
|B6
|B5
|B4
|B3
)) == (B7
|B6
|B5
|B4
)) { /* 11110xxx */ 
 417         for(i 
= n
, ++s
; i 
> 0; --i
, ++rtn
, ++s
) { 
 419                 if((c 
& (B7
|B6
)) != B7
) /* 10xxxxxx */ 
 422                 *u 
|= c 
& (B5
|B4
|B3
|B2
|B1
|B0
); 
 424         if((n 
== 1 && *u 
< 0x80) || 
 425            (n 
== 2 && *u 
< 0x800) || 
 426            (n 
== 3 && *u 
< 0x10000) || 
 427            (*u 
>= 0xD800 && *u 
<= 0xDFFF)) 
 436 utf8encode(long *u
, char *s
) { 
 444                 *sp 
= uc
; /* 0xxxxxxx */ 
 446         } else if(*u 
< 0x800) { 
 447                 *sp 
= (uc 
>> 6) | (B7
|B6
); /* 110xxxxx */ 
 449         } else if(uc 
< 0x10000) { 
 450                 *sp 
= (uc 
>> 12) | (B7
|B6
|B5
); /* 1110xxxx */ 
 452         } else if(uc 
<= 0x10FFFF) { 
 453                 *sp 
= (uc 
>> 18) | (B7
|B6
|B5
|B4
); /* 11110xxx */ 
 458         for(i
=n
,++sp
; i
>0; --i
,++sp
) 
 459                 *sp 
= ((uc 
>> 6*(i
-1)) & (B5
|B4
|B3
|B2
|B1
|B0
)) | B7
; /* 10xxxxxx */ 
 469 /* use this if your buffer is less than UTF_SIZ, it returns 1 if you can decode 
 470    UTF-8 otherwise return 0 */ 
 472 isfullutf8(char *s
, int b
) { 
 480         else if((*c1
&(B7
|B6
|B5
)) == (B7
|B6
) && b 
== 1) 
 482         else if((*c1
&(B7
|B6
|B5
|B4
)) == (B7
|B6
|B5
) && 
 484             ((b 
== 2) && (*c2
&(B7
|B6
)) == B7
))) 
 486         else if((*c1
&(B7
|B6
|B5
|B4
|B3
)) == (B7
|B6
|B5
|B4
) && 
 488             ((b 
== 2) && (*c2
&(B7
|B6
)) == B7
) || 
 489             ((b 
== 3) && (*c2
&(B7
|B6
)) == B7 
&& (*c3
&(B7
|B6
)) == B7
))) 
 501         else if((c
&(B7
|B6
|B5
)) == (B7
|B6
)) 
 503         else if((c
&(B7
|B6
|B5
|B4
)) == (B7
|B6
|B5
)) 
 511         memset(&sel
.tclick1
, 0, sizeof(sel
.tclick1
)); 
 512         memset(&sel
.tclick2
, 0, sizeof(sel
.tclick2
)); 
 516         sel
.xtarget 
= XInternAtom(xw
.dpy
, "UTF8_STRING", 0); 
 517         if(sel
.xtarget 
== None
) 
 518                 sel
.xtarget 
= XA_STRING
; 
 522 selected(int x
, int y
) { 
 523         if(sel
.ey 
== y 
&& sel
.by 
== y
) { 
 524                 int bx 
= MIN(sel
.bx
, sel
.ex
); 
 525                 int ex 
= MAX(sel
.bx
, sel
.ex
); 
 526                 return BETWEEN(x
, bx
, ex
); 
 528         return ((sel
.b
.y 
< y
&&y 
< sel
.e
.y
) || (y
==sel
.e
.y 
&& x
<=sel
.e
.x
)) 
 529                 || (y
==sel
.b
.y 
&& x
>=sel
.b
.x 
&& (x
<=sel
.e
.x 
|| sel
.b
.y
!=sel
.e
.y
)); 
 533 getbuttoninfo(XEvent 
*e
, int *b
, int *x
, int *y
) { 
 535                 *b 
= e
->xbutton
.button
; 
 537         *x 
= X2COL(e
->xbutton
.x
); 
 538         *y 
= Y2ROW(e
->xbutton
.y
); 
 539         sel
.b
.x 
= sel
.by 
< sel
.ey 
? sel
.bx 
: sel
.ex
; 
 540         sel
.b
.y 
= MIN(sel
.by
, sel
.ey
); 
 541         sel
.e
.x 
= sel
.by 
< sel
.ey 
? sel
.ex 
: sel
.bx
; 
 542         sel
.e
.y 
= MAX(sel
.by
, sel
.ey
); 
 546 mousereport(XEvent 
*e
) { 
 547         int x 
= X2COL(e
->xbutton
.x
); 
 548         int y 
= Y2ROW(e
->xbutton
.y
); 
 549         int button 
= e
->xbutton
.button
; 
 550         int state 
= e
->xbutton
.state
; 
 551         char buf
[] = { '\033', '[', 'M', 0, 32+x
+1, 32+y
+1 }; 
 552         static int ob
, ox
, oy
; 
 555         if(e
->xbutton
.type 
== MotionNotify
) { 
 556                 if(!IS_SET(MODE_MOUSEMOTION
) || (x 
== ox 
&& y 
== oy
)) 
 560         } else if(e
->xbutton
.type 
== ButtonRelease 
|| button 
== AnyButton
) { 
 566                 if(e
->xbutton
.type 
== ButtonPress
) { 
 572         buf
[3] = 32 + button 
+ (state 
& ShiftMask 
? 4 : 0) 
 573                 + (state 
& Mod4Mask    
? 8  : 0) 
 574                 + (state 
& ControlMask 
? 16 : 0); 
 576         ttywrite(buf
, sizeof(buf
)); 
 581         if(IS_SET(MODE_MOUSE
)) 
 583         else if(e
->xbutton
.button 
== Button1
) { 
 586                         tsetdirt(sel
.b
.y
, sel
.e
.y
); 
 590                 sel
.ex 
= sel
.bx 
= X2COL(e
->xbutton
.x
); 
 591                 sel
.ey 
= sel
.by 
= Y2ROW(e
->xbutton
.y
); 
 598         int x
, y
, bufsize
, is_selected 
= 0; 
 604                 bufsize 
= (term
.col
+1) * (sel
.e
.y
-sel
.b
.y
+1) * UTF_SIZ
; 
 605                 ptr 
= str 
= xmalloc(bufsize
); 
 607                 /* append every set & selected glyph to the selection */ 
 608                 for(y 
= 0; y 
< term
.row
; y
++) { 
 609                         for(x 
= 0; x 
< term
.col
; x
++) { 
 612                                 Glyph 
*gp 
= &term
.line
[y
][x
]; 
 614                                 if(!(is_selected 
= selected(x
, y
))) 
 616                                 p 
= (gp
->state 
& GLYPH_SET
) ? gp
->c 
: " "; 
 618                                 memcpy(ptr
, p
, size
); 
 621                         /* \n at the end of every selected line except for the last one */ 
 622                         if(is_selected 
&& y 
< sel
.e
.y
) 
 627         sel
.alt 
= IS_SET(MODE_ALTSCREEN
); 
 632 selnotify(XEvent 
*e
) { 
 633         ulong nitems
, ofs
, rem
; 
 640                 if(XGetWindowProperty(xw
.dpy
, xw
.win
, XA_PRIMARY
, ofs
, BUFSIZ
/4, 
 641                                         False
, AnyPropertyType
, &type
, &format
, 
 642                                         &nitems
, &rem
, &data
)) { 
 643                         fprintf(stderr
, "Clipboard allocation failed\n"); 
 646                 ttywrite((const char *) data
, nitems 
* format 
/ 8); 
 648                 /* number of 32-bit chunks returned */ 
 649                 ofs 
+= nitems 
* format 
/ 32; 
 655         XConvertSelection(xw
.dpy
, XA_PRIMARY
, sel
.xtarget
, XA_PRIMARY
, xw
.win
, CurrentTime
); 
 658 void selclear(XEvent 
*e
) { 
 662         tsetdirt(sel
.b
.y
, sel
.e
.y
); 
 666 selrequest(XEvent 
*e
) { 
 667         XSelectionRequestEvent 
*xsre
; 
 671         xsre 
= (XSelectionRequestEvent 
*) e
; 
 672         xev
.type 
= SelectionNotify
; 
 673         xev
.requestor 
= xsre
->requestor
; 
 674         xev
.selection 
= xsre
->selection
; 
 675         xev
.target 
= xsre
->target
; 
 676         xev
.time 
= xsre
->time
; 
 680         xa_targets 
= XInternAtom(xw
.dpy
, "TARGETS", 0); 
 681         if(xsre
->target 
== xa_targets
) { 
 682                 /* respond with the supported type */ 
 683                 Atom string 
= sel
.xtarget
; 
 684                 XChangeProperty(xsre
->display
, xsre
->requestor
, xsre
->property
, 
 685                                 XA_ATOM
, 32, PropModeReplace
, 
 686                                 (uchar 
*) &string
, 1); 
 687                 xev
.property 
= xsre
->property
; 
 688         } else if(xsre
->target 
== sel
.xtarget 
&& sel
.clip 
!= NULL
) { 
 689                 XChangeProperty(xsre
->display
, xsre
->requestor
, xsre
->property
, 
 690                                 xsre
->target
, 8, PropModeReplace
, 
 691                                 (uchar 
*) sel
.clip
, strlen(sel
.clip
)); 
 692                 xev
.property 
= xsre
->property
; 
 695         /* all done, send a notification to the listener */ 
 696         if(!XSendEvent(xsre
->display
, xsre
->requestor
, True
, 0, (XEvent 
*) &xev
)) 
 697                 fprintf(stderr
, "Error sending SelectionNotify event\n"); 
 702         /* register the selection for both the clipboard and the primary */ 
 708         XSetSelectionOwner(xw
.dpy
, XA_PRIMARY
, xw
.win
, CurrentTime
); 
 710         clipboard 
= XInternAtom(xw
.dpy
, "CLIPBOARD", 0); 
 711         XSetSelectionOwner(xw
.dpy
, clipboard
, xw
.win
, CurrentTime
); 
 715 brelease(XEvent 
*e
) { 
 716         if(IS_SET(MODE_MOUSE
)) { 
 720         if(e
->xbutton
.button 
== Button2
) 
 722         else if(e
->xbutton
.button 
== Button1
) { 
 724                 getbuttoninfo(e
, NULL
, &sel
.ex
, &sel
.ey
); 
 725                 term
.dirty
[sel
.ey
] = 1; 
 726                 if(sel
.bx 
== sel
.ex 
&& sel
.by 
== sel
.ey
) { 
 729                         gettimeofday(&now
, NULL
); 
 731                         if(TIMEDIFF(now
, sel
.tclick2
) <= TRIPLECLICK_TIMEOUT
) { 
 732                                 /* triple click on the line */ 
 733                                 sel
.b
.x 
= sel
.bx 
= 0; 
 734                                 sel
.e
.x 
= sel
.ex 
= term
.col
; 
 735                                 sel
.b
.y 
= sel
.e
.y 
= sel
.ey
; 
 737                         } else if(TIMEDIFF(now
, sel
.tclick1
) <= DOUBLECLICK_TIMEOUT
) { 
 738                                 /* double click to select word */ 
 740                                 while(sel
.bx 
> 0 && term
.line
[sel
.ey
][sel
.bx
-1].state 
& GLYPH_SET 
&& 
 741                                           term
.line
[sel
.ey
][sel
.bx
-1].c
[0] != ' ') sel
.bx
--; 
 743                                 while(sel
.ex 
< term
.col
-1 && term
.line
[sel
.ey
][sel
.ex
+1].state 
& GLYPH_SET 
&& 
 744                                           term
.line
[sel
.ey
][sel
.ex
+1].c
[0] != ' ') sel
.ex
++; 
 746                                 sel
.b
.y 
= sel
.e
.y 
= sel
.ey
; 
 752         memcpy(&sel
.tclick2
, &sel
.tclick1
, sizeof(struct timeval
)); 
 753         gettimeofday(&sel
.tclick1
, NULL
); 
 758         if(IS_SET(MODE_MOUSE
)) { 
 763                 int oldey 
= sel
.ey
, oldex 
= sel
.ex
; 
 764                 getbuttoninfo(e
, NULL
, &sel
.ex
, &sel
.ey
); 
 766                 if(oldey 
!= sel
.ey 
|| oldex 
!= sel
.ex
) { 
 767                         int starty 
= MIN(oldey
, sel
.ey
); 
 768                         int endy 
= MAX(oldey
, sel
.ey
); 
 769                         tsetdirt(starty
, endy
); 
 775 die(const char *errstr
, ...) { 
 778         va_start(ap
, errstr
); 
 779         vfprintf(stderr
, errstr
, ap
); 
 787         char *envshell 
= getenv("SHELL"); 
 793         signal(SIGCHLD
, SIG_DFL
); 
 794         signal(SIGHUP
, SIG_DFL
); 
 795         signal(SIGINT
, SIG_DFL
); 
 796         signal(SIGQUIT
, SIG_DFL
); 
 797         signal(SIGTERM
, SIG_DFL
); 
 798         signal(SIGALRM
, SIG_DFL
); 
 800         DEFAULT(envshell
, SHELL
); 
 801         putenv("TERM="TNAME
); 
 802         args 
= opt_cmd 
? opt_cmd 
: (char*[]){envshell
, "-i", NULL
}; 
 803         execvp(args
[0], args
); 
 810         if(waitpid(pid
, &stat
, 0) < 0) 
 811                 die("Waiting for pid %hd failed: %s\n", pid
, SERRNO
); 
 813                 exit(WEXITSTATUS(stat
)); 
 822         /* seems to work fine on linux, openbsd and freebsd */ 
 823         struct winsize w 
= {term
.row
, term
.col
, 0, 0}; 
 824         if(openpty(&m
, &s
, NULL
, NULL
, &w
) < 0) 
 825                 die("openpty failed: %s\n", SERRNO
); 
 827         switch(pid 
= fork()) { 
 829                 die("fork failed\n"); 
 832                 setsid(); /* create a new process group */ 
 833                 dup2(s
, STDIN_FILENO
); 
 834                 dup2(s
, STDOUT_FILENO
); 
 835                 dup2(s
, STDERR_FILENO
); 
 836                 if(ioctl(s
, TIOCSCTTY
, NULL
) < 0) 
 837                         die("ioctl TIOCSCTTY failed: %s\n", SERRNO
); 
 845                 signal(SIGCHLD
, sigchld
); 
 847                         if(!strcmp(opt_io
, "-")) { 
 848                                 iofd 
= STDOUT_FILENO
; 
 850                                 if((iofd 
= open(opt_io
, O_WRONLY 
| O_CREAT
, 0666)) < 0) { 
 851                                         fprintf(stderr
, "Error opening %s:%s\n", 
 852                                                 opt_io
, strerror(errno
)); 
 862         fprintf(stderr
, " %02x '%c' ", c
, isprint(c
)?c
:'.'); 
 864                 fprintf(stderr
, "\n"); 
 869         static char buf
[BUFSIZ
]; 
 870         static int buflen 
= 0; 
 873         int charsize
; /* size of utf8 char in bytes */ 
 877         /* append read bytes to unprocessed bytes */ 
 878         if((ret 
= read(cmdfd
, buf
+buflen
, LEN(buf
)-buflen
)) < 0) 
 879                 die("Couldn't read from shell: %s\n", SERRNO
); 
 881         /* process every complete utf8 char */ 
 884         while(buflen 
>= UTF_SIZ 
|| isfullutf8(ptr
,buflen
)) { 
 885                 charsize 
= utf8decode(ptr
, &utf8c
); 
 886                 utf8encode(&utf8c
, s
); 
 892         /* keep any uncomplete utf8 char for the next call */ 
 893         memmove(buf
, ptr
, buflen
); 
 897 ttywrite(const char *s
, size_t n
) { 
 898         if(write(cmdfd
, s
, n
) == -1) 
 899                 die("write error on tty: %s\n", SERRNO
); 
 910         if(ioctl(cmdfd
, TIOCSWINSZ
, &w
) < 0) 
 911                 fprintf(stderr
, "Couldn't set window size: %s\n", SERRNO
); 
 915 tsetdirt(int top
, int bot
) 
 919         LIMIT(top
, 0, term
.row
-1); 
 920         LIMIT(bot
, 0, term
.row
-1); 
 922         for(i 
= top
; i 
<= bot
; i
++) 
 929         tsetdirt(0, term
.row
-1); 
 936         if(mode 
== CURSOR_SAVE
) 
 938         else if(mode 
== CURSOR_LOAD
) 
 939                 term
.c 
= c
, tmoveto(c
.x
, c
.y
); 
 949         }, .x 
= 0, .y 
= 0, .state 
= CURSOR_DEFAULT
}; 
 951         memset(term
.tabs
, 0, term
.col 
* sizeof(*term
.tabs
)); 
 952         for(i 
= TAB
; i 
< term
.col
; i 
+= TAB
) 
 954         term
.top 
= 0, term
.bot 
= term
.row 
- 1; 
 955         term
.mode 
= MODE_WRAP
; 
 957         tclearregion(0, 0, term
.col
-1, term
.row
-1); 
 961 tnew(int col
, int row
) { 
 962         /* set screen size */ 
 963         term
.row 
= row
, term
.col 
= col
; 
 964         term
.line 
= xmalloc(term
.row 
* sizeof(Line
)); 
 965         term
.alt  
= xmalloc(term
.row 
* sizeof(Line
)); 
 966         term
.dirty 
= xmalloc(term
.row 
* sizeof(*term
.dirty
)); 
 967         term
.tabs 
= xmalloc(term
.col 
* sizeof(*term
.tabs
)); 
 969         for(row 
= 0; row 
< term
.row
; row
++) { 
 970                 term
.line
[row
] = xmalloc(term
.col 
* sizeof(Glyph
)); 
 971                 term
.alt 
[row
] = xmalloc(term
.col 
* sizeof(Glyph
)); 
 974         memset(term
.tabs
, 0, term
.col 
* sizeof(*term
.tabs
)); 
 981         Line
* tmp 
= term
.line
; 
 982         term
.line 
= term
.alt
; 
 984         term
.mode 
^= MODE_ALTSCREEN
; 
 989 tscrolldown(int orig
, int n
) { 
 993         LIMIT(n
, 0, term
.bot
-orig
+1); 
 995         tclearregion(0, term
.bot
-n
+1, term
.col
-1, term
.bot
); 
 997         for(i 
= term
.bot
; i 
>= orig
+n
; i
--) { 
 999                 term
.line
[i
] = term
.line
[i
-n
]; 
1000                 term
.line
[i
-n
] = temp
; 
1003                 term
.dirty
[i
-n
] = 1; 
1010 tscrollup(int orig
, int n
) { 
1013         LIMIT(n
, 0, term
.bot
-orig
+1); 
1015         tclearregion(0, orig
, term
.col
-1, orig
+n
-1); 
1017         for(i 
= orig
; i 
<= term
.bot
-n
; i
++) { 
1018                  temp 
= term
.line
[i
]; 
1019                  term
.line
[i
] = term
.line
[i
+n
]; 
1020                  term
.line
[i
+n
] = temp
; 
1023                  term
.dirty
[i
+n
] = 1; 
1026         selscroll(orig
, -n
); 
1030 selscroll(int orig
, int n
) { 
1034         if(BETWEEN(sel
.by
, orig
, term
.bot
) || BETWEEN(sel
.ey
, orig
, term
.bot
)) { 
1035                 if((sel
.by 
+= n
) > term
.bot 
|| (sel
.ey 
+= n
) < term
.top
) { 
1039                 if(sel
.by 
< term
.top
) { 
1043                 if(sel
.ey 
> term
.bot
) { 
1047                 sel
.b
.y 
= sel
.by
, sel
.b
.x 
= sel
.bx
; 
1048                 sel
.e
.y 
= sel
.ey
, sel
.e
.x 
= sel
.ex
; 
1053 tnewline(int first_col
) { 
1056                 tscrollup(term
.top
, 1); 
1059         tmoveto(first_col 
? 0 : term
.c
.x
, y
); 
1064         /* int noarg = 1; */ 
1065         char *p 
= csiescseq
.buf
; 
1069                 csiescseq
.priv 
= 1, p
++; 
1071         while(p 
< csiescseq
.buf
+csiescseq
.len
) { 
1072                 while(isdigit(*p
)) { 
1073                         csiescseq
.arg
[csiescseq
.narg
] *= 10; 
1074                         csiescseq
.arg
[csiescseq
.narg
] += *p
++ - '0'/*, noarg = 0 */; 
1076                 if(*p 
== ';' && csiescseq
.narg
+1 < ESC_ARG_SIZ
) 
1077                         csiescseq
.narg
++, p
++; 
1079                         csiescseq
.mode 
= *p
; 
1087 tmoveto(int x
, int y
) { 
1088         LIMIT(x
, 0, term
.col
-1); 
1089         LIMIT(y
, 0, term
.row
-1); 
1090         term
.c
.state 
&= ~CURSOR_WRAPNEXT
; 
1097         term
.dirty
[term
.c
.y
] = 1; 
1098         term
.line
[term
.c
.y
][term
.c
.x
] = term
.c
.attr
; 
1099         memcpy(term
.line
[term
.c
.y
][term
.c
.x
].c
, c
, UTF_SIZ
); 
1100         term
.line
[term
.c
.y
][term
.c
.x
].state 
|= GLYPH_SET
; 
1104 tclearregion(int x1
, int y1
, int x2
, int y2
) { 
1108                 temp 
= x1
, x1 
= x2
, x2 
= temp
; 
1110                 temp 
= y1
, y1 
= y2
, y2 
= temp
; 
1112         LIMIT(x1
, 0, term
.col
-1); 
1113         LIMIT(x2
, 0, term
.col
-1); 
1114         LIMIT(y1
, 0, term
.row
-1); 
1115         LIMIT(y2
, 0, term
.row
-1); 
1117         for(y 
= y1
; y 
<= y2
; y
++) { 
1119                 for(x 
= x1
; x 
<= x2
; x
++) 
1120                         term
.line
[y
][x
].state 
= 0; 
1125 tdeletechar(int n
) { 
1126         int src 
= term
.c
.x 
+ n
; 
1128         int size 
= term
.col 
- src
; 
1130         term
.dirty
[term
.c
.y
] = 1; 
1132         if(src 
>= term
.col
) { 
1133                 tclearregion(term
.c
.x
, term
.c
.y
, term
.col
-1, term
.c
.y
); 
1136         memmove(&term
.line
[term
.c
.y
][dst
], &term
.line
[term
.c
.y
][src
], size 
* sizeof(Glyph
)); 
1137         tclearregion(term
.col
-n
, term
.c
.y
, term
.col
-1, term
.c
.y
); 
1141 tinsertblank(int n
) { 
1144         int size 
= term
.col 
- dst
; 
1146         term
.dirty
[term
.c
.y
] = 1; 
1148         if(dst 
>= term
.col
) { 
1149                 tclearregion(term
.c
.x
, term
.c
.y
, term
.col
-1, term
.c
.y
); 
1152         memmove(&term
.line
[term
.c
.y
][dst
], &term
.line
[term
.c
.y
][src
], size 
* sizeof(Glyph
)); 
1153         tclearregion(src
, term
.c
.y
, dst 
- 1, term
.c
.y
); 
1157 tinsertblankline(int n
) { 
1158         if(term
.c
.y 
< term
.top 
|| term
.c
.y 
> term
.bot
) 
1161         tscrolldown(term
.c
.y
, n
); 
1165 tdeleteline(int n
) { 
1166         if(term
.c
.y 
< term
.top 
|| term
.c
.y 
> term
.bot
) 
1169         tscrollup(term
.c
.y
, n
); 
1173 tsetattr(int *attr
, int l
) { 
1176         for(i 
= 0; i 
< l
; i
++) { 
1179                         term
.c
.attr
.mode 
&= ~(ATTR_REVERSE 
| ATTR_UNDERLINE 
| ATTR_BOLD \
 
1180                                         | ATTR_ITALIC 
| ATTR_BLINK 
| ATTR_GFX
); 
1181                         term
.c
.attr
.fg 
= DefaultFG
; 
1182                         term
.c
.attr
.bg 
= DefaultBG
; 
1185                         term
.c
.attr
.mode 
|= ATTR_BOLD
; 
1187                 case 3: /* enter standout (highlight) */ 
1188                         term
.c
.attr
.mode 
|= ATTR_ITALIC
; 
1191                         term
.c
.attr
.mode 
|= ATTR_UNDERLINE
; 
1194                         term
.c
.attr
.mode 
|= ATTR_BLINK
; 
1197                         term
.c
.attr
.mode 
|= ATTR_REVERSE
; 
1201                         term
.c
.attr
.mode 
&= ~ATTR_BOLD
; 
1203                 case 23: /* leave standout (highlight) mode */ 
1204                         term
.c
.attr
.mode 
&= ~ATTR_ITALIC
; 
1207                         term
.c
.attr
.mode 
&= ~ATTR_UNDERLINE
; 
1210                         term
.c
.attr
.mode 
&= ~ATTR_BLINK
; 
1213                         term
.c
.attr
.mode 
&= ~ATTR_REVERSE
; 
1216                         if(i 
+ 2 < l 
&& attr
[i 
+ 1] == 5) { 
1218                                 if(BETWEEN(attr
[i
], 0, 255)) 
1219                                         term
.c
.attr
.fg 
= attr
[i
]; 
1221                                         fprintf(stderr
, "erresc: bad fgcolor %d\n", attr
[i
]); 
1224                                 fprintf(stderr
, "erresc(38): gfx attr %d unknown\n", attr
[i
]); 
1227                         term
.c
.attr
.fg 
= DefaultFG
; 
1230                         if(i 
+ 2 < l 
&& attr
[i 
+ 1] == 5) { 
1232                                 if(BETWEEN(attr
[i
], 0, 255)) 
1233                                         term
.c
.attr
.bg 
= attr
[i
]; 
1235                                         fprintf(stderr
, "erresc: bad bgcolor %d\n", attr
[i
]); 
1238                                 fprintf(stderr
, "erresc(48): gfx attr %d unknown\n", attr
[i
]); 
1241                         term
.c
.attr
.bg 
= DefaultBG
; 
1244                         if(BETWEEN(attr
[i
], 30, 37)) 
1245                                 term
.c
.attr
.fg 
= attr
[i
] - 30; 
1246                         else if(BETWEEN(attr
[i
], 40, 47)) 
1247                                 term
.c
.attr
.bg 
= attr
[i
] - 40; 
1248                         else if(BETWEEN(attr
[i
], 90, 97)) 
1249                                 term
.c
.attr
.fg 
= attr
[i
] - 90 + 8; 
1250                         else if(BETWEEN(attr
[i
], 100, 107)) 
1251                                 term
.c
.attr
.bg 
= attr
[i
] - 100 + 8; 
1253                                 fprintf(stderr
, "erresc(default): gfx attr %d unknown\n", attr
[i
]), csidump(); 
1260 tsetscroll(int t
, int b
) { 
1263         LIMIT(t
, 0, term
.row
-1); 
1264         LIMIT(b
, 0, term
.row
-1); 
1274 #define MODBIT(x, set, bit) ((set) ? ((x) |= (bit)) : ((x) &= ~(bit))) 
1277 tsetmode(bool priv
, bool set
, int *args
, int narg
) { 
1280         for(lim 
= args 
+ narg
; args 
< lim
; ++args
) { 
1284                         case 1: /* DECCKM -- Cursor key */ 
1285                                 MODBIT(term
.mode
, set
, MODE_APPKEYPAD
); 
1287                         case 5: /* DECSCNM -- Reverve video */ 
1289                                 MODBIT(term
.mode
,set
, MODE_REVERSE
); 
1290                                 if(mode 
!= term
.mode
) 
1293                         case 6: /* XXX: DECOM -- Origin */ 
1295                         case 7: /* DECAWM -- Auto wrap */ 
1296                                 MODBIT(term
.mode
, set
, MODE_WRAP
); 
1298                         case 8: /* XXX: DECARM -- Auto repeat */ 
1300                         case 0:  /* Error (IGNORED) */ 
1301                         case 12: /* att610 -- Start blinking cursor (IGNORED) */ 
1304                                 MODBIT(term
.c
.state
, !set
, CURSOR_HIDE
); 
1306                         case 1000: /* 1000,1002: enable xterm mouse report */ 
1307                                 MODBIT(term
.mode
, set
, MODE_MOUSEBTN
); 
1310                                 MODBIT(term
.mode
, set
, MODE_MOUSEMOTION
); 
1312                         case 1049: /* = 1047 and 1048 */ 
1315                                 if(IS_SET(MODE_ALTSCREEN
)) 
1316                                         tclearregion(0, 0, term
.col
-1, term
.row
-1); 
1317                                 if((set 
&& !IS_SET(MODE_ALTSCREEN
)) || 
1318                                     (!set 
&& IS_SET(MODE_ALTSCREEN
))) { 
1325                                 tcursor((set
) ? CURSOR_SAVE 
: CURSOR_LOAD
); 
1328                         /* case 2:  DECANM -- ANSI/VT52 (NOT SUPPOURTED) */ 
1329                         /* case 3:  DECCOLM -- Column  (NOT SUPPORTED) */ 
1330                         /* case 4:  DECSCLM -- Scroll (NOT SUPPORTED) */ 
1331                         /* case 18: DECPFF -- Printer feed (NOT SUPPORTED) */ 
1332                         /* case 19: DECPEX -- Printer extent (NOT SUPPORTED) */ 
1333                         /* case 42: DECNRCM -- National characters (NOT SUPPORTED) */ 
1335                                         "erresc: unknown private set/reset mode %d\n", 
1341                         case 0:  /* Error (IGNORED) */ 
1343                         case 2:  /* KAM -- keyboard action */ 
1344                                 MODBIT(term
.mode
, set
, MODE_KBDLOCK
); 
1346                         case 4:  /* IRM -- Insertion-replacement */ 
1347                                 MODBIT(term
.mode
, set
, MODE_INSERT
); 
1349                         case 12: /* XXX: SRM -- Send/Receive */ 
1351                         case 20: /* LNM -- Linefeed/new line */ 
1352                                 MODBIT(term
.mode
, set
, MODE_CRLF
); 
1356                                         "erresc: unknown set/reset mode %d\n", 
1368         switch(csiescseq
.mode
) { 
1371                 fprintf(stderr
, "erresc: unknown csi "); 
1375         case '@': /* ICH -- Insert <n> blank char */ 
1376                 DEFAULT(csiescseq
.arg
[0], 1); 
1377                 tinsertblank(csiescseq
.arg
[0]); 
1379         case 'A': /* CUU -- Cursor <n> Up */ 
1381                 DEFAULT(csiescseq
.arg
[0], 1); 
1382                 tmoveto(term
.c
.x
, term
.c
.y
-csiescseq
.arg
[0]); 
1384         case 'B': /* CUD -- Cursor <n> Down */ 
1385                 DEFAULT(csiescseq
.arg
[0], 1); 
1386                 tmoveto(term
.c
.x
, term
.c
.y
+csiescseq
.arg
[0]); 
1388         case 'C': /* CUF -- Cursor <n> Forward */ 
1390                 DEFAULT(csiescseq
.arg
[0], 1); 
1391                 tmoveto(term
.c
.x
+csiescseq
.arg
[0], term
.c
.y
); 
1393         case 'D': /* CUB -- Cursor <n> Backward */ 
1394                 DEFAULT(csiescseq
.arg
[0], 1); 
1395                 tmoveto(term
.c
.x
-csiescseq
.arg
[0], term
.c
.y
); 
1397         case 'E': /* CNL -- Cursor <n> Down and first col */ 
1398                 DEFAULT(csiescseq
.arg
[0], 1); 
1399                 tmoveto(0, term
.c
.y
+csiescseq
.arg
[0]); 
1401         case 'F': /* CPL -- Cursor <n> Up and first col */ 
1402                 DEFAULT(csiescseq
.arg
[0], 1); 
1403                 tmoveto(0, term
.c
.y
-csiescseq
.arg
[0]); 
1405         case 'g': /* TBC -- Tabulation clear */ 
1406                 switch (csiescseq
.arg
[0]) { 
1407                 case 0: /* clear current tab stop */ 
1408                         term
.tabs
[term
.c
.x
] = 0; 
1410                 case 3: /* clear all the tabs */ 
1411                         memset(term
.tabs
, 0, term
.col 
* sizeof(*term
.tabs
)); 
1417         case 'G': /* CHA -- Move to <col> */ 
1419                 DEFAULT(csiescseq
.arg
[0], 1); 
1420                 tmoveto(csiescseq
.arg
[0]-1, term
.c
.y
); 
1422         case 'H': /* CUP -- Move to <row> <col> */ 
1424                 DEFAULT(csiescseq
.arg
[0], 1); 
1425                 DEFAULT(csiescseq
.arg
[1], 1); 
1426                 tmoveto(csiescseq
.arg
[1]-1, csiescseq
.arg
[0]-1); 
1428         case 'I': /* CHT -- Cursor Forward Tabulation <n> tab stops */ 
1429                 DEFAULT(csiescseq
.arg
[0], 1); 
1430                 while(csiescseq
.arg
[0]--) 
1433         case 'J': /* ED -- Clear screen */ 
1435                 switch(csiescseq
.arg
[0]) { 
1437                         tclearregion(term
.c
.x
, term
.c
.y
, term
.col
-1, term
.c
.y
); 
1438                         if(term
.c
.y 
< term
.row
-1) 
1439                                 tclearregion(0, term
.c
.y
+1, term
.col
-1, term
.row
-1); 
1443                                 tclearregion(0, 0, term
.col
-1, term
.c
.y
-1); 
1444                         tclearregion(0, term
.c
.y
, term
.c
.x
, term
.c
.y
); 
1447                         tclearregion(0, 0, term
.col
-1, term
.row
-1); 
1453         case 'K': /* EL -- Clear line */ 
1454                 switch(csiescseq
.arg
[0]) { 
1456                         tclearregion(term
.c
.x
, term
.c
.y
, term
.col
-1, term
.c
.y
); 
1459                         tclearregion(0, term
.c
.y
, term
.c
.x
, term
.c
.y
); 
1462                         tclearregion(0, term
.c
.y
, term
.col
-1, term
.c
.y
); 
1466         case 'S': /* SU -- Scroll <n> line up */ 
1467                 DEFAULT(csiescseq
.arg
[0], 1); 
1468                 tscrollup(term
.top
, csiescseq
.arg
[0]); 
1470         case 'T': /* SD -- Scroll <n> line down */ 
1471                 DEFAULT(csiescseq
.arg
[0], 1); 
1472                 tscrolldown(term
.top
, csiescseq
.arg
[0]); 
1474         case 'L': /* IL -- Insert <n> blank lines */ 
1475                 DEFAULT(csiescseq
.arg
[0], 1); 
1476                 tinsertblankline(csiescseq
.arg
[0]); 
1478         case 'l': /* RM -- Reset Mode */ 
1479                 tsetmode(csiescseq
.priv
, 0, csiescseq
.arg
, csiescseq
.narg
); 
1481         case 'M': /* DL -- Delete <n> lines */ 
1482                 DEFAULT(csiescseq
.arg
[0], 1); 
1483                 tdeleteline(csiescseq
.arg
[0]); 
1485         case 'X': /* ECH -- Erase <n> char */ 
1486                 DEFAULT(csiescseq
.arg
[0], 1); 
1487                 tclearregion(term
.c
.x
, term
.c
.y
, term
.c
.x 
+ csiescseq
.arg
[0], term
.c
.y
); 
1489         case 'P': /* DCH -- Delete <n> char */ 
1490                 DEFAULT(csiescseq
.arg
[0], 1); 
1491                 tdeletechar(csiescseq
.arg
[0]); 
1493         case 'Z': /* CBT -- Cursor Backward Tabulation <n> tab stops */ 
1494                 DEFAULT(csiescseq
.arg
[0], 1); 
1495                 while(csiescseq
.arg
[0]--) 
1498         case 'd': /* VPA -- Move to <row> */ 
1499                 DEFAULT(csiescseq
.arg
[0], 1); 
1500                 tmoveto(term
.c
.x
, csiescseq
.arg
[0]-1); 
1502         case 'h': /* SM -- Set terminal mode */ 
1503                 tsetmode(csiescseq
.priv
, 1, csiescseq
.arg
, csiescseq
.narg
); 
1505         case 'm': /* SGR -- Terminal attribute (color) */ 
1506                 tsetattr(csiescseq
.arg
, csiescseq
.narg
); 
1508         case 'r': /* DECSTBM -- Set Scrolling Region */ 
1512                         DEFAULT(csiescseq
.arg
[0], 1); 
1513                         DEFAULT(csiescseq
.arg
[1], term
.row
); 
1514                         tsetscroll(csiescseq
.arg
[0]-1, csiescseq
.arg
[1]-1); 
1518         case 's': /* DECSC -- Save cursor position (ANSI.SYS) */ 
1519                 tcursor(CURSOR_SAVE
); 
1521         case 'u': /* DECRC -- Restore cursor position (ANSI.SYS) */ 
1522                 tcursor(CURSOR_LOAD
); 
1531         for(i 
= 0; i 
< csiescseq
.len
; i
++) { 
1532                 uint c 
= csiescseq
.buf
[i
] & 0xff; 
1533                 if(isprint(c
)) putchar(c
); 
1534                 else if(c 
== '\n') printf("(\\n)"); 
1535                 else if(c 
== '\r') printf("(\\r)"); 
1536                 else if(c 
== 0x1b) printf("(\\e)"); 
1537                 else printf("(%02x)", c
); 
1544         memset(&csiescseq
, 0, sizeof(csiescseq
)); 
1552          * TODO: make this being useful in case of color palette change. 
1558         switch(strescseq
.type
) { 
1559         case ']': /* OSC -- Operating System Command */ 
1565                          * TODO: Handle special chars in string, like umlauts. 
1568                                 XStoreName(xw
.dpy
, xw
.win
, strescseq
.buf
+2); 
1572                         XStoreName(xw
.dpy
, xw
.win
, strescseq
.buf
+1); 
1574                 case '4': /* TODO: Set color (arg0) to "rgb:%hexr/$hexg/$hexb" (arg1) */ 
1577                         fprintf(stderr
, "erresc: unknown str "); 
1582         case 'k': /* old title set compatibility */ 
1583                 XStoreName(xw
.dpy
, xw
.win
, strescseq
.buf
); 
1585         case 'P': /* DSC -- Device Control String */ 
1586         case '_': /* APC -- Application Program Command */ 
1587         case '^': /* PM -- Privacy Message */ 
1589                 fprintf(stderr
, "erresc: unknown str "); 
1599          * TODO: Implement parsing like for CSI when required. 
1600          * Format: ESC type cmd ';' arg0 [';' argn] ESC \ 
1608         printf("ESC%c", strescseq
.type
); 
1609         for(i 
= 0; i 
< strescseq
.len
; i
++) { 
1610                 uint c 
= strescseq
.buf
[i
] & 0xff; 
1611                 if(isprint(c
)) putchar(c
); 
1612                 else if(c 
== '\n') printf("(\\n)"); 
1613                 else if(c 
== '\r') printf("(\\r)"); 
1614                 else if(c 
== 0x1b) printf("(\\e)"); 
1615                 else printf("(%02x)", c
); 
1622         memset(&strescseq
, 0, sizeof(strescseq
)); 
1626 tputtab(bool forward
) { 
1632                 for(++x
; x 
< term
.col 
&& !term
.tabs
[x
]; ++x
) 
1637                 for(--x
; x 
> 0 && !term
.tabs
[x
]; --x
) 
1640         tmoveto(x
, term
.c
.y
); 
1644 tputc(char *c
, int len
) { 
1648                 write(iofd
, c
, len
); 
1650         if(term
.esc 
& ESC_START
) { 
1651                 if(term
.esc 
& ESC_CSI
) { 
1652                         csiescseq
.buf
[csiescseq
.len
++] = ascii
; 
1653                         if(BETWEEN(ascii
, 0x40, 0x7E) || csiescseq
.len 
>= ESC_BUF_SIZ
) { 
1655                                 csiparse(), csihandle(); 
1657                 } else if(term
.esc 
& ESC_STR
) { 
1660                                 term
.esc 
= ESC_START 
| ESC_STR_END
; 
1662                         case '\a': /* backwards compatibility to xterm */ 
1667                                 strescseq
.buf
[strescseq
.len
++] = ascii
; 
1668                                 if(strescseq
.len
+1 >= STR_BUF_SIZ
) { 
1673                 } else if(term
.esc 
& ESC_STR_END
) { 
1677                 } else if(term
.esc 
& ESC_ALTCHARSET
) { 
1679                         case '0': /* Line drawing crap */ 
1680                                 term
.c
.attr
.mode 
|= ATTR_GFX
; 
1682                         case 'B': /* Back to regular text */ 
1683                                 term
.c
.attr
.mode 
&= ~ATTR_GFX
; 
1686                                 fprintf(stderr
, "esc unhandled charset: ESC ( %c\n", ascii
); 
1692                                 term
.esc 
|= ESC_CSI
; 
1694                         case 'P': /* DCS -- Device Control String */ 
1695                         case '_': /* APC -- Application Program Command */ 
1696                         case '^': /* PM -- Privacy Message */ 
1697                         case ']': /* OSC -- Operating System Command */ 
1698                         case 'k': /* old title set compatibility */ 
1700                                 strescseq
.type 
= ascii
; 
1701                                 term
.esc 
|= ESC_STR
; 
1705                                 term
.esc 
|= ESC_ALTCHARSET
; 
1707                         case 'D': /* IND -- Linefeed */ 
1708                                 if(term
.c
.y 
== term
.bot
) 
1709                                         tscrollup(term
.top
, 1); 
1711                                         tmoveto(term
.c
.x
, term
.c
.y
+1); 
1714                         case 'E': /* NEL -- Next line */ 
1715                                 tnewline(1); /* always go to first col */ 
1718                         case 'H': /* HTS -- Horizontal tab stop */ 
1719                                 term
.tabs
[term
.c
.x
] = 1; 
1722                         case 'M': /* RI -- Reverse index */ 
1723                                 if(term
.c
.y 
== term
.top
) 
1724                                         tscrolldown(term
.top
, 1); 
1726                                         tmoveto(term
.c
.x
, term
.c
.y
-1); 
1729                         case 'c': /* RIS -- Reset to inital state */ 
1732                                 xclear(0, 0, xw
.w
, xw
.h
); 
1735                         case '=': /* DECPAM -- Application keypad */ 
1736                                 term
.mode 
|= MODE_APPKEYPAD
; 
1739                         case '>': /* DECPNM -- Normal keypad */ 
1740                                 term
.mode 
&= ~MODE_APPKEYPAD
; 
1743                         case '7': /* DECSC -- Save Cursor */ 
1744                                 tcursor(CURSOR_SAVE
); 
1747                         case '8': /* DECRC -- Restore Cursor */ 
1748                                 tcursor(CURSOR_LOAD
); 
1751                         case '\\': /* ST -- Stop */ 
1755                                 fprintf(stderr
, "erresc: unknown sequence ESC 0x%02X '%c'\n", 
1756                                     (uchar
) ascii
, isprint(ascii
)?ascii
:'.'); 
1761                 if(sel
.bx 
!= -1 && BETWEEN(term
.c
.y
, sel
.by
, sel
.ey
)) 
1764                 case '\0': /* padding character, do nothing */ 
1770                         tmoveto(term
.c
.x
-1, term
.c
.y
); 
1773                         tmoveto(0, term
.c
.y
); 
1778                         /* go to first col if the mode is set */ 
1779                         tnewline(IS_SET(MODE_CRLF
)); 
1782                         if(!(xw
.state 
& WIN_FOCUSED
)) 
1787                         term
.esc 
= ESC_START
; 
1790                         if(IS_SET(MODE_WRAP
) && term
.c
.state 
& CURSOR_WRAPNEXT
) 
1791                                 tnewline(1); /* always go to first col */ 
1793                         if(term
.c
.x
+1 < term
.col
) 
1794                                 tmoveto(term
.c
.x
+1, term
.c
.y
); 
1796                                 term
.c
.state 
|= CURSOR_WRAPNEXT
; 
1802 tresize(int col
, int row
) { 
1804         int minrow 
= MIN(row
, term
.row
); 
1805         int mincol 
= MIN(col
, term
.col
); 
1806         int slide 
= term
.c
.y 
- row 
+ 1; 
1808         if(col 
< 1 || row 
< 1) 
1811         /* free unneeded rows */ 
1814                 /* slide screen to keep cursor where we expect it - 
1815                  * tscrollup would work here, but we can optimize to 
1816                  * memmove because we're freeing the earlier lines */ 
1817                 for(/* i = 0 */; i 
< slide
; i
++) { 
1821                 memmove(term
.line
, term
.line 
+ slide
, row 
* sizeof(Line
)); 
1822                 memmove(term
.alt
, term
.alt 
+ slide
, row 
* sizeof(Line
)); 
1824         for(i 
+= row
; i 
< term
.row
; i
++) { 
1829         /* resize to new height */ 
1830         term
.line 
= xrealloc(term
.line
, row 
* sizeof(Line
)); 
1831         term
.alt  
= xrealloc(term
.alt
,  row 
* sizeof(Line
)); 
1832         term
.dirty 
= xrealloc(term
.dirty
, row 
* sizeof(*term
.dirty
)); 
1833         term
.tabs 
= xrealloc(term
.tabs
, col 
* sizeof(*term
.tabs
)); 
1835         /* resize each row to new width, zero-pad if needed */ 
1836         for(i 
= 0; i 
< minrow
; i
++) { 
1838                 term
.line
[i
] = xrealloc(term
.line
[i
], col 
* sizeof(Glyph
)); 
1839                 term
.alt
[i
]  = xrealloc(term
.alt
[i
],  col 
* sizeof(Glyph
)); 
1840                 for(x 
= mincol
; x 
< col
; x
++) { 
1841                         term
.line
[i
][x
].state 
= 0; 
1842                         term
.alt
[i
][x
].state 
= 0; 
1846         /* allocate any new rows */ 
1847         for(/* i == minrow */; i 
< row
; i
++) { 
1849                 term
.line
[i
] = xcalloc(col
, sizeof(Glyph
)); 
1850                 term
.alt 
[i
] = xcalloc(col
, sizeof(Glyph
)); 
1852         if(col 
> term
.col
) { 
1853                 bool *bp 
= term
.tabs 
+ term
.col
; 
1855                 memset(bp
, 0, sizeof(*term
.tabs
) * (col 
- term
.col
)); 
1856                 while(--bp 
> term
.tabs 
&& !*bp
) 
1858                 for(bp 
+= TAB
; bp 
< term
.tabs 
+ col
; bp 
+= TAB
) 
1861         /* update terminal size */ 
1862         term
.col 
= col
, term
.row 
= row
; 
1863         /* make use of the LIMIT in tmoveto */ 
1864         tmoveto(term
.c
.x
, term
.c
.y
); 
1865         /* reset scrolling region */ 
1866         tsetscroll(0, row
-1); 
1872 xresize(int col
, int row
) { 
1873         xw
.tw 
= MAX(1, 2*BORDER 
+ col 
* xw
.cw
); 
1874         xw
.th 
= MAX(1, 2*BORDER 
+ row 
* xw
.ch
); 
1876         XftDrawChange(xw
.xft_draw
, xw
.buf
); 
1882         XRenderColor xft_color 
= { .alpha 
= 0 }; 
1883         ulong white 
= WhitePixel(xw
.dpy
, xw
.scr
); 
1885         /* load colors [0-15] colors and [256-LEN(colorname)[ (config.h) */ 
1886         for(i 
= 0; i 
< LEN(colorname
); i
++) { 
1889                 if(!XftColorAllocName(xw
.dpy
, xw
.vis
, xw
.cmap
, colorname
[i
], &dc
.xft_col
[i
])) { 
1891                         fprintf(stderr
, "Could not allocate color '%s'\n", colorname
[i
]); 
1893                         dc
.col
[i
] = dc
.xft_col
[i
].pixel
; 
1896         /* load colors [16-255] ; same colors as xterm */ 
1897         for(i 
= 16, r 
= 0; r 
< 6; r
++) 
1898                 for(g 
= 0; g 
< 6; g
++) 
1899                         for(b 
= 0; b 
< 6; b
++) { 
1900                                 xft_color
.red 
= r 
== 0 ? 0 : 0x3737 + 0x2828 * r
; 
1901                                 xft_color
.green 
= g 
== 0 ? 0 : 0x3737 + 0x2828 * g
; 
1902                                 xft_color
.blue 
= b 
== 0 ? 0 : 0x3737 + 0x2828 * b
; 
1903                                 if(!XftColorAllocValue(xw
.dpy
, xw
.vis
, xw
.cmap
, &xft_color
, &dc
.xft_col
[i
])) { 
1905                                         fprintf(stderr
, "Could not allocate color %d\n", i
); 
1907                                         dc
.col
[i
] = dc
.xft_col
[i
].pixel
; 
1911         for(r 
= 0; r 
< 24; r
++, i
++) { 
1912                 xft_color
.red 
= xft_color
.green 
= xft_color
.blue 
= 0x0808 + 0x0a0a * r
; 
1913                 if(!XftColorAllocValue(xw
.dpy
, xw
.vis
, xw
.cmap
, &xft_color
, &dc
.xft_col
[i
])) { 
1915                         fprintf(stderr
, "Could not allocate color %d\n", i
); 
1917                         dc
.col
[i
] = dc
.xft_col
[i
].pixel
; 
1922 xclear(int x1
, int y1
, int x2
, int y2
) { 
1923         XSetForeground(xw
.dpy
, dc
.gc
, dc
.col
[IS_SET(MODE_REVERSE
) ? DefaultFG 
: DefaultBG
]); 
1924         XFillRectangle(xw
.dpy
, xw
.buf
, dc
.gc
, 
1925                        BORDER 
+ x1 
* xw
.cw
, BORDER 
+ y1 
* xw
.ch
, 
1926                        (x2
-x1
+1) * xw
.cw
, (y2
-y1
+1) * xw
.ch
); 
1931         XClassHint 
class = {opt_class 
? opt_class 
: TNAME
, TNAME
}; 
1932         XWMHints wm 
= {.flags 
= InputHint
, .input 
= 1}; 
1933         XSizeHints 
*sizeh 
= NULL
; 
1935         sizeh 
= XAllocSizeHints(); 
1936         if(xw
.isfixed 
== False
) { 
1937                 sizeh
->flags 
= PSize 
| PResizeInc 
| PBaseSize
; 
1938                 sizeh
->height 
= xw
.h
; 
1939                 sizeh
->width 
= xw
.w
; 
1940                 sizeh
->height_inc 
= xw
.ch
; 
1941                 sizeh
->width_inc 
= xw
.cw
; 
1942                 sizeh
->base_height 
= 2*BORDER
; 
1943                 sizeh
->base_width 
= 2*BORDER
; 
1945                 sizeh
->flags 
= PMaxSize 
| PMinSize
; 
1946                 sizeh
->min_width 
= sizeh
->max_width 
= xw
.fw
; 
1947                 sizeh
->min_height 
= sizeh
->max_height 
= xw
.fh
; 
1950         XSetWMProperties(xw
.dpy
, xw
.win
, NULL
, NULL
, NULL
, 0, sizeh
, &wm
, &class); 
1955 xinitfont(Font 
*f
, char *fontstr
) { 
1956         f
->xft_set 
= XftFontOpenName(xw
.dpy
, xw
.scr
, fontstr
); 
1959                 die("st: can't open font %s.\n", fontstr
); 
1961         f
->ascent 
= f
->xft_set
->ascent
; 
1962         f
->descent 
= f
->xft_set
->descent
; 
1964         f
->rbearing 
= f
->xft_set
->max_advance_width
; 
1968 initfonts(char *fontstr
, char *bfontstr
, char *ifontstr
, char *ibfontstr
) { 
1969         xinitfont(&dc
.font
, fontstr
); 
1970         xinitfont(&dc
.bfont
, bfontstr
); 
1971         xinitfont(&dc
.ifont
, ifontstr
); 
1972         xinitfont(&dc
.ibfont
, ibfontstr
); 
1977         XSetWindowAttributes attrs
; 
1980         int sw
, sh
, major
, minor
; 
1982         if(!(xw
.dpy 
= XOpenDisplay(NULL
))) 
1983                 die("Can't open display\n"); 
1984         xw
.scr 
= XDefaultScreen(xw
.dpy
); 
1985         xw
.vis 
= XDefaultVisual(xw
.dpy
, xw
.scr
); 
1988         initfonts(FONT
, BOLDFONT
, ITALICFONT
, ITALICBOLDFONT
); 
1990         /* XXX: Assuming same size for bold font */ 
1991         xw
.cw 
= dc
.font
.rbearing 
- dc
.font
.lbearing
; 
1992         xw
.ch 
= dc
.font
.ascent 
+ dc
.font
.descent
; 
1995         xw
.cmap 
= XDefaultColormap(xw
.dpy
, xw
.scr
); 
1998         /* adjust fixed window geometry */ 
2000                 sw 
= DisplayWidth(xw
.dpy
, xw
.scr
); 
2001                 sh 
= DisplayHeight(xw
.dpy
, xw
.scr
); 
2003                         xw
.fx 
= sw 
+ xw
.fx 
- xw
.fw 
- 1; 
2005                         xw
.fy 
= sh 
+ xw
.fy 
- xw
.fh 
- 1; 
2010                 /* window - default size */ 
2011                 xw
.h 
= 2*BORDER 
+ term
.row 
* xw
.ch
; 
2012                 xw
.w 
= 2*BORDER 
+ term
.col 
* xw
.cw
; 
2017         attrs
.background_pixel 
= dc
.col
[DefaultBG
]; 
2018         attrs
.border_pixel 
= dc
.col
[DefaultBG
]; 
2019         attrs
.bit_gravity 
= NorthWestGravity
; 
2020         attrs
.event_mask 
= FocusChangeMask 
| KeyPressMask
 
2021                 | ExposureMask 
| VisibilityChangeMask 
| StructureNotifyMask
 
2022                 | ButtonMotionMask 
| ButtonPressMask 
| ButtonReleaseMask
; 
2023         attrs
.colormap 
= xw
.cmap
; 
2025         parent 
= opt_embed 
? strtol(opt_embed
, NULL
, 0) : XRootWindow(xw
.dpy
, xw
.scr
); 
2026         xw
.win 
= XCreateWindow(xw
.dpy
, parent
, xw
.fx
, xw
.fy
, 
2027                         xw
.w
, xw
.h
, 0, XDefaultDepth(xw
.dpy
, xw
.scr
), InputOutput
, 
2029                         CWBackPixel 
| CWBorderPixel 
| CWBitGravity 
| CWEventMask
 
2033         /* double buffering */ 
2034         if(!XdbeQueryExtension(xw
.dpy
, &major
, &minor
)) 
2035                 die("Xdbe extension is not present\n"); 
2036         xw
.buf 
= XdbeAllocateBackBufferName(xw
.dpy
, xw
.win
, XdbeCopied
); 
2038         /* Xft rendering context */ 
2039         xw
.xft_draw 
= XftDrawCreate(xw
.dpy
, xw
.buf
, xw
.vis
, xw
.cmap
); 
2042         xw
.xim 
= XOpenIM(xw
.dpy
, NULL
, NULL
, NULL
); 
2043         xw
.xic 
= XCreateIC(xw
.xim
, XNInputStyle
, XIMPreeditNothing
 
2044                                            | XIMStatusNothing
, XNClientWindow
, xw
.win
, 
2045                                            XNFocusWindow
, xw
.win
, NULL
); 
2047         dc
.gc 
= XCreateGC(xw
.dpy
, xw
.win
, 0, NULL
); 
2049         /* white cursor, black outline */ 
2050         cursor 
= XCreateFontCursor(xw
.dpy
, XC_xterm
); 
2051         XDefineCursor(xw
.dpy
, xw
.win
, cursor
); 
2052         XRecolorCursor(xw
.dpy
, cursor
, 
2053                 &(XColor
){.red 
= 0xffff, .green 
= 0xffff, .blue 
= 0xffff}, 
2054                 &(XColor
){.red 
= 0x0000, .green 
= 0x0000, .blue 
= 0x0000}); 
2056         xw
.xembed 
= XInternAtom(xw
.dpy
, "_XEMBED", False
); 
2059         XMapWindow(xw
.dpy
, xw
.win
); 
2065 xdraws(char *s
, Glyph base
, int x
, int y
, int charlen
, int bytelen
) { 
2066         int fg 
= base
.fg
, bg 
= base
.bg
, temp
; 
2067         int winx 
= BORDER
+x
*xw
.cw
, winy 
= BORDER
+y
*xw
.ch 
+ dc
.font
.ascent
, width 
= charlen
*xw
.cw
; 
2068         Font 
*font 
= &dc
.font
; 
2072         /* only switch default fg/bg if term is in RV mode */ 
2073         if(IS_SET(MODE_REVERSE
)) { 
2080         if(base
.mode 
& ATTR_REVERSE
) 
2081                 temp 
= fg
, fg 
= bg
, bg 
= temp
; 
2083         if(base
.mode 
& ATTR_BOLD
) { 
2088         if(base
.mode 
& ATTR_ITALIC
) 
2090         if(base
.mode 
& (ATTR_ITALIC
|ATTR_ITALIC
)) 
2093         XSetBackground(xw
.dpy
, dc
.gc
, dc
.col
[bg
]); 
2094         XSetForeground(xw
.dpy
, dc
.gc
, dc
.col
[fg
]); 
2096         if(base
.mode 
& ATTR_GFX
) { 
2097                 for(i 
= 0; i 
< bytelen
; i
++) { 
2098                         char c 
= gfx
[(uint
)s
[i
] % 256]; 
2101                         else if(s
[i
] > 0x5f) 
2106         XftTextExtentsUtf8(xw
.dpy
, font
->xft_set
, (FcChar8 
*)s
, bytelen
, &extents
); 
2107         width 
= extents
.xOff
; 
2108         XftDrawRect(xw
.xft_draw
, &dc
.xft_col
[bg
], winx
, winy 
- font
->ascent
, width
, xw
.ch
); 
2109         XftDrawStringUtf8(xw
.xft_draw
, &dc
.xft_col
[fg
], font
->xft_set
, winx
, winy
, (FcChar8 
*)s
, bytelen
); 
2111         if(base
.mode 
& ATTR_UNDERLINE
) 
2112                 XDrawLine(xw
.dpy
, xw
.buf
, dc
.gc
, winx
, winy
+1, winx
+width
-1, winy
+1); 
2117         static int oldx 
= 0; 
2118         static int oldy 
= 0; 
2120         Glyph g 
= {{' '}, ATTR_NULL
, DefaultBG
, DefaultCS
, 0}; 
2122         LIMIT(oldx
, 0, term
.col
-1); 
2123         LIMIT(oldy
, 0, term
.row
-1); 
2125         if(term
.line
[term
.c
.y
][term
.c
.x
].state 
& GLYPH_SET
) 
2126                 memcpy(g
.c
, term
.line
[term
.c
.y
][term
.c
.x
].c
, UTF_SIZ
); 
2128         /* remove the old cursor */ 
2129         if(term
.line
[oldy
][oldx
].state 
& GLYPH_SET
) { 
2130                 sl 
= utf8size(term
.line
[oldy
][oldx
].c
); 
2131                 xdraws(term
.line
[oldy
][oldx
].c
, term
.line
[oldy
][oldx
], oldx
, oldy
, 1, sl
); 
2133                 xclear(oldx
, oldy
, oldx
, oldy
); 
2135         /* draw the new one */ 
2136         if(!(term
.c
.state 
& CURSOR_HIDE
)) { 
2137                 if(!(xw
.state 
& WIN_FOCUSED
)) 
2140                 if(IS_SET(MODE_REVERSE
)) 
2141                         g
.mode 
|= ATTR_REVERSE
, g
.fg 
= DefaultCS
, g
.bg 
= DefaultFG
; 
2144                 xdraws(g
.c
, g
, term
.c
.x
, term
.c
.y
, 1, sl
); 
2145                 oldx 
= term
.c
.x
, oldy 
= term
.c
.y
; 
2151         XStoreName(xw
.dpy
, xw
.win
, opt_title 
? opt_title 
: "st"); 
2156         struct timespec tv 
= {0, REDRAW_TIMEOUT 
* 1000}; 
2158         xclear(0, 0, xw
.w
, xw
.h
); 
2161         XSync(xw
.dpy
, False
); /* necessary for a good tput flash */ 
2162         nanosleep(&tv
, NULL
); 
2167         XdbeSwapInfo swpinfo
[1] = {{xw
.win
, XdbeCopied
}}; 
2169         drawregion(0, 0, term
.col
, term
.row
); 
2170         XdbeSwapBuffers(xw
.dpy
, swpinfo
, 1); 
2174 drawregion(int x1
, int y1
, int x2
, int y2
) { 
2175         int ic
, ib
, x
, y
, ox
, sl
; 
2177         char buf
[DRAW_BUF_SIZ
]; 
2178         bool ena_sel 
= sel
.bx 
!= -1, alt 
= IS_SET(MODE_ALTSCREEN
); 
2180         if((sel
.alt 
&& !alt
) || (!sel
.alt 
&& alt
)) 
2182         if(!(xw
.state 
& WIN_VISIBLE
)) 
2185         for(y 
= y1
; y 
< y2
; y
++) { 
2188                 xclear(0, y
, term
.col
, y
); 
2190                 base 
= term
.line
[y
][0]; 
2192                 for(x 
= x1
; x 
< x2
; x
++) { 
2193                         new = term
.line
[y
][x
]; 
2194                         if(ena_sel 
&& *(new.c
) && selected(x
, y
)) 
2195                                 new.mode 
^= ATTR_REVERSE
; 
2196                         if(ib 
> 0 && (!(new.state 
& GLYPH_SET
) || ATTRCMP(base
, new) || 
2197                                                   ib 
>= DRAW_BUF_SIZ
-UTF_SIZ
)) { 
2198                                 xdraws(buf
, base
, ox
, y
, ic
, ib
); 
2201                         if(new.state 
& GLYPH_SET
) { 
2206                                 sl 
= utf8size(new.c
); 
2207                                 memcpy(buf
+ib
, new.c
, sl
); 
2213                         xdraws(buf
, base
, ox
, y
, ic
, ib
); 
2219 expose(XEvent 
*ev
) { 
2220         XExposeEvent 
*e 
= &ev
->xexpose
; 
2221         if(xw
.state 
& WIN_REDRAW
) { 
2223                         xw
.state 
&= ~WIN_REDRAW
; 
2228 visibility(XEvent 
*ev
) { 
2229         XVisibilityEvent 
*e 
= &ev
->xvisibility
; 
2230         if(e
->state 
== VisibilityFullyObscured
) 
2231                 xw
.state 
&= ~WIN_VISIBLE
; 
2232         else if(!(xw
.state 
& WIN_VISIBLE
)) 
2233                 /* need a full redraw for next Expose, not just a buf copy */ 
2234                 xw
.state 
|= WIN_VISIBLE 
| WIN_REDRAW
; 
2239         xw
.state 
&= ~WIN_VISIBLE
; 
2243 xseturgency(int add
) { 
2244         XWMHints 
*h 
= XGetWMHints(xw
.dpy
, xw
.win
); 
2245         h
->flags 
= add 
? (h
->flags 
| XUrgencyHint
) : (h
->flags 
& ~XUrgencyHint
); 
2246         XSetWMHints(xw
.dpy
, xw
.win
, h
); 
2252         if(ev
->type 
== FocusIn
) { 
2253                 xw
.state 
|= WIN_FOCUSED
; 
2256                 xw
.state 
&= ~WIN_FOCUSED
; 
2260 kmap(KeySym k
, uint state
) { 
2263         for(i 
= 0; i 
< LEN(key
); i
++) { 
2264                 uint mask 
= key
[i
].mask
; 
2265                 if(key
[i
].k 
== k 
&& ((state 
& mask
) == mask 
|| (mask 
== XK_NO_MOD 
&& !state
))) 
2266                         return (char*)key
[i
].s
; 
2272 kpress(XEvent 
*ev
) { 
2273         XKeyEvent 
*e 
= &ev
->xkey
; 
2282         if (IS_SET(MODE_KBDLOCK
)) 
2284         meta 
= e
->state 
& Mod1Mask
; 
2285         shift 
= e
->state 
& ShiftMask
; 
2286         len 
= XmbLookupString(xw
.xic
, e
, buf
, sizeof(buf
), &ksym
, &status
); 
2288         /* 1. custom keys from config.h */ 
2289         if((customkey 
= kmap(ksym
, e
->state
))) 
2290                 ttywrite(customkey
, strlen(customkey
)); 
2291         /* 2. hardcoded (overrides X lookup) */ 
2298                         /* XXX: shift up/down doesn't work */ 
2299                         sprintf(buf
, "\033%c%c", IS_SET(MODE_APPKEYPAD
) ? 'O' : '[', (shift 
? "dacb":"DACB")[ksym 
- XK_Left
]); 
2307                         if(IS_SET(MODE_CRLF
)) 
2308                                 ttywrite("\r\n", 2); 
2315                                 if(meta 
&& len 
== 1) 
2316                                         ttywrite("\033", 1); 
2324 cmessage(XEvent 
*e
) { 
2326            http://standards.freedesktop.org/xembed-spec/xembed-spec-latest.html */ 
2327         if(e
->xclient
.message_type 
== xw
.xembed 
&& e
->xclient
.format 
== 32) { 
2328                 if(e
->xclient
.data
.l
[1] == XEMBED_FOCUS_IN
) { 
2329                         xw
.state 
|= WIN_FOCUSED
; 
2331                 } else if(e
->xclient
.data
.l
[1] == XEMBED_FOCUS_OUT
) { 
2332                         xw
.state 
&= ~WIN_FOCUSED
; 
2341         if(e
->xconfigure
.width 
== xw
.w 
&& e
->xconfigure
.height 
== xw
.h
) 
2344         xw
.w 
= e
->xconfigure
.width
; 
2345         xw
.h 
= e
->xconfigure
.height
; 
2346         col 
= (xw
.w 
- 2*BORDER
) / xw
.cw
; 
2347         row 
= (xw
.h 
- 2*BORDER
) / xw
.ch
; 
2348         if(col 
== term
.col 
&& row 
== term
.row
) 
2351         xclear(0, 0, xw
.w
, xw
.h
); 
2361         int xfd 
= XConnectionNumber(xw
.dpy
), i
; 
2362         struct timeval drawtimeout
, *tv 
= NULL
; 
2366                 FD_SET(cmdfd
, &rfd
); 
2368                 if(select(MAX(xfd
, cmdfd
)+1, &rfd
, NULL
, NULL
, tv
) < 0) { 
2371                         die("select failed: %s\n", SERRNO
); 
2375                  * Stop after a certain number of reads so the user does not 
2376                  * feel like the system is stuttering. 
2378                 if(i 
< 1000 && FD_ISSET(cmdfd
, &rfd
)) { 
2382                          * Just wait a bit so it isn't disturbing the 
2383                          * user and the system is able to write something. 
2385                         drawtimeout
.tv_sec 
= 0; 
2386                         drawtimeout
.tv_usec 
= 5; 
2393                 while(XPending(xw
.dpy
)) { 
2394                         XNextEvent(xw
.dpy
, &ev
); 
2395                         if(XFilterEvent(&ev
, xw
.win
)) 
2397                         if(handler
[ev
.type
]) 
2398                                 (handler
[ev
.type
])(&ev
); 
2407 main(int argc
, char *argv
[]) { 
2408         int i
, bitm
, xr
, yr
; 
2411         xw
.fw 
= xw
.fh 
= xw
.fx 
= xw
.fy 
= 0; 
2414         for(i 
= 1; i 
< argc
; i
++) { 
2415                 switch(argv
[i
][0] != '-' || argv
[i
][2] ? -1 : argv
[i
][1]) { 
2417                         if(++i 
< argc
) opt_title 
= argv
[i
]; 
2420                         if(++i 
< argc
) opt_class 
= argv
[i
]; 
2423                         if(++i 
< argc
) opt_embed 
= argv
[i
]; 
2426                         if(++i 
< argc
) opt_io 
= argv
[i
]; 
2429                         /* eat every remaining arguments */ 
2430                         if(++i 
< argc
) opt_cmd 
= &argv
[i
]; 
2436                         bitm 
= XParseGeometry(argv
[i
], &xr
, &yr
, &wr
, &hr
); 
2441                         if(bitm 
& WidthValue
) 
2443                         if(bitm 
& HeightValue
) 
2445                         if(bitm 
& XNegative 
&& xw
.fx 
== 0) 
2447                         if(bitm 
& XNegative 
&& xw
.fy 
== 0) 
2450                         if(xw
.fh 
!= 0 && xw
.fw 
!= 0) 
2460         setlocale(LC_CTYPE
, "");