1 /* See LICENSE for licence details. */ 
  15 #include <sys/ioctl.h> 
  16 #include <sys/select.h> 
  19 #include <sys/types.h> 
  24 #include <X11/Xatom.h> 
  26 #include <X11/Xutil.h> 
  27 #include <X11/cursorfont.h> 
  28 #include <X11/keysym.h> 
  29 #include <X11/Xft/Xft.h> 
  30 #include <fontconfig/fontconfig.h> 
  42 #elif defined(__OpenBSD__) || defined(__NetBSD__) || defined(__APPLE__) 
  44 #elif defined(__FreeBSD__) || defined(__DragonFly__) 
  50 #define XEMBED_FOCUS_IN  4 
  51 #define XEMBED_FOCUS_OUT 5 
  54 #define UTF_INVALID   0xFFFD 
  56 #define ESC_BUF_SIZ   (128*UTF_SIZ) 
  57 #define ESC_ARG_SIZ   16 
  58 #define STR_BUF_SIZ   ESC_BUF_SIZ 
  59 #define STR_ARG_SIZ   ESC_ARG_SIZ 
  60 #define DRAW_BUF_SIZ  20*1024 
  61 #define XK_ANY_MOD    UINT_MAX 
  63 #define XK_SWITCH_MOD (1<<13) 
  65 #define REDRAW_TIMEOUT (80*1000) /* 80 ms */ 
  68 #define MIN(a, b)  ((a) < (b) ? (a) : (b)) 
  69 #define MAX(a, b)  ((a) < (b) ? (b) : (a)) 
  70 #define LEN(a)     (sizeof(a) / sizeof(a)[0]) 
  71 #define DEFAULT(a, b)     (a) = (a) ? (a) : (b) 
  72 #define BETWEEN(x, a, b)  ((a) <= (x) && (x) <= (b)) 
  73 #define ISCONTROLC0(c) (BETWEEN(c, 0, 0x1f)) 
  74 #define ISCONTROLC1(c) (BETWEEN(c, 0x80, 0x9f)) 
  75 #define ISCONTROL(c) (ISCONTROLC0(c) || ISCONTROLC1(c)) 
  76 #define LIMIT(x, a, b)    (x) = (x) < (a) ? (a) : (x) > (b) ? (b) : (x) 
  77 #define ATTRCMP(a, b) ((a).mode != (b).mode || (a).fg != (b).fg || (a).bg != (b).bg) 
  78 #define IS_SET(flag) ((term.mode & (flag)) != 0) 
  79 #define TIMEDIFF(t1, t2) ((t1.tv_sec-t2.tv_sec)*1000 + (t1.tv_usec-t2.tv_usec)/1000) 
  80 #define CEIL(x) (((x) != (int) (x)) ? (x) + 1 : (x)) 
  81 #define MODBIT(x, set, bit) ((set) ? ((x) |= (bit)) : ((x) &= ~(bit))) 
  83 #define TRUECOLOR(r,g,b) (1 << 24 | (r) << 16 | (g) << 8 | (b)) 
  84 #define IS_TRUECOL(x)    (1 << 24 & (x)) 
  85 #define TRUERED(x)       (((x) & 0xff0000) >> 8) 
  86 #define TRUEGREEN(x)     (((x) & 0xff00)) 
  87 #define TRUEBLUE(x)      (((x) & 0xff) << 8) 
  90 #define VT102ID "\033[?6c" 
  92 enum glyph_attribute 
{ 
 104 enum cursor_movement 
{ 
 122         MODE_MOUSEMOTION 
= 64, 
 127         MODE_APPCURSOR   
= 2048, 
 128         MODE_MOUSESGR    
= 4096, 
 133         MODE_MOUSEX10    
= 131072, 
 134         MODE_MOUSEMANY   
= 262144, 
 135         MODE_BRCKTPASTE  
= 524288, 
 136         MODE_PRINT       
= 1048576, 
 137         MODE_MOUSE       
= MODE_MOUSEBTN
|MODE_MOUSEMOTION
|MODE_MOUSEX10\
 
 154         ESC_STR        
= 4,  /* DCS, OSC, PM, APC */ 
 156         ESC_STR_END    
= 16, /* a final string was encountered */ 
 157         ESC_TEST       
= 32, /* Enter in test mode */ 
 166 enum selection_type 
{ 
 171 enum selection_snap 
{ 
 176 typedef unsigned char uchar
; 
 177 typedef unsigned int uint
; 
 178 typedef unsigned long ulong
; 
 179 typedef unsigned short ushort
; 
 181 typedef XftDraw 
*Draw
; 
 182 typedef XftColor Color
; 
 183 typedef Colormap Colormap
; 
 186         char c
[UTF_SIZ
]; /* character code */ 
 187         ushort mode
;      /* attribute flags */ 
 188         uint32_t fg
;      /* foreground  */ 
 189         uint32_t bg
;      /* background  */ 
 195         Glyph attr
; /* current char attributes */ 
 201 /* CSI Escape sequence structs */ 
 202 /* ESC '[' [[ [<priv>] <arg> [;]] <mode>] */ 
 204         char buf
[ESC_BUF_SIZ
]; /* raw string */ 
 205         int len
;               /* raw string length */ 
 207         int arg
[ESC_ARG_SIZ
]; 
 208         int narg
;              /* nb of args */ 
 212 /* STR Escape sequence structs */ 
 213 /* ESC type [[ [<priv>] <arg> [;]] <mode>] ESC '\' */ 
 215         char type
;             /* ESC type ... */ 
 216         char buf
[STR_BUF_SIZ
]; /* raw string */ 
 217         int len
;               /* raw string length */ 
 218         char *args
[STR_ARG_SIZ
]; 
 219         int narg
;              /* nb of args */ 
 222 /* Internal representation of the screen */ 
 224         int row
;      /* nb row */ 
 225         int col
;      /* nb col */ 
 226         Line 
*line
;   /* screen */ 
 227         Line 
*alt
;    /* alternate screen */ 
 228         bool *dirty
;  /* dirtyness of lines */ 
 229         TCursor c
;    /* cursor */ 
 230         int top
;      /* top    scroll limit */ 
 231         int bot
;      /* bottom scroll limit */ 
 232         int mode
;     /* terminal mode flags */ 
 233         int esc
;      /* escape state flags */ 
 234         char trantbl
[4]; /* charset table translation */ 
 235         int charset
;  /* current charset */ 
 236         int icharset
; /* selected charset for sequence */ 
 237         bool numlock
; /* lock numbers in keyboard */ 
 241 /* Purely graphic info */ 
 247         Atom xembed
, wmdeletewin
, netwmname
, netwmpid
; 
 252         XSetWindowAttributes attrs
; 
 254         bool isfixed
; /* is fixed geometry? */ 
 255         int l
, t
; /* left and top offset */ 
 256         int gm
; /* geometry mask */ 
 257         int tw
, th
; /* tty width and height */ 
 258         int w
, h
; /* window width and height */ 
 259         int ch
; /* char height */ 
 260         int cw
; /* char width  */ 
 261         char state
; /* focus, redraw, visible */ 
 274         /* three valued logic variables: 0 indifferent, 1 on, -1 off */ 
 275         signed char appkey
;    /* application keypad */ 
 276         signed char appcursor
; /* application cursor */ 
 277         signed char crlf
;      /* crlf mode          */ 
 285          * Selection variables: 
 286          * nb – normalized coordinates of the beginning of the selection 
 287          * ne – normalized coordinates of the end of the selection 
 288          * ob – original coordinates of the beginning of the selection 
 289          * oe – original coordinates of the end of the selection 
 298         struct timeval tclick1
; 
 299         struct timeval tclick2
; 
 312         void (*func
)(const Arg 
*); 
 316 /* function definitions used in config.h */ 
 317 static void clippaste(const Arg 
*); 
 318 static void numlock(const Arg 
*); 
 319 static void selpaste(const Arg 
*); 
 320 static void xzoom(const Arg 
*); 
 321 static void printsel(const Arg 
*); 
 322 static void printscreen(const Arg 
*) ; 
 323 static void toggleprinter(const Arg 
*); 
 325 /* Config.h for applying patches and the configuration. */ 
 341 /* Drawing Context */ 
 343         Color col
[MAX(LEN(colorname
), 256)]; 
 344         Font font
, bfont
, ifont
, ibfont
; 
 348 static void die(const char *, ...); 
 349 static void draw(void); 
 350 static void redraw(int); 
 351 static void drawregion(int, int, int, int); 
 352 static void execsh(void); 
 353 static void sigchld(int); 
 354 static void run(void); 
 356 static void csidump(void); 
 357 static void csihandle(void); 
 358 static void csiparse(void); 
 359 static void csireset(void); 
 360 static void strdump(void); 
 361 static void strhandle(void); 
 362 static void strparse(void); 
 363 static void strreset(void); 
 365 static int tattrset(int); 
 366 static void tprinter(char *, size_t); 
 367 static void tdumpsel(void); 
 368 static void tdumpline(int); 
 369 static void tdump(void); 
 370 static void tclearregion(int, int, int, int); 
 371 static void tcursor(int); 
 372 static void tdeletechar(int); 
 373 static void tdeleteline(int); 
 374 static void tinsertblank(int); 
 375 static void tinsertblankline(int); 
 376 static void tmoveto(int, int); 
 377 static void tmoveato(int, int); 
 378 static void tnew(int, int); 
 379 static void tnewline(int); 
 380 static void tputtab(int); 
 381 static void tputc(char *, int); 
 382 static void treset(void); 
 383 static int tresize(int, int); 
 384 static void tscrollup(int, int); 
 385 static void tscrolldown(int, int); 
 386 static void tsetattr(int *, int); 
 387 static void tsetchar(char *, Glyph 
*, int, int); 
 388 static void tsetscroll(int, int); 
 389 static void tswapscreen(void); 
 390 static void tsetdirt(int, int); 
 391 static void tsetdirtattr(int); 
 392 static void tsetmode(bool, bool, int *, int); 
 393 static void tfulldirt(void); 
 394 static void techo(char *, int); 
 395 static void tcontrolcode(uchar 
); 
 396 static void tdectest(char ); 
 397 static int32_t tdefcolor(int *, int *, int); 
 398 static void tdeftran(char); 
 399 static inline bool match(uint
, uint
); 
 400 static void ttynew(void); 
 401 static void ttyread(void); 
 402 static void ttyresize(void); 
 403 static void ttysend(char *, size_t); 
 404 static void ttywrite(const char *, size_t); 
 406 static void xdraws(char *, Glyph
, int, int, int, int); 
 407 static void xhints(void); 
 408 static void xclear(int, int, int, int); 
 409 static void xdrawcursor(void); 
 410 static void xinit(void); 
 411 static void xloadcols(void); 
 412 static int xsetcolorname(int, const char *); 
 413 static int xgeommasktogravity(int); 
 414 static int xloadfont(Font 
*, FcPattern 
*); 
 415 static void xloadfonts(char *, double); 
 416 static int xloadfontset(Font 
*); 
 417 static void xsettitle(char *); 
 418 static void xresettitle(void); 
 419 static void xsetpointermotion(int); 
 420 static void xseturgency(int); 
 421 static void xsetsel(char *); 
 422 static void xtermclear(int, int, int, int); 
 423 static void xunloadfont(Font 
*); 
 424 static void xunloadfonts(void); 
 425 static void xresize(int, int); 
 427 static void expose(XEvent 
*); 
 428 static void visibility(XEvent 
*); 
 429 static void unmap(XEvent 
*); 
 430 static char *kmap(KeySym
, uint
); 
 431 static void kpress(XEvent 
*); 
 432 static void cmessage(XEvent 
*); 
 433 static void cresize(int, int); 
 434 static void resize(XEvent 
*); 
 435 static void focus(XEvent 
*); 
 436 static void brelease(XEvent 
*); 
 437 static void bpress(XEvent 
*); 
 438 static void bmotion(XEvent 
*); 
 439 static void selnotify(XEvent 
*); 
 440 static void selclear(XEvent 
*); 
 441 static void selrequest(XEvent 
*); 
 443 static void selinit(void); 
 444 static void selnormalize(void); 
 445 static inline bool selected(int, int); 
 446 static char *getsel(void); 
 447 static void selcopy(void); 
 448 static void selscroll(int, int); 
 449 static void selsnap(int, int *, int *, int); 
 450 static void getbuttoninfo(XEvent 
*); 
 451 static void mousereport(XEvent 
*); 
 453 static size_t utf8decode(char *, long *, size_t); 
 454 static long utf8decodebyte(char, size_t *); 
 455 static size_t utf8encode(long, char *, size_t); 
 456 static char utf8encodebyte(long, size_t); 
 457 static size_t utf8len(char *); 
 458 static size_t utf8validate(long *, size_t); 
 460 static ssize_t 
xwrite(int, const char *, size_t); 
 461 static void *xmalloc(size_t); 
 462 static void *xrealloc(void *, size_t); 
 463 static char *xstrdup(char *); 
 465 static void usage(void); 
 467 static void (*handler
[LASTEvent
])(XEvent 
*) = { 
 469         [ClientMessage
] = cmessage
, 
 470         [ConfigureNotify
] = resize
, 
 471         [VisibilityNotify
] = visibility
, 
 472         [UnmapNotify
] = unmap
, 
 476         [MotionNotify
] = bmotion
, 
 477         [ButtonPress
] = bpress
, 
 478         [ButtonRelease
] = brelease
, 
 479         [SelectionClear
] = selclear
, 
 480         [SelectionNotify
] = selnotify
, 
 481         [SelectionRequest
] = selrequest
, 
 488 static CSIEscape csiescseq
; 
 489 static STREscape strescseq
; 
 492 static Selection sel
; 
 493 static int iofd 
= STDOUT_FILENO
; 
 494 static char **opt_cmd 
= NULL
; 
 495 static char *opt_io 
= NULL
; 
 496 static char *opt_title 
= NULL
; 
 497 static char *opt_embed 
= NULL
; 
 498 static char *opt_class 
= NULL
; 
 499 static char *opt_font 
= NULL
; 
 500 static int oldbutton 
= 3; /* button event on startup: 3 = release */ 
 502 static char *usedfont 
= NULL
; 
 503 static double usedfontsize 
= 0; 
 505 static uchar utfbyte
[UTF_SIZ 
+ 1] = {0x80,    0, 0xC0, 0xE0, 0xF0}; 
 506 static uchar utfmask
[UTF_SIZ 
+ 1] = {0xC0, 0x80, 0xE0, 0xF0, 0xF8}; 
 507 static long utfmin
[UTF_SIZ 
+ 1] = {       0,    0,  0x80,  0x800,  0x10000}; 
 508 static long utfmax
[UTF_SIZ 
+ 1] = {0x10FFFF, 0x7F, 0x7FF, 0xFFFF, 0x10FFFF}; 
 510 /* Font Ring Cache */ 
 523 /* Fontcache is an array now. A new font will be appended to the array. */ 
 524 static Fontcache frc
[16]; 
 525 static int frclen 
= 0; 
 528 xwrite(int fd
, const char *s
, size_t len
) { 
 532                 ssize_t r 
= write(fd
, s
, len
); 
 542 xmalloc(size_t len
) { 
 543         void *p 
= malloc(len
); 
 546                 die("Out of memory\n"); 
 552 xrealloc(void *p
, size_t len
) { 
 553         if((p 
= realloc(p
, len
)) == NULL
) 
 554                 die("Out of memory\n"); 
 561         if((s 
= strdup(s
)) == NULL
) 
 562                 die("Out of memory\n"); 
 568 utf8decode(char *c
, long *u
, size_t clen
) { 
 569         size_t i
, j
, len
, type
; 
 575         udecoded 
= utf8decodebyte(c
[0], &len
); 
 576         if(!BETWEEN(len
, 1, UTF_SIZ
)) 
 578         for(i 
= 1, j 
= 1; i 
< clen 
&& j 
< len
; ++i
, ++j
) { 
 579                 udecoded 
= (udecoded 
<< 6) | utf8decodebyte(c
[i
], &type
); 
 586         utf8validate(u
, len
); 
 591 utf8decodebyte(char c
, size_t *i
) { 
 592         for(*i 
= 0; *i 
< LEN(utfmask
); ++(*i
)) 
 593                 if(((uchar
)c 
& utfmask
[*i
]) == utfbyte
[*i
]) 
 594                         return (uchar
)c 
& ~utfmask
[*i
]; 
 599 utf8encode(long u
, char *c
, size_t clen
) { 
 602         len 
= utf8validate(&u
, 0); 
 605         for(i 
= len 
- 1; i 
!= 0; --i
) { 
 606                 c
[i
] = utf8encodebyte(u
, 0); 
 609         c
[0] = utf8encodebyte(u
, len
); 
 614 utf8encodebyte(long u
, size_t i
) { 
 615         return utfbyte
[i
] | (u 
& ~utfmask
[i
]); 
 620         return utf8decode(c
, &(long){0}, UTF_SIZ
); 
 624 utf8validate(long *u
, size_t i
) { 
 625         if(!BETWEEN(*u
, utfmin
[i
], utfmax
[i
]) || BETWEEN(*u
, 0xD800, 0xDFFF)) 
 627         for(i 
= 1; *u 
> utfmax
[i
]; ++i
) 
 634         memset(&sel
.tclick1
, 0, sizeof(sel
.tclick1
)); 
 635         memset(&sel
.tclick2
, 0, sizeof(sel
.tclick2
)); 
 639         sel
.xtarget 
= XInternAtom(xw
.dpy
, "UTF8_STRING", 0); 
 640         if(sel
.xtarget 
== None
) 
 641                 sel
.xtarget 
= XA_STRING
; 
 649         return LIMIT(x
, 0, term
.col
-1); 
 657         return LIMIT(y
, 0, term
.row
-1); 
 660 static int tlinelen(int y
) { 
 663         while (i 
> 0 && term
.line
[y
][i 
- 1].c
[0] == ' ') 
 673         if(sel
.ob
.y 
== sel
.oe
.y 
|| sel
.type 
== SEL_RECTANGULAR
) { 
 674                 sel
.nb
.x 
= MIN(sel
.ob
.x
, sel
.oe
.x
); 
 675                 sel
.ne
.x 
= MAX(sel
.ob
.x
, sel
.oe
.x
); 
 677                 sel
.nb
.x 
= sel
.ob
.y 
< sel
.oe
.y 
? sel
.ob
.x 
: sel
.oe
.x
; 
 678                 sel
.ne
.x 
= sel
.ob
.y 
< sel
.oe
.y 
? sel
.oe
.x 
: sel
.ob
.x
; 
 680         sel
.nb
.y 
= MIN(sel
.ob
.y
, sel
.oe
.y
); 
 681         sel
.ne
.y 
= MAX(sel
.ob
.y
, sel
.oe
.y
); 
 683         /* expand selection over line breaks */ 
 684         if (sel
.type 
== SEL_RECTANGULAR
) 
 686         i 
= tlinelen(sel
.nb
.y
); 
 689         if (tlinelen(sel
.ne
.y
) <= sel
.ne
.x
) 
 690                 sel
.ne
.x 
= term
.col 
- 1; 
 694 selected(int x
, int y
) { 
 695         if(sel
.type 
== SEL_RECTANGULAR
) 
 696                 return BETWEEN(y
, sel
.nb
.y
, sel
.ne
.y
) 
 697                     && BETWEEN(x
, sel
.nb
.x
, sel
.ne
.x
); 
 699         return BETWEEN(y
, sel
.nb
.y
, sel
.ne
.y
) 
 700             && (y 
!= sel
.nb
.y 
|| x 
>= sel
.nb
.x
) 
 701             && (y 
!= sel
.ne
.y 
|| x 
<= sel
.ne
.x
); 
 705 selsnap(int mode
, int *x
, int *y
, int direction
) { 
 709                  * Snap around if the word wraps around at the end or 
 710                  * beginning of a line. 
 713                         if(direction 
< 0 && *x 
<= 0) { 
 714                                 if(*y 
> 0 && term
.line
[*y 
- 1][term
.col
-1].mode
 
 722                         if(direction 
> 0 && *x 
>= term
.col
-1) { 
 723                                 if(*y 
< term
.row
-1 && term
.line
[*y
][*x
].mode
 
 732                         if(term
.line
[*y
][*x
+direction
].mode 
& ATTR_WDUMMY
) { 
 737                         if(*x 
>= tlinelen(*y
) || strchr(worddelimiters
, 
 738                                         term
.line
[*y
][*x
+direction
].c
[0])) { 
 747                  * Snap around if the the previous line or the current one 
 748                  * has set ATTR_WRAP at its end. Then the whole next or 
 749                  * previous line will be selected. 
 751                 *x 
= (direction 
< 0) ? 0 : term
.col 
- 1; 
 752                 if(direction 
< 0 && *y 
> 0) { 
 753                         for(; *y 
> 0; *y 
+= direction
) { 
 754                                 if(!(term
.line
[*y
-1][term
.col
-1].mode
 
 759                 } else if(direction 
> 0 && *y 
< term
.row
-1) { 
 760                         for(; *y 
< term
.row
; *y 
+= direction
) { 
 761                                 if(!(term
.line
[*y
][term
.col
-1].mode
 
 772 getbuttoninfo(XEvent 
*e
) { 
 774         uint state 
= e
->xbutton
.state 
& ~(Button1Mask 
| forceselmod
); 
 776         sel
.alt 
= IS_SET(MODE_ALTSCREEN
); 
 778         sel
.oe
.x 
= x2col(e
->xbutton
.x
); 
 779         sel
.oe
.y 
= y2row(e
->xbutton
.y
); 
 781         if(sel
.ob
.y 
< sel
.oe
.y
 
 782                         || (sel
.ob
.y 
== sel
.oe
.y 
&& sel
.ob
.x 
< sel
.oe
.x
)) { 
 783                 selsnap(sel
.snap
, &sel
.ob
.x
, &sel
.ob
.y
, -1); 
 784                 selsnap(sel
.snap
, &sel
.oe
.x
, &sel
.oe
.y
, +1); 
 786                 selsnap(sel
.snap
, &sel
.oe
.x
, &sel
.oe
.y
, -1); 
 787                 selsnap(sel
.snap
, &sel
.ob
.x
, &sel
.ob
.y
, +1); 
 791         sel
.type 
= SEL_REGULAR
; 
 792         for(type 
= 1; type 
< LEN(selmasks
); ++type
) { 
 793                 if(match(selmasks
[type
], state
)) { 
 801 mousereport(XEvent 
*e
) { 
 802         int x 
= x2col(e
->xbutton
.x
), y 
= y2row(e
->xbutton
.y
), 
 803             button 
= e
->xbutton
.button
, state 
= e
->xbutton
.state
, 
 809         if(e
->xbutton
.type 
== MotionNotify
) { 
 810                 if(x 
== ox 
&& y 
== oy
) 
 812                 if(!IS_SET(MODE_MOUSEMOTION
) && !IS_SET(MODE_MOUSEMANY
)) 
 814                 /* MOUSE_MOTION: no reporting if no button is pressed */ 
 815                 if(IS_SET(MODE_MOUSEMOTION
) && oldbutton 
== 3) 
 818                 button 
= oldbutton 
+ 32; 
 822                 if(!IS_SET(MODE_MOUSESGR
) && e
->xbutton
.type 
== ButtonRelease
) { 
 829                 if(e
->xbutton
.type 
== ButtonPress
) { 
 833                 } else if(e
->xbutton
.type 
== ButtonRelease
) { 
 835                         /* MODE_MOUSEX10: no button release reporting */ 
 836                         if(IS_SET(MODE_MOUSEX10
)) 
 838                         if (button 
== 64 || button 
== 65) 
 843         if(!IS_SET(MODE_MOUSEX10
)) { 
 844                 button 
+= (state 
& ShiftMask   
? 4  : 0) 
 845                         + (state 
& Mod4Mask    
? 8  : 0) 
 846                         + (state 
& ControlMask 
? 16 : 0); 
 850         if(IS_SET(MODE_MOUSESGR
)) { 
 851                 len 
= snprintf(buf
, sizeof(buf
), "\033[<%d;%d;%d%c", 
 853                                 e
->xbutton
.type 
== ButtonRelease 
? 'm' : 'M'); 
 854         } else if(x 
< 223 && y 
< 223) { 
 855                 len 
= snprintf(buf
, sizeof(buf
), "\033[M%c%c%c", 
 856                                 32+button
, 32+x
+1, 32+y
+1); 
 869         if(IS_SET(MODE_MOUSE
) && !(e
->xbutton
.state 
& forceselmod
)) { 
 874         for(mk 
= mshortcuts
; mk 
< mshortcuts 
+ LEN(mshortcuts
); mk
++) { 
 875                 if(e
->xbutton
.button 
== mk
->b
 
 876                                 && match(mk
->mask
, e
->xbutton
.state
)) { 
 877                         ttysend(mk
->s
, strlen(mk
->s
)); 
 882         if(e
->xbutton
.button 
== Button1
) { 
 883                 gettimeofday(&now
, NULL
); 
 885                 /* Clear previous selection, logically and visually. */ 
 888                 sel
.type 
= SEL_REGULAR
; 
 889                 sel
.oe
.x 
= sel
.ob
.x 
= x2col(e
->xbutton
.x
); 
 890                 sel
.oe
.y 
= sel
.ob
.y 
= y2row(e
->xbutton
.y
); 
 893                  * If the user clicks below predefined timeouts specific 
 894                  * snapping behaviour is exposed. 
 896                 if(TIMEDIFF(now
, sel
.tclick2
) <= tripleclicktimeout
) { 
 897                         sel
.snap 
= SNAP_LINE
; 
 898                 } else if(TIMEDIFF(now
, sel
.tclick1
) <= doubleclicktimeout
) { 
 899                         sel
.snap 
= SNAP_WORD
; 
 903                 selsnap(sel
.snap
, &sel
.ob
.x
, &sel
.ob
.y
, -1); 
 904                 selsnap(sel
.snap
, &sel
.oe
.x
, &sel
.oe
.y
, +1); 
 908                  * Draw selection, unless it's regular and we don't want to 
 909                  * make clicks visible 
 913                         tsetdirt(sel
.nb
.y
, sel
.ne
.y
); 
 915                 sel
.tclick2 
= sel
.tclick1
; 
 923         int x
, y
, bufsize
, size
, ex
; 
 929         bufsize 
= (term
.col
+1) * (sel
.ne
.y
-sel
.nb
.y
+1) * UTF_SIZ
; 
 930         ptr 
= str 
= xmalloc(bufsize
); 
 932         /* append every set & selected glyph to the selection */ 
 933         for(y 
= sel
.nb
.y
; y 
< sel
.ne
.y 
+ 1; y
++) { 
 934                 gp 
= &term
.line
[y
][0]; 
 935                 last 
= &gp
[term
.col
-1]; 
 937                 while(last 
>= gp 
&& !(selected(last 
- gp
, y
) && 
 938                                       strcmp(last
->c
, " ") != 0)) { 
 942                 for(x 
= 0; gp 
<= last
; x
++, ++gp
) { 
 943                         if(!selected(x
, y
) || (gp
->mode 
& ATTR_WDUMMY
)) 
 946                         size 
= utf8len(gp
->c
); 
 947                         memcpy(ptr
, gp
->c
, size
); 
 952                  * Copy and pasting of line endings is inconsistent 
 953                  * in the inconsistent terminal and GUI world. 
 954                  * The best solution seems like to produce '\n' when 
 955                  * something is copied from st and convert '\n' to 
 956                  * '\r', when something to be pasted is received by 
 958                  * FIXME: Fix the computer world. 
 960                 if(y 
< sel
.ne
.y 
&& !(x 
> 0 && (gp
-1)->mode 
& ATTR_WRAP
)) 
 964                  * If the last selected line expands in the selection 
 965                  * after the visible text '\n' is appended. 
 969                         if(sel
.nb
.y 
== sel
.ne
.y 
&& sel
.ne
.x 
< sel
.nb
.x
) 
 985 selnotify(XEvent 
*e
) { 
 986         ulong nitems
, ofs
, rem
; 
 988         uchar 
*data
, *last
, *repl
; 
 993                 if(XGetWindowProperty(xw
.dpy
, xw
.win
, XA_PRIMARY
, ofs
, BUFSIZ
/4, 
 994                                         False
, AnyPropertyType
, &type
, &format
, 
 995                                         &nitems
, &rem
, &data
)) { 
 996                         fprintf(stderr
, "Clipboard allocation failed\n"); 
1001                  * As seen in getsel: 
1002                  * Line endings are inconsistent in the terminal and GUI world 
1003                  * copy and pasting. When receiving some selection data, 
1004                  * replace all '\n' with '\r'. 
1005                  * FIXME: Fix the computer world. 
1008                 last 
= data 
+ nitems 
* format 
/ 8; 
1009                 while((repl 
= memchr(repl
, '\n', last 
- repl
))) { 
1013                 if(IS_SET(MODE_BRCKTPASTE
)) 
1014                         ttywrite("\033[200~", 6); 
1015                 ttysend((char *)data
, nitems 
* format 
/ 8); 
1016                 if(IS_SET(MODE_BRCKTPASTE
)) 
1017                         ttywrite("\033[201~", 6); 
1019                 /* number of 32-bit chunks returned */ 
1020                 ofs 
+= nitems 
* format 
/ 32; 
1025 selpaste(const Arg 
*dummy
) { 
1026         XConvertSelection(xw
.dpy
, XA_PRIMARY
, sel
.xtarget
, XA_PRIMARY
, 
1027                         xw
.win
, CurrentTime
); 
1031 clippaste(const Arg 
*dummy
) { 
1034         clipboard 
= XInternAtom(xw
.dpy
, "CLIPBOARD", 0); 
1035         XConvertSelection(xw
.dpy
, clipboard
, sel
.xtarget
, XA_PRIMARY
, 
1036                         xw
.win
, CurrentTime
); 
1040 selclear(XEvent 
*e
) { 
1044         tsetdirt(sel
.nb
.y
, sel
.ne
.y
); 
1048 selrequest(XEvent 
*e
) { 
1049         XSelectionRequestEvent 
*xsre
; 
1050         XSelectionEvent xev
; 
1051         Atom xa_targets
, string
; 
1053         xsre 
= (XSelectionRequestEvent 
*) e
; 
1054         xev
.type 
= SelectionNotify
; 
1055         xev
.requestor 
= xsre
->requestor
; 
1056         xev
.selection 
= xsre
->selection
; 
1057         xev
.target 
= xsre
->target
; 
1058         xev
.time 
= xsre
->time
; 
1060         xev
.property 
= None
; 
1062         xa_targets 
= XInternAtom(xw
.dpy
, "TARGETS", 0); 
1063         if(xsre
->target 
== xa_targets
) { 
1064                 /* respond with the supported type */ 
1065                 string 
= sel
.xtarget
; 
1066                 XChangeProperty(xsre
->display
, xsre
->requestor
, xsre
->property
, 
1067                                 XA_ATOM
, 32, PropModeReplace
, 
1068                                 (uchar 
*) &string
, 1); 
1069                 xev
.property 
= xsre
->property
; 
1070         } else if(xsre
->target 
== sel
.xtarget 
&& sel
.clip 
!= NULL
) { 
1071                 XChangeProperty(xsre
->display
, xsre
->requestor
, xsre
->property
, 
1072                                 xsre
->target
, 8, PropModeReplace
, 
1073                                 (uchar 
*) sel
.clip
, strlen(sel
.clip
)); 
1074                 xev
.property 
= xsre
->property
; 
1077         /* all done, send a notification to the listener */ 
1078         if(!XSendEvent(xsre
->display
, xsre
->requestor
, True
, 0, (XEvent 
*) &xev
)) 
1079                 fprintf(stderr
, "Error sending SelectionNotify event\n"); 
1083 xsetsel(char *str
) { 
1084         /* register the selection for both the clipboard and the primary */ 
1090         XSetSelectionOwner(xw
.dpy
, XA_PRIMARY
, xw
.win
, CurrentTime
); 
1092         clipboard 
= XInternAtom(xw
.dpy
, "CLIPBOARD", 0); 
1093         XSetSelectionOwner(xw
.dpy
, clipboard
, xw
.win
, CurrentTime
); 
1097 brelease(XEvent 
*e
) { 
1098         if(IS_SET(MODE_MOUSE
) && !(e
->xbutton
.state 
& forceselmod
)) { 
1103         if(e
->xbutton
.button 
== Button2
) { 
1105         } else if(e
->xbutton
.button 
== Button1
) { 
1113                 tsetdirt(sel
.nb
.y
, sel
.ne
.y
); 
1118 bmotion(XEvent 
*e
) { 
1119         int oldey
, oldex
, oldsby
, oldsey
; 
1121         if(IS_SET(MODE_MOUSE
) && !(e
->xbutton
.state 
& forceselmod
)) { 
1136         if(oldey 
!= sel
.oe
.y 
|| oldex 
!= sel
.oe
.x
) 
1137                 tsetdirt(MIN(sel
.nb
.y
, oldsby
), MAX(sel
.ne
.y
, oldsey
)); 
1141 die(const char *errstr
, ...) { 
1144         va_start(ap
, errstr
); 
1145         vfprintf(stderr
, errstr
, ap
); 
1153         char *envshell 
= getenv("SHELL"); 
1154         const struct passwd 
*pass 
= getpwuid(getuid()); 
1155         char buf
[sizeof(long) * 8 + 1]; 
1157         unsetenv("COLUMNS"); 
1159         unsetenv("TERMCAP"); 
1162                 setenv("LOGNAME", pass
->pw_name
, 1); 
1163                 setenv("USER", pass
->pw_name
, 1); 
1164                 setenv("SHELL", pass
->pw_shell
, 0); 
1165                 setenv("HOME", pass
->pw_dir
, 0); 
1168         snprintf(buf
, sizeof(buf
), "%lu", xw
.win
); 
1169         setenv("WINDOWID", buf
, 1); 
1171         signal(SIGCHLD
, SIG_DFL
); 
1172         signal(SIGHUP
, SIG_DFL
); 
1173         signal(SIGINT
, SIG_DFL
); 
1174         signal(SIGQUIT
, SIG_DFL
); 
1175         signal(SIGTERM
, SIG_DFL
); 
1176         signal(SIGALRM
, SIG_DFL
); 
1178         DEFAULT(envshell
, shell
); 
1179         setenv("TERM", termname
, 1); 
1180         args 
= opt_cmd 
? opt_cmd 
: (char *[]){envshell
, "-i", NULL
}; 
1181         execvp(args
[0], args
); 
1189         if(waitpid(pid
, &stat
, 0) < 0) 
1190                 die("Waiting for pid %hd failed: %s\n", pid
, strerror(errno
)); 
1192         if(WIFEXITED(stat
)) { 
1193                 exit(WEXITSTATUS(stat
)); 
1202         struct winsize w 
= {term
.row
, term
.col
, 0, 0}; 
1204         /* seems to work fine on linux, openbsd and freebsd */ 
1205         if(openpty(&m
, &s
, NULL
, NULL
, &w
) < 0) 
1206                 die("openpty failed: %s\n", strerror(errno
)); 
1208         switch(pid 
= fork()) { 
1210                 die("fork failed\n"); 
1213                 setsid(); /* create a new process group */ 
1214                 dup2(s
, STDIN_FILENO
); 
1215                 dup2(s
, STDOUT_FILENO
); 
1216                 dup2(s
, STDERR_FILENO
); 
1217                 if(ioctl(s
, TIOCSCTTY
, NULL
) < 0) 
1218                         die("ioctl TIOCSCTTY failed: %s\n", strerror(errno
)); 
1226                 signal(SIGCHLD
, sigchld
); 
1228                         term
.mode 
|= MODE_PRINT
; 
1229                         iofd 
= (!strcmp(opt_io
, "-")) ? 
1231                                   open(opt_io
, O_WRONLY 
| O_CREAT
, 0666); 
1233                                 fprintf(stderr
, "Error opening %s:%s\n", 
1234                                         opt_io
, strerror(errno
)); 
1243         static char buf
[BUFSIZ
]; 
1244         static int buflen 
= 0; 
1247         int charsize
; /* size of utf8 char in bytes */ 
1251         /* append read bytes to unprocessed bytes */ 
1252         if((ret 
= read(cmdfd
, buf
+buflen
, LEN(buf
)-buflen
)) < 0) 
1253                 die("Couldn't read from shell: %s\n", strerror(errno
)); 
1255         /* process every complete utf8 char */ 
1258         while((charsize 
= utf8decode(ptr
, &unicodep
, buflen
))) { 
1259                 utf8encode(unicodep
, s
, UTF_SIZ
); 
1265         /* keep any uncomplete utf8 char for the next call */ 
1266         memmove(buf
, ptr
, buflen
); 
1270 ttywrite(const char *s
, size_t n
) { 
1271         if(xwrite(cmdfd
, s
, n
) == -1) 
1272                 die("write error on tty: %s\n", strerror(errno
)); 
1276 ttysend(char *s
, size_t n
) { 
1278         if(IS_SET(MODE_ECHO
)) 
1286         w
.ws_row 
= term
.row
; 
1287         w
.ws_col 
= term
.col
; 
1288         w
.ws_xpixel 
= xw
.tw
; 
1289         w
.ws_ypixel 
= xw
.th
; 
1290         if(ioctl(cmdfd
, TIOCSWINSZ
, &w
) < 0) 
1291                 fprintf(stderr
, "Couldn't set window size: %s\n", strerror(errno
)); 
1295 tattrset(int attr
) { 
1298         for(i 
= 0; i 
< term
.row
-1; i
++) { 
1299                 for(j 
= 0; j 
< term
.col
-1; j
++) { 
1300                         if(term
.line
[i
][j
].mode 
& attr
) 
1309 tsetdirt(int top
, int bot
) { 
1312         LIMIT(top
, 0, term
.row
-1); 
1313         LIMIT(bot
, 0, term
.row
-1); 
1315         for(i 
= top
; i 
<= bot
; i
++) 
1320 tsetdirtattr(int attr
) { 
1323         for(i 
= 0; i 
< term
.row
-1; i
++) { 
1324                 for(j 
= 0; j 
< term
.col
-1; j
++) { 
1325                         if(term
.line
[i
][j
].mode 
& attr
) { 
1335         tsetdirt(0, term
.row
-1); 
1340         static TCursor c
[2]; 
1341         bool alt 
= IS_SET(MODE_ALTSCREEN
); 
1343         if(mode 
== CURSOR_SAVE
) { 
1345         } else if(mode 
== CURSOR_LOAD
) { 
1347                 tmoveto(c
[alt
].x
, c
[alt
].y
); 
1355         term
.c 
= (TCursor
){{ 
1359         }, .x 
= 0, .y 
= 0, .state 
= CURSOR_DEFAULT
}; 
1361         memset(term
.tabs
, 0, term
.col 
* sizeof(*term
.tabs
)); 
1362         for(i 
= tabspaces
; i 
< term
.col
; i 
+= tabspaces
) 
1365         term
.bot 
= term
.row 
- 1; 
1366         term
.mode 
= MODE_WRAP
; 
1367         memset(term
.trantbl
, sizeof(term
.trantbl
), CS_USA
); 
1370         tclearregion(0, 0, term
.col
-1, term
.row
-1); 
1372         tcursor(CURSOR_SAVE
); 
1376 tnew(int col
, int row
) { 
1377         term 
= (Term
){ .c 
= { .attr 
= { .fg 
= defaultfg
, .bg 
= defaultbg 
} } }; 
1386         Line 
*tmp 
= term
.line
; 
1388         term
.line 
= term
.alt
; 
1390         term
.mode 
^= MODE_ALTSCREEN
; 
1395 tscrolldown(int orig
, int n
) { 
1399         LIMIT(n
, 0, term
.bot
-orig
+1); 
1401         tsetdirt(orig
, term
.bot
-n
); 
1402         tclearregion(0, term
.bot
-n
+1, term
.col
-1, term
.bot
); 
1404         for(i 
= term
.bot
; i 
>= orig
+n
; i
--) { 
1405                 temp 
= term
.line
[i
]; 
1406                 term
.line
[i
] = term
.line
[i
-n
]; 
1407                 term
.line
[i
-n
] = temp
; 
1414 tscrollup(int orig
, int n
) { 
1418         LIMIT(n
, 0, term
.bot
-orig
+1); 
1420         tclearregion(0, orig
, term
.col
-1, orig
+n
-1); 
1421         tsetdirt(orig
+n
, term
.bot
); 
1423         for(i 
= orig
; i 
<= term
.bot
-n
; i
++) { 
1424                 temp 
= term
.line
[i
]; 
1425                 term
.line
[i
] = term
.line
[i
+n
]; 
1426                 term
.line
[i
+n
] = temp
; 
1429         selscroll(orig
, -n
); 
1433 selscroll(int orig
, int n
) { 
1437         if(BETWEEN(sel
.ob
.y
, orig
, term
.bot
) || BETWEEN(sel
.oe
.y
, orig
, term
.bot
)) { 
1438                 if((sel
.ob
.y 
+= n
) > term
.bot 
|| (sel
.oe
.y 
+= n
) < term
.top
) { 
1442                 if(sel
.type 
== SEL_RECTANGULAR
) { 
1443                         if(sel
.ob
.y 
< term
.top
) 
1444                                 sel
.ob
.y 
= term
.top
; 
1445                         if(sel
.oe
.y 
> term
.bot
) 
1446                                 sel
.oe
.y 
= term
.bot
; 
1448                         if(sel
.ob
.y 
< term
.top
) { 
1449                                 sel
.ob
.y 
= term
.top
; 
1452                         if(sel
.oe
.y 
> term
.bot
) { 
1453                                 sel
.oe
.y 
= term
.bot
; 
1454                                 sel
.oe
.x 
= term
.col
; 
1462 tnewline(int first_col
) { 
1466                 tscrollup(term
.top
, 1); 
1470         tmoveto(first_col 
? 0 : term
.c
.x
, y
); 
1475         char *p 
= csiescseq
.buf
, *np
; 
1484         csiescseq
.buf
[csiescseq
.len
] = '\0'; 
1485         while(p 
< csiescseq
.buf
+csiescseq
.len
) { 
1487                 v 
= strtol(p
, &np
, 10); 
1490                 if(v 
== LONG_MAX 
|| v 
== LONG_MIN
) 
1492                 csiescseq
.arg
[csiescseq
.narg
++] = v
; 
1494                 if(*p 
!= ';' || csiescseq
.narg 
== ESC_ARG_SIZ
) 
1498         csiescseq
.mode 
= *p
; 
1501 /* for absolute user moves, when decom is set */ 
1503 tmoveato(int x
, int y
) { 
1504         tmoveto(x
, y 
+ ((term
.c
.state 
& CURSOR_ORIGIN
) ? term
.top
: 0)); 
1508 tmoveto(int x
, int y
) { 
1511         if(term
.c
.state 
& CURSOR_ORIGIN
) { 
1516                 maxy 
= term
.row 
- 1; 
1518         LIMIT(x
, 0, term
.col
-1); 
1519         LIMIT(y
, miny
, maxy
); 
1520         term
.c
.state 
&= ~CURSOR_WRAPNEXT
; 
1526 tsetchar(char *c
, Glyph 
*attr
, int x
, int y
) { 
1527         static char *vt100_0
[62] = { /* 0x41 - 0x7e */ 
1528                 "↑", "↓", "→", "←", "█", "▚", "☃", /* A - G */ 
1529                 0, 0, 0, 0, 0, 0, 0, 0, /* H - O */ 
1530                 0, 0, 0, 0, 0, 0, 0, 0, /* P - W */ 
1531                 0, 0, 0, 0, 0, 0, 0, " ", /* X - _ */ 
1532                 "◆", "▒", "␉", "␌", "␍", "␊", "°", "±", /* ` - g */ 
1533                 "", "␋", "┘", "┐", "┌", "└", "┼", "⎺", /* h - o */ 
1534                 "⎻", "─", "⎼", "⎽", "├", "┤", "┴", "┬", /* p - w */ 
1535                 "│", "≤", "≥", "π", "≠", "£", "·", /* x - ~ */ 
1539          * The table is proudly stolen from rxvt. 
1541         if(term
.trantbl
[term
.charset
] == CS_GRAPHIC0
) { 
1542                 if(BETWEEN(c
[0], 0x41, 0x7e) && vt100_0
[c
[0] - 0x41]) { 
1543                         c 
= vt100_0
[c
[0] - 0x41]; 
1547         if(term
.line
[y
][x
].mode 
& ATTR_WIDE
) { 
1548                 if(x
+1 < term
.col
) { 
1549                         term
.line
[y
][x
+1].c
[0] = ' '; 
1550                         term
.line
[y
][x
+1].mode 
&= ~ATTR_WDUMMY
; 
1552         } else if(term
.line
[y
][x
].mode 
& ATTR_WDUMMY
) { 
1553                 term
.line
[y
][x
-1].c
[0] = ' '; 
1554                 term
.line
[y
][x
-1].mode 
&= ~ATTR_WIDE
; 
1558         term
.line
[y
][x
] = *attr
; 
1559         memcpy(term
.line
[y
][x
].c
, c
, UTF_SIZ
); 
1563 tclearregion(int x1
, int y1
, int x2
, int y2
) { 
1567                 temp 
= x1
, x1 
= x2
, x2 
= temp
; 
1569                 temp 
= y1
, y1 
= y2
, y2 
= temp
; 
1571         LIMIT(x1
, 0, term
.col
-1); 
1572         LIMIT(x2
, 0, term
.col
-1); 
1573         LIMIT(y1
, 0, term
.row
-1); 
1574         LIMIT(y2
, 0, term
.row
-1); 
1576         for(y 
= y1
; y 
<= y2
; y
++) { 
1578                 for(x 
= x1
; x 
<= x2
; x
++) { 
1581                         term
.line
[y
][x
] = term
.c
.attr
; 
1582                         memcpy(term
.line
[y
][x
].c
, " ", 2); 
1588 tdeletechar(int n
) { 
1592         LIMIT(n
, 0, term
.col 
- term
.c
.x
); 
1596         size 
= term
.col 
- src
; 
1597         line 
= term
.line
[term
.c
.y
]; 
1599         memmove(&line
[dst
], &line
[src
], size 
* sizeof(Glyph
)); 
1600         tclearregion(term
.col
-n
, term
.c
.y
, term
.col
-1, term
.c
.y
); 
1604 tinsertblank(int n
) { 
1608         LIMIT(n
, 0, term
.col 
- term
.c
.x
); 
1612         size 
= term
.col 
- dst
; 
1613         line 
= term
.line
[term
.c
.y
]; 
1615         memmove(&line
[dst
], &line
[src
], size 
* sizeof(Glyph
)); 
1616         tclearregion(src
, term
.c
.y
, dst 
- 1, term
.c
.y
); 
1620 tinsertblankline(int n
) { 
1621         if(BETWEEN(term
.c
.y
, term
.top
, term
.bot
)) 
1622                 tscrolldown(term
.c
.y
, n
); 
1626 tdeleteline(int n
) { 
1627         if(BETWEEN(term
.c
.y
, term
.top
, term
.bot
)) 
1628                 tscrollup(term
.c
.y
, n
); 
1632 tdefcolor(int *attr
, int *npar
, int l
) { 
1636         switch (attr
[*npar 
+ 1]) { 
1637         case 2: /* direct color in RGB space */ 
1638                 if (*npar 
+ 4 >= l
) { 
1640                                 "erresc(38): Incorrect number of parameters (%d)\n", 
1644                 r 
= attr
[*npar 
+ 2]; 
1645                 g 
= attr
[*npar 
+ 3]; 
1646                 b 
= attr
[*npar 
+ 4]; 
1648                 if(!BETWEEN(r
, 0, 255) || !BETWEEN(g
, 0, 255) || !BETWEEN(b
, 0, 255)) 
1649                         fprintf(stderr
, "erresc: bad rgb color (%d,%d,%d)\n", 
1652                         idx 
= TRUECOLOR(r
, g
, b
); 
1654         case 5: /* indexed color */ 
1655                 if (*npar 
+ 2 >= l
) { 
1657                                 "erresc(38): Incorrect number of parameters (%d)\n", 
1662                 if(!BETWEEN(attr
[*npar
], 0, 255)) 
1663                         fprintf(stderr
, "erresc: bad fgcolor %d\n", attr
[*npar
]); 
1667         case 0: /* implemented defined (only foreground) */ 
1668         case 1: /* transparent */ 
1669         case 3: /* direct color in CMY space */ 
1670         case 4: /* direct color in CMYK space */ 
1673                         "erresc(38): gfx attr %d unknown\n", attr
[*npar
]); 
1681 tsetattr(int *attr
, int l
) { 
1685         for(i 
= 0; i 
< l
; i
++) { 
1688                         term
.c
.attr
.mode 
&= ~(ATTR_REVERSE 
| ATTR_UNDERLINE \
 
1689                                         | ATTR_BOLD 
| ATTR_ITALIC \
 
1691                         term
.c
.attr
.fg 
= defaultfg
; 
1692                         term
.c
.attr
.bg 
= defaultbg
; 
1695                         term
.c
.attr
.mode 
|= ATTR_BOLD
; 
1698                         term
.c
.attr
.mode 
|= ATTR_ITALIC
; 
1701                         term
.c
.attr
.mode 
|= ATTR_UNDERLINE
; 
1703                 case 5: /* slow blink */ 
1704                 case 6: /* rapid blink */ 
1705                         term
.c
.attr
.mode 
|= ATTR_BLINK
; 
1708                         term
.c
.attr
.mode 
|= ATTR_REVERSE
; 
1712                         term
.c
.attr
.mode 
&= ~ATTR_BOLD
; 
1715                         term
.c
.attr
.mode 
&= ~ATTR_ITALIC
; 
1718                         term
.c
.attr
.mode 
&= ~ATTR_UNDERLINE
; 
1722                         term
.c
.attr
.mode 
&= ~ATTR_BLINK
; 
1725                         term
.c
.attr
.mode 
&= ~ATTR_REVERSE
; 
1728                         if ((idx 
= tdefcolor(attr
, &i
, l
)) >= 0) 
1729                                 term
.c
.attr
.fg 
= idx
; 
1732                         term
.c
.attr
.fg 
= defaultfg
; 
1735                         if ((idx 
= tdefcolor(attr
, &i
, l
)) >= 0) 
1736                                 term
.c
.attr
.bg 
= idx
; 
1739                         term
.c
.attr
.bg 
= defaultbg
; 
1742                         if(BETWEEN(attr
[i
], 30, 37)) { 
1743                                 term
.c
.attr
.fg 
= attr
[i
] - 30; 
1744                         } else if(BETWEEN(attr
[i
], 40, 47)) { 
1745                                 term
.c
.attr
.bg 
= attr
[i
] - 40; 
1746                         } else if(BETWEEN(attr
[i
], 90, 97)) { 
1747                                 term
.c
.attr
.fg 
= attr
[i
] - 90 + 8; 
1748                         } else if(BETWEEN(attr
[i
], 100, 107)) { 
1749                                 term
.c
.attr
.bg 
= attr
[i
] - 100 + 8; 
1752                                         "erresc(default): gfx attr %d unknown\n", 
1753                                         attr
[i
]), csidump(); 
1761 tsetscroll(int t
, int b
) { 
1764         LIMIT(t
, 0, term
.row
-1); 
1765         LIMIT(b
, 0, term
.row
-1); 
1776 tsetmode(bool priv
, bool set
, int *args
, int narg
) { 
1780         for(lim 
= args 
+ narg
; args 
< lim
; ++args
) { 
1783                         case 1: /* DECCKM -- Cursor key */ 
1784                                 MODBIT(term
.mode
, set
, MODE_APPCURSOR
); 
1786                         case 5: /* DECSCNM -- Reverse video */ 
1788                                 MODBIT(term
.mode
, set
, MODE_REVERSE
); 
1789                                 if(mode 
!= term
.mode
) 
1790                                         redraw(REDRAW_TIMEOUT
); 
1792                         case 6: /* DECOM -- Origin */ 
1793                                 MODBIT(term
.c
.state
, set
, CURSOR_ORIGIN
); 
1796                         case 7: /* DECAWM -- Auto wrap */ 
1797                                 MODBIT(term
.mode
, set
, MODE_WRAP
); 
1799                         case 0:  /* Error (IGNORED) */ 
1800                         case 2:  /* DECANM -- ANSI/VT52 (IGNORED) */ 
1801                         case 3:  /* DECCOLM -- Column  (IGNORED) */ 
1802                         case 4:  /* DECSCLM -- Scroll (IGNORED) */ 
1803                         case 8:  /* DECARM -- Auto repeat (IGNORED) */ 
1804                         case 18: /* DECPFF -- Printer feed (IGNORED) */ 
1805                         case 19: /* DECPEX -- Printer extent (IGNORED) */ 
1806                         case 42: /* DECNRCM -- National characters (IGNORED) */ 
1807                         case 12: /* att610 -- Start blinking cursor (IGNORED) */ 
1809                         case 25: /* DECTCEM -- Text Cursor Enable Mode */ 
1810                                 MODBIT(term
.mode
, !set
, MODE_HIDE
); 
1812                         case 9:    /* X10 mouse compatibility mode */ 
1813                                 xsetpointermotion(0); 
1814                                 MODBIT(term
.mode
, 0, MODE_MOUSE
); 
1815                                 MODBIT(term
.mode
, set
, MODE_MOUSEX10
); 
1817                         case 1000: /* 1000: report button press */ 
1818                                 xsetpointermotion(0); 
1819                                 MODBIT(term
.mode
, 0, MODE_MOUSE
); 
1820                                 MODBIT(term
.mode
, set
, MODE_MOUSEBTN
); 
1822                         case 1002: /* 1002: report motion on button press */ 
1823                                 xsetpointermotion(0); 
1824                                 MODBIT(term
.mode
, 0, MODE_MOUSE
); 
1825                                 MODBIT(term
.mode
, set
, MODE_MOUSEMOTION
); 
1827                         case 1003: /* 1003: enable all mouse motions */ 
1828                                 xsetpointermotion(set
); 
1829                                 MODBIT(term
.mode
, 0, MODE_MOUSE
); 
1830                                 MODBIT(term
.mode
, set
, MODE_MOUSEMANY
); 
1832                         case 1004: /* 1004: send focus events to tty */ 
1833                                 MODBIT(term
.mode
, set
, MODE_FOCUS
); 
1835                         case 1006: /* 1006: extended reporting mode */ 
1836                                 MODBIT(term
.mode
, set
, MODE_MOUSESGR
); 
1839                                 MODBIT(term
.mode
, set
, MODE_8BIT
); 
1841                         case 1049: /* swap screen & set/restore cursor as xterm */ 
1842                                 if (!allowaltscreen
) 
1844                                 tcursor((set
) ? CURSOR_SAVE 
: CURSOR_LOAD
); 
1846                         case 47: /* swap screen */ 
1848                                 if (!allowaltscreen
) 
1850                                 alt 
= IS_SET(MODE_ALTSCREEN
); 
1852                                         tclearregion(0, 0, term
.col
-1, 
1855                                 if(set 
^ alt
) /* set is always 1 or 0 */ 
1861                                 tcursor((set
) ? CURSOR_SAVE 
: CURSOR_LOAD
); 
1863                         case 2004: /* 2004: bracketed paste mode */ 
1864                                 MODBIT(term
.mode
, set
, MODE_BRCKTPASTE
); 
1866                         /* Not implemented mouse modes. See comments there. */ 
1867                         case 1001: /* mouse highlight mode; can hang the 
1868                                       terminal by design when implemented. */ 
1869                         case 1005: /* UTF-8 mouse mode; will confuse 
1870                                       applications not supporting UTF-8 
1872                         case 1015: /* urxvt mangled mouse mode; incompatible 
1873                                       and can be mistaken for other control 
1877                                         "erresc: unknown private set/reset mode %d\n", 
1883                         case 0:  /* Error (IGNORED) */ 
1885                         case 2:  /* KAM -- keyboard action */ 
1886                                 MODBIT(term
.mode
, set
, MODE_KBDLOCK
); 
1888                         case 4:  /* IRM -- Insertion-replacement */ 
1889                                 MODBIT(term
.mode
, set
, MODE_INSERT
); 
1891                         case 12: /* SRM -- Send/Receive */ 
1892                                 MODBIT(term
.mode
, !set
, MODE_ECHO
); 
1894                         case 20: /* LNM -- Linefeed/new line */ 
1895                                 MODBIT(term
.mode
, set
, MODE_CRLF
); 
1899                                         "erresc: unknown set/reset mode %d\n", 
1912         switch(csiescseq
.mode
) { 
1915                 fprintf(stderr
, "erresc: unknown csi "); 
1919         case '@': /* ICH -- Insert <n> blank char */ 
1920                 DEFAULT(csiescseq
.arg
[0], 1); 
1921                 tinsertblank(csiescseq
.arg
[0]); 
1923         case 'A': /* CUU -- Cursor <n> Up */ 
1924                 DEFAULT(csiescseq
.arg
[0], 1); 
1925                 tmoveto(term
.c
.x
, term
.c
.y
-csiescseq
.arg
[0]); 
1927         case 'B': /* CUD -- Cursor <n> Down */ 
1928         case 'e': /* VPR --Cursor <n> Down */ 
1929                 DEFAULT(csiescseq
.arg
[0], 1); 
1930                 tmoveto(term
.c
.x
, term
.c
.y
+csiescseq
.arg
[0]); 
1932         case 'i': /* MC -- Media Copy */ 
1933                 switch(csiescseq
.arg
[0]) { 
1938                         tdumpline(term
.c
.y
); 
1944                         term
.mode 
&= ~MODE_PRINT
; 
1947                         term
.mode 
|= MODE_PRINT
; 
1951         case 'c': /* DA -- Device Attributes */ 
1952                 if(csiescseq
.arg
[0] == 0) 
1953                         ttywrite(VT102ID
, sizeof(VT102ID
) - 1); 
1955         case 'C': /* CUF -- Cursor <n> Forward */ 
1956         case 'a': /* HPR -- Cursor <n> Forward */ 
1957                 DEFAULT(csiescseq
.arg
[0], 1); 
1958                 tmoveto(term
.c
.x
+csiescseq
.arg
[0], term
.c
.y
); 
1960         case 'D': /* CUB -- Cursor <n> Backward */ 
1961                 DEFAULT(csiescseq
.arg
[0], 1); 
1962                 tmoveto(term
.c
.x
-csiescseq
.arg
[0], term
.c
.y
); 
1964         case 'E': /* CNL -- Cursor <n> Down and first col */ 
1965                 DEFAULT(csiescseq
.arg
[0], 1); 
1966                 tmoveto(0, term
.c
.y
+csiescseq
.arg
[0]); 
1968         case 'F': /* CPL -- Cursor <n> Up and first col */ 
1969                 DEFAULT(csiescseq
.arg
[0], 1); 
1970                 tmoveto(0, term
.c
.y
-csiescseq
.arg
[0]); 
1972         case 'g': /* TBC -- Tabulation clear */ 
1973                 switch(csiescseq
.arg
[0]) { 
1974                 case 0: /* clear current tab stop */ 
1975                         term
.tabs
[term
.c
.x
] = 0; 
1977                 case 3: /* clear all the tabs */ 
1978                         memset(term
.tabs
, 0, term
.col 
* sizeof(*term
.tabs
)); 
1984         case 'G': /* CHA -- Move to <col> */ 
1986                 DEFAULT(csiescseq
.arg
[0], 1); 
1987                 tmoveto(csiescseq
.arg
[0]-1, term
.c
.y
); 
1989         case 'H': /* CUP -- Move to <row> <col> */ 
1991                 DEFAULT(csiescseq
.arg
[0], 1); 
1992                 DEFAULT(csiescseq
.arg
[1], 1); 
1993                 tmoveato(csiescseq
.arg
[1]-1, csiescseq
.arg
[0]-1); 
1995         case 'I': /* CHT -- Cursor Forward Tabulation <n> tab stops */ 
1996                 DEFAULT(csiescseq
.arg
[0], 1); 
1997                 tputtab(csiescseq
.arg
[0]); 
1999         case 'J': /* ED -- Clear screen */ 
2001                 switch(csiescseq
.arg
[0]) { 
2003                         tclearregion(term
.c
.x
, term
.c
.y
, term
.col
-1, term
.c
.y
); 
2004                         if(term
.c
.y 
< term
.row
-1) { 
2005                                 tclearregion(0, term
.c
.y
+1, term
.col
-1, 
2011                                 tclearregion(0, 0, term
.col
-1, term
.c
.y
-1); 
2012                         tclearregion(0, term
.c
.y
, term
.c
.x
, term
.c
.y
); 
2015                         tclearregion(0, 0, term
.col
-1, term
.row
-1); 
2021         case 'K': /* EL -- Clear line */ 
2022                 switch(csiescseq
.arg
[0]) { 
2024                         tclearregion(term
.c
.x
, term
.c
.y
, term
.col
-1, 
2028                         tclearregion(0, term
.c
.y
, term
.c
.x
, term
.c
.y
); 
2031                         tclearregion(0, term
.c
.y
, term
.col
-1, term
.c
.y
); 
2035         case 'S': /* SU -- Scroll <n> line up */ 
2036                 DEFAULT(csiescseq
.arg
[0], 1); 
2037                 tscrollup(term
.top
, csiescseq
.arg
[0]); 
2039         case 'T': /* SD -- Scroll <n> line down */ 
2040                 DEFAULT(csiescseq
.arg
[0], 1); 
2041                 tscrolldown(term
.top
, csiescseq
.arg
[0]); 
2043         case 'L': /* IL -- Insert <n> blank lines */ 
2044                 DEFAULT(csiescseq
.arg
[0], 1); 
2045                 tinsertblankline(csiescseq
.arg
[0]); 
2047         case 'l': /* RM -- Reset Mode */ 
2048                 tsetmode(csiescseq
.priv
, 0, csiescseq
.arg
, csiescseq
.narg
); 
2050         case 'M': /* DL -- Delete <n> lines */ 
2051                 DEFAULT(csiescseq
.arg
[0], 1); 
2052                 tdeleteline(csiescseq
.arg
[0]); 
2054         case 'X': /* ECH -- Erase <n> char */ 
2055                 DEFAULT(csiescseq
.arg
[0], 1); 
2056                 tclearregion(term
.c
.x
, term
.c
.y
, 
2057                                 term
.c
.x 
+ csiescseq
.arg
[0] - 1, term
.c
.y
); 
2059         case 'P': /* DCH -- Delete <n> char */ 
2060                 DEFAULT(csiescseq
.arg
[0], 1); 
2061                 tdeletechar(csiescseq
.arg
[0]); 
2063         case 'Z': /* CBT -- Cursor Backward Tabulation <n> tab stops */ 
2064                 DEFAULT(csiescseq
.arg
[0], 1); 
2065                 tputtab(-csiescseq
.arg
[0]); 
2067         case 'd': /* VPA -- Move to <row> */ 
2068                 DEFAULT(csiescseq
.arg
[0], 1); 
2069                 tmoveato(term
.c
.x
, csiescseq
.arg
[0]-1); 
2071         case 'h': /* SM -- Set terminal mode */ 
2072                 tsetmode(csiescseq
.priv
, 1, csiescseq
.arg
, csiescseq
.narg
); 
2074         case 'm': /* SGR -- Terminal attribute (color) */ 
2075                 tsetattr(csiescseq
.arg
, csiescseq
.narg
); 
2077         case 'n': /* DSR – Device Status Report (cursor position) */ 
2078                 if (csiescseq
.arg
[0] == 6) { 
2079                         len 
= snprintf(buf
, sizeof(buf
),"\033[%i;%iR", 
2080                                         term
.c
.y
+1, term
.c
.x
+1); 
2084         case 'r': /* DECSTBM -- Set Scrolling Region */ 
2085                 if(csiescseq
.priv
) { 
2088                         DEFAULT(csiescseq
.arg
[0], 1); 
2089                         DEFAULT(csiescseq
.arg
[1], term
.row
); 
2090                         tsetscroll(csiescseq
.arg
[0]-1, csiescseq
.arg
[1]-1); 
2094         case 's': /* DECSC -- Save cursor position (ANSI.SYS) */ 
2095                 tcursor(CURSOR_SAVE
); 
2097         case 'u': /* DECRC -- Restore cursor position (ANSI.SYS) */ 
2098                 tcursor(CURSOR_LOAD
); 
2109         for(i 
= 0; i 
< csiescseq
.len
; i
++) { 
2110                 c 
= csiescseq
.buf
[i
] & 0xff; 
2113                 } else if(c 
== '\n') { 
2115                 } else if(c 
== '\r') { 
2117                 } else if(c 
== 0x1b) { 
2120                         printf("(%02x)", c
); 
2128         memset(&csiescseq
, 0, sizeof(csiescseq
)); 
2136         term
.esc 
&= ~(ESC_STR_END
|ESC_STR
); 
2138         narg 
= strescseq
.narg
; 
2139         par 
= atoi(strescseq
.args
[0]); 
2141         switch(strescseq
.type
) { 
2142         case ']': /* OSC -- Operating System Command */ 
2148                                 xsettitle(strescseq
.args
[1]); 
2150                 case 4: /* color set */ 
2153                         p 
= strescseq
.args
[2]; 
2155                 case 104: /* color reset, here p = NULL */ 
2156                         j 
= (narg 
> 1) ? atoi(strescseq
.args
[1]) : -1; 
2157                         if(xsetcolorname(j
, p
)) { 
2158                                 fprintf(stderr
, "erresc: invalid color %s\n", p
); 
2161                                  * TODO if defaultbg color is changed, borders 
2169         case 'k': /* old title set compatibility */ 
2170                 xsettitle(strescseq
.args
[0]); 
2172         case 'P': /* DCS -- Device Control String */ 
2173         case '_': /* APC -- Application Program Command */ 
2174         case '^': /* PM -- Privacy Message */ 
2178         fprintf(stderr
, "erresc: unknown str "); 
2184         char *p 
= strescseq
.buf
; 
2187         strescseq
.buf
[strescseq
.len
] = '\0'; 
2188         while(p 
&& strescseq
.narg 
< STR_ARG_SIZ
) 
2189                 strescseq
.args
[strescseq
.narg
++] = strsep(&p
, ";"); 
2197         printf("ESC%c", strescseq
.type
); 
2198         for(i 
= 0; i 
< strescseq
.len
; i
++) { 
2199                 c 
= strescseq
.buf
[i
] & 0xff; 
2202                 } else if(isprint(c
)) { 
2204                 } else if(c 
== '\n') { 
2206                 } else if(c 
== '\r') { 
2208                 } else if(c 
== 0x1b) { 
2211                         printf("(%02x)", c
); 
2219         memset(&strescseq
, 0, sizeof(strescseq
)); 
2223 tprinter(char *s
, size_t len
) { 
2224         if(iofd 
!= -1 && xwrite(iofd
, s
, len
) < 0) { 
2225                 fprintf(stderr
, "Error writing in %s:%s\n", 
2226                         opt_io
, strerror(errno
)); 
2233 toggleprinter(const Arg 
*arg
) { 
2234         term
.mode 
^= MODE_PRINT
; 
2238 printscreen(const Arg 
*arg
) { 
2243 printsel(const Arg 
*arg
) { 
2251         if((ptr 
= getsel())) { 
2252                 tprinter(ptr
, strlen(ptr
)); 
2261         bp 
= &term
.line
[n
][0]; 
2262         end 
= &bp
[term
.col
-1]; 
2263         while(end 
> bp 
&& !strcmp(" ", end
->c
)) 
2265         if(bp 
!= end 
|| strcmp(bp
->c
, " ")) { 
2266                 for( ;bp 
<= end
; ++bp
) 
2267                         tprinter(bp
->c
, strlen(bp
->c
)); 
2276         for(i 
= 0; i 
< term
.row
; ++i
) 
2285                 while(x 
< term
.col 
&& n
--) 
2286                         for(++x
; x 
< term
.col 
&& !term
.tabs
[x
]; ++x
) 
2290                         for(--x
; x 
> 0 && !term
.tabs
[x
]; --x
) 
2293         tmoveto(x
, term
.c
.y
); 
2297 techo(char *buf
, int len
) { 
2298         for(; len 
> 0; buf
++, len
--) { 
2301                 if(ISCONTROL(c
)) { /* control code */ 
2306                         } else if(c 
!= '\n' && c 
!= '\r' && c 
!= '\t') { 
2320 tdeftran(char ascii
) { 
2322         static char tbl
[][2] = { 
2323                 {'0', CS_GRAPHIC0
}, {'B', CS_USA
}, 
2327         for (bp 
= &tbl
[0]; (c 
= (*bp
)[0]) && c 
!= ascii
; ++bp
) 
2331                 fprintf(stderr
, "esc unhandled charset: ESC ( %c\n", ascii
); 
2333                 term
.trantbl
[term
.icharset
] = (*bp
)[1]; 
2337 tcontrolcode(uchar ascii
) { 
2338         static char question
[UTF_SIZ
] = "?"; 
2345                 tmoveto(term
.c
.x
-1, term
.c
.y
); 
2348                 tmoveto(0, term
.c
.y
); 
2353                 /* go to first col if the mode is set */ 
2354                 tnewline(IS_SET(MODE_CRLF
)); 
2356         case '\a':   /* BEL */ 
2357                 if(term
.esc 
& ESC_STR_END
) { 
2358                         /* backwards compatibility to xterm */ 
2361                         if(!(xw
.state 
& WIN_FOCUSED
)) 
2364                                 XBell(xw
.dpy
, bellvolume
); 
2367         case '\033': /* ESC */ 
2369                 term
.esc 
&= ~(ESC_CSI
|ESC_ALTCHARSET
|ESC_TEST
); 
2370                 term
.esc 
|= ESC_START
; 
2372         case '\016': /* SO */ 
2375         case '\017': /* SI */ 
2378         case '\032': /* SUB */ 
2379                 tsetchar(question
, &term
.c
.attr
, term
.c
.x
, term
.c
.y
); 
2380         case '\030': /* CAN */ 
2383         case '\005': /* ENQ (IGNORED) */ 
2384         case '\000': /* NUL (IGNORED) */ 
2385         case '\021': /* XON (IGNORED) */ 
2386         case '\023': /* XOFF (IGNORED) */ 
2387         case 0177:   /* DEL (IGNORED) */ 
2389         case 0x84:   /* TODO: IND */ 
2390         case 0x85:   /* TODO: NEL */ 
2391         case 0x88:   /* TODO: HTS */ 
2392         case 0x8d:   /* TODO: RI */ 
2393         case 0x8e:   /* TODO: SS2 */ 
2394         case 0x8f:   /* TODO: SS3 */ 
2395         case 0x90:   /* TODO: DCS */ 
2396         case 0x98:   /* TODO: SOS */ 
2397         case 0x9a:   /* TODO: DECID */ 
2398         case 0x9b:   /* TODO: CSI */ 
2399         case 0x9c:   /* TODO: ST */ 
2400         case 0x9d:   /* TODO: OSC */ 
2401         case 0x9e:   /* TODO: PM */ 
2402         case 0x9f:   /* TODO: APC */ 
2405         /* only CAN, SUB, \a and C1 chars interrupt a sequence */ 
2406         term
.esc 
&= ~(ESC_STR_END
|ESC_STR
); 
2412         static char E
[UTF_SIZ
] = "E"; 
2415         if(c 
== '8') { /* DEC screen alignment test. */ 
2416                 for(x 
= 0; x 
< term
.col
; ++x
) { 
2417                         for(y 
= 0; y 
< term
.row
; ++y
) 
2418                                 tsetchar(E
, &term
.c
.attr
, x
, y
); 
2424 tputc(char *c
, int len
) { 
2433                 unicodep 
= ascii 
= *c
; 
2435                 utf8decode(c
, &unicodep
, UTF_SIZ
); 
2436                 width 
= wcwidth(unicodep
); 
2437                 control 
= ISCONTROLC1(unicodep
); 
2441         if(IS_SET(MODE_PRINT
)) 
2443         control 
= ISCONTROL(unicodep
); 
2446          * STR sequence must be checked before anything else 
2447          * because it uses all following characters until it 
2448          * receives a ESC, a SUB, a ST or any other C1 control 
2451         if(term
.esc 
& ESC_STR
) { 
2453                    (ascii 
== '\a' || ascii 
== 030 || 
2454                     ascii 
== 032  || ascii 
== 033 || 
2455                     ISCONTROLC1(unicodep
))) { 
2456                         term
.esc 
&= ~(ESC_START
|ESC_STR
); 
2457                         term
.esc 
|= ESC_STR_END
; 
2458                 } else if(strescseq
.len 
+ len 
< sizeof(strescseq
.buf
) - 1) { 
2459                         memmove(&strescseq
.buf
[strescseq
.len
], c
, len
); 
2460                         strescseq
.len 
+= len
; 
2464                  * Here is a bug in terminals. If the user never sends 
2465                  * some code to stop the str or esc command, then st 
2466                  * will stop responding. But this is better than 
2467                  * silently failing with unknown characters. At least 
2468                  * then users will report back. 
2470                  * In the case users ever get fixed, here is the code: 
2481          * Actions of control codes must be performed as soon they arrive 
2482          * because they can be embedded inside a control sequence, and 
2483          * they must not cause conflicts with sequences. 
2486                 tcontrolcode(ascii
); 
2488                  * control codes are not shown ever 
2491         } else if(term
.esc 
& ESC_START
) { 
2492                 if(term
.esc 
& ESC_CSI
) { 
2493                         csiescseq
.buf
[csiescseq
.len
++] = ascii
; 
2494                         if(BETWEEN(ascii
, 0x40, 0x7E) 
2495                                         || csiescseq
.len 
>= \
 
2496                                         sizeof(csiescseq
.buf
)-1) { 
2502                 } else if(term
.esc 
& ESC_ALTCHARSET
) { 
2504                 } else if(term
.esc 
& ESC_TEST
) { 
2509                                 term
.esc 
|= ESC_CSI
; 
2512                                 term
.esc 
|= ESC_TEST
; 
2514                         case 'P': /* DCS -- Device Control String */ 
2515                         case '_': /* APC -- Application Program Command */ 
2516                         case '^': /* PM -- Privacy Message */ 
2517                         case ']': /* OSC -- Operating System Command */ 
2518                         case 'k': /* old title set compatibility */ 
2520                                 strescseq
.type 
= ascii
; 
2521                                 term
.esc 
|= ESC_STR
; 
2523                         case '(': /* set primary charset G0 */ 
2524                         case ')': /* set secondary charset G1 */ 
2525                         case '*': /* set tertiary charset G2 */ 
2526                         case '+': /* set quaternary charset G3 */ 
2527                                 term
.icharset 
= ascii 
- '('; 
2528                                 term
.esc 
|= ESC_ALTCHARSET
; 
2530                         case 'D': /* IND -- Linefeed */ 
2531                                 if(term
.c
.y 
== term
.bot
) { 
2532                                         tscrollup(term
.top
, 1); 
2534                                         tmoveto(term
.c
.x
, term
.c
.y
+1); 
2537                         case 'E': /* NEL -- Next line */ 
2538                                 tnewline(1); /* always go to first col */ 
2540                         case 'H': /* HTS -- Horizontal tab stop */ 
2541                                 term
.tabs
[term
.c
.x
] = 1; 
2543                         case 'M': /* RI -- Reverse index */ 
2544                                 if(term
.c
.y 
== term
.top
) { 
2545                                         tscrolldown(term
.top
, 1); 
2547                                         tmoveto(term
.c
.x
, term
.c
.y
-1); 
2550                         case 'Z': /* DECID -- Identify Terminal */ 
2551                                 ttywrite(VT102ID
, sizeof(VT102ID
) - 1); 
2553                         case 'c': /* RIS -- Reset to inital state */ 
2558                         case '=': /* DECPAM -- Application keypad */ 
2559                                 term
.mode 
|= MODE_APPKEYPAD
; 
2561                         case '>': /* DECPNM -- Normal keypad */ 
2562                                 term
.mode 
&= ~MODE_APPKEYPAD
; 
2564                         case '7': /* DECSC -- Save Cursor */ 
2565                                 tcursor(CURSOR_SAVE
); 
2567                         case '8': /* DECRC -- Restore Cursor */ 
2568                                 tcursor(CURSOR_LOAD
); 
2570                         case '\\': /* ST -- String Terminator */ 
2571                                 if(term
.esc 
& ESC_STR_END
) 
2575                                 fprintf(stderr
, "erresc: unknown sequence ESC 0x%02X '%c'\n", 
2576                                         (uchar
) ascii
, isprint(ascii
)? ascii
:'.'); 
2582                  * All characters which form part of a sequence are not 
2587         if(sel
.ob
.x 
!= -1 && BETWEEN(term
.c
.y
, sel
.ob
.y
, sel
.oe
.y
)) 
2590         gp 
= &term
.line
[term
.c
.y
][term
.c
.x
]; 
2591         if(IS_SET(MODE_WRAP
) && (term
.c
.state 
& CURSOR_WRAPNEXT
)) { 
2592                 gp
->mode 
|= ATTR_WRAP
; 
2596         if(IS_SET(MODE_INSERT
) && term
.c
.x
+1 < term
.col
) 
2597                 memmove(gp
+1, gp
, (term
.col 
- term
.c
.x 
- 1) * sizeof(Glyph
)); 
2599         if(term
.c
.x
+width 
> term
.col
) 
2602         tsetchar(c
, &term
.c
.attr
, term
.c
.x
, term
.c
.y
); 
2605                 gp
->mode 
|= ATTR_WIDE
; 
2606                 if(term
.c
.x
+1 < term
.col
) { 
2608                         gp
[1].mode 
= ATTR_WDUMMY
; 
2611         if(term
.c
.x
+width 
< term
.col
) { 
2612                 tmoveto(term
.c
.x
+width
, term
.c
.y
); 
2614                 term
.c
.state 
|= CURSOR_WRAPNEXT
; 
2619 tresize(int col
, int row
) { 
2621         int minrow 
= MIN(row
, term
.row
); 
2622         int mincol 
= MIN(col
, term
.col
); 
2623         int slide 
= term
.c
.y 
- row 
+ 1; 
2628         if(col 
< 1 || row 
< 1) 
2631         /* free unneeded rows */ 
2635                  * slide screen to keep cursor where we expect it - 
2636                  * tscrollup would work here, but we can optimize to 
2637                  * memmove because we're freeing the earlier lines 
2639                 for(/* i = 0 */; i 
< slide
; i
++) { 
2643                 memmove(term
.line
, term
.line 
+ slide
, row 
* sizeof(Line
)); 
2644                 memmove(term
.alt
, term
.alt 
+ slide
, row 
* sizeof(Line
)); 
2646         for(i 
+= row
; i 
< term
.row
; i
++) { 
2651         /* resize to new height */ 
2652         term
.line 
= xrealloc(term
.line
, row 
* sizeof(Line
)); 
2653         term
.alt  
= xrealloc(term
.alt
,  row 
* sizeof(Line
)); 
2654         term
.dirty 
= xrealloc(term
.dirty
, row 
* sizeof(*term
.dirty
)); 
2655         term
.tabs 
= xrealloc(term
.tabs
, col 
* sizeof(*term
.tabs
)); 
2657         /* resize each row to new width, zero-pad if needed */ 
2658         for(i 
= 0; i 
< minrow
; i
++) { 
2660                 term
.line
[i
] = xrealloc(term
.line
[i
], col 
* sizeof(Glyph
)); 
2661                 term
.alt
[i
]  = xrealloc(term
.alt
[i
],  col 
* sizeof(Glyph
)); 
2664         /* allocate any new rows */ 
2665         for(/* i == minrow */; i 
< row
; i
++) { 
2667                 term
.line
[i
] = xmalloc(col 
* sizeof(Glyph
)); 
2668                 term
.alt
[i
] = xmalloc(col 
* sizeof(Glyph
)); 
2670         if(col 
> term
.col
) { 
2671                 bp 
= term
.tabs 
+ term
.col
; 
2673                 memset(bp
, 0, sizeof(*term
.tabs
) * (col 
- term
.col
)); 
2674                 while(--bp 
> term
.tabs 
&& !*bp
) 
2676                 for(bp 
+= tabspaces
; bp 
< term
.tabs 
+ col
; bp 
+= tabspaces
) 
2679         /* update terminal size */ 
2682         /* reset scrolling region */ 
2683         tsetscroll(0, row
-1); 
2684         /* make use of the LIMIT in tmoveto */ 
2685         tmoveto(term
.c
.x
, term
.c
.y
); 
2686         /* Clearing both screens */ 
2690                 if(mincol 
< col 
&& 0 < minrow
) { 
2691                         tclearregion(mincol
, 0, col 
- 1, minrow 
- 1); 
2693                 if(0 < col 
&& minrow 
< row
) { 
2694                         tclearregion(0, minrow
, col 
- 1, row 
- 1); 
2697                 tcursor(CURSOR_LOAD
); 
2698         } while(orig 
!= term
.line
); 
2705 xresize(int col
, int row
) { 
2706         xw
.tw 
= MAX(1, col 
* xw
.cw
); 
2707         xw
.th 
= MAX(1, row 
* xw
.ch
); 
2709         XFreePixmap(xw
.dpy
, xw
.buf
); 
2710         xw
.buf 
= XCreatePixmap(xw
.dpy
, xw
.win
, xw
.w
, xw
.h
, 
2711                         DefaultDepth(xw
.dpy
, xw
.scr
)); 
2712         XftDrawChange(xw
.draw
, xw
.buf
); 
2713         xclear(0, 0, xw
.w
, xw
.h
); 
2716 static inline ushort
 
2717 sixd_to_16bit(int x
) { 
2718         return x 
== 0 ? 0 : 0x3737 + 0x2828 * x
; 
2724         XRenderColor color 
= { .alpha 
= 0xffff }; 
2729                 for (cp 
= dc
.col
; cp 
< dc
.col 
+ LEN(dc
.col
); ++cp
) 
2730                         XftColorFree(xw
.dpy
, xw
.vis
, xw
.cmap
, cp
); 
2733         /* load colors [0-15] and [256-LEN(colorname)] (config.h) */ 
2734         for(i 
= 0; i 
< LEN(colorname
); i
++) { 
2737                 if(!XftColorAllocName(xw
.dpy
, xw
.vis
, xw
.cmap
, colorname
[i
], &dc
.col
[i
])) { 
2738                         die("Could not allocate color '%s'\n", colorname
[i
]); 
2742         /* load colors [16-231] ; same colors as xterm */ 
2743         for(i 
= 16; i 
< 6*6*6+16; i
++) { 
2744                 color
.red   
= sixd_to_16bit( ((i
-16)/36)%6 ); 
2745                 color
.green 
= sixd_to_16bit( ((i
-16)/6) %6 ); 
2746                 color
.blue  
= sixd_to_16bit( ((i
-16)/1) %6 ); 
2747                 if(!XftColorAllocValue(xw
.dpy
, xw
.vis
, xw
.cmap
, &color
, &dc
.col
[i
])) 
2748                         die("Could not allocate color %d\n", i
); 
2751         /* load colors [232-255] ; grayscale */ 
2752         for(; i 
< 256; i
++) { 
2753                 color
.red 
= color
.green 
= color
.blue 
= 0x0808 + 0x0a0a * (i
-(6*6*6+16)); 
2754                 if(!XftColorAllocValue(xw
.dpy
, xw
.vis
, xw
.cmap
, &color
, &dc
.col
[i
])) 
2755                         die("Could not allocate color %d\n", i
); 
2761 xsetcolorname(int x
, const char *name
) { 
2762         XRenderColor color 
= { .alpha 
= 0xffff }; 
2765         if(!BETWEEN(x
, 0, LEN(colorname
))) 
2769                 if(BETWEEN(x
, 16, 16 + 215)) { /* 256 color */ 
2770                         color
.red   
= sixd_to_16bit( ((x
-16)/36)%6 ); 
2771                         color
.green 
= sixd_to_16bit( ((x
-16)/6) %6 ); 
2772                         color
.blue  
= sixd_to_16bit( ((x
-16)/1) %6 ); 
2773                         if(!XftColorAllocValue(xw
.dpy
, xw
.vis
, 
2774                                                 xw
.cmap
, &color
, &ncolor
)) { 
2778                         XftColorFree(xw
.dpy
, xw
.vis
, xw
.cmap
, &dc
.col
[x
]); 
2781                 } else if(BETWEEN(x
, 16 + 216, 255)) { /* greyscale */ 
2782                         color
.red 
= color
.green 
= color
.blue 
= \
 
2783                                     0x0808 + 0x0a0a * (x 
- (16 + 216)); 
2784                         if(!XftColorAllocValue(xw
.dpy
, xw
.vis
, 
2785                                                 xw
.cmap
, &color
, &ncolor
)) { 
2789                         XftColorFree(xw
.dpy
, xw
.vis
, xw
.cmap
, &dc
.col
[x
]); 
2792                 } else { /* system colors */ 
2793                         name 
= colorname
[x
]; 
2796         if(!XftColorAllocName(xw
.dpy
, xw
.vis
, xw
.cmap
, name
, &ncolor
)) 
2799         XftColorFree(xw
.dpy
, xw
.vis
, xw
.cmap
, &dc
.col
[x
]); 
2805 xtermclear(int col1
, int row1
, int col2
, int row2
) { 
2806         XftDrawRect(xw
.draw
, 
2807                         &dc
.col
[IS_SET(MODE_REVERSE
) ? defaultfg 
: defaultbg
], 
2808                         borderpx 
+ col1 
* xw
.cw
, 
2809                         borderpx 
+ row1 
* xw
.ch
, 
2810                         (col2
-col1
+1) * xw
.cw
, 
2811                         (row2
-row1
+1) * xw
.ch
); 
2815  * Absolute coordinates. 
2818 xclear(int x1
, int y1
, int x2
, int y2
) { 
2819         XftDrawRect(xw
.draw
, 
2820                         &dc
.col
[IS_SET(MODE_REVERSE
)? defaultfg 
: defaultbg
], 
2821                         x1
, y1
, x2
-x1
, y2
-y1
); 
2826         XClassHint 
class = {opt_class 
? opt_class 
: termname
, termname
}; 
2827         XWMHints wm 
= {.flags 
= InputHint
, .input 
= 1}; 
2828         XSizeHints 
*sizeh 
= NULL
; 
2830         sizeh 
= XAllocSizeHints(); 
2832         sizeh
->flags 
= PSize 
| PResizeInc 
| PBaseSize
; 
2833         sizeh
->height 
= xw
.h
; 
2834         sizeh
->width 
= xw
.w
; 
2835         sizeh
->height_inc 
= xw
.ch
; 
2836         sizeh
->width_inc 
= xw
.cw
; 
2837         sizeh
->base_height 
= 2 * borderpx
; 
2838         sizeh
->base_width 
= 2 * borderpx
; 
2839         if(xw
.isfixed 
== True
) { 
2840                 sizeh
->flags 
|= PMaxSize 
| PMinSize
; 
2841                 sizeh
->min_width 
= sizeh
->max_width 
= xw
.w
; 
2842                 sizeh
->min_height 
= sizeh
->max_height 
= xw
.h
; 
2844         if(xw
.gm 
& (XValue
|YValue
)) { 
2845                 sizeh
->flags 
|= USPosition 
| PWinGravity
; 
2848                 sizeh
->win_gravity 
= xgeommasktogravity(xw
.gm
); 
2851         XSetWMProperties(xw
.dpy
, xw
.win
, NULL
, NULL
, NULL
, 0, sizeh
, &wm
, 
2857 xgeommasktogravity(int mask
) { 
2858         switch(mask 
& (XNegative
|YNegative
)) { 
2860                 return NorthWestGravity
; 
2862                 return NorthEastGravity
; 
2864                 return SouthWestGravity
; 
2866         return SouthEastGravity
; 
2870 xloadfont(Font 
*f
, FcPattern 
*pattern
) { 
2874         match 
= FcFontMatch(NULL
, pattern
, &result
); 
2878         if(!(f
->match 
= XftFontOpenPattern(xw
.dpy
, match
))) { 
2879                 FcPatternDestroy(match
); 
2884         f
->pattern 
= FcPatternDuplicate(pattern
); 
2886         f
->ascent 
= f
->match
->ascent
; 
2887         f
->descent 
= f
->match
->descent
; 
2889         f
->rbearing 
= f
->match
->max_advance_width
; 
2891         f
->height 
= f
->ascent 
+ f
->descent
; 
2892         f
->width 
= f
->lbearing 
+ f
->rbearing
; 
2898 xloadfonts(char *fontstr
, double fontsize
) { 
2900         FcResult r_sz
, r_psz
; 
2903         if(fontstr
[0] == '-') { 
2904                 pattern 
= XftXlfdParse(fontstr
, False
, False
); 
2906                 pattern 
= FcNameParse((FcChar8 
*)fontstr
); 
2910                 die("st: can't open font %s\n", fontstr
); 
2913                 FcPatternDel(pattern
, FC_PIXEL_SIZE
); 
2914                 FcPatternDel(pattern
, FC_SIZE
); 
2915                 FcPatternAddDouble(pattern
, FC_PIXEL_SIZE
, (double)fontsize
); 
2916                 usedfontsize 
= fontsize
; 
2918                 r_psz 
= FcPatternGetDouble(pattern
, FC_PIXEL_SIZE
, 0, &fontval
); 
2919                 r_sz 
= FcPatternGetDouble(pattern
, FC_SIZE
, 0, &fontval
); 
2920                 if(r_psz 
== FcResultMatch
) { 
2921                         usedfontsize 
= fontval
; 
2922                 } else if(r_sz 
== FcResultMatch
) { 
2926                          * Default font size is 12, if none given. This is to 
2927                          * have a known usedfontsize value. 
2929                         FcPatternAddDouble(pattern
, FC_PIXEL_SIZE
, 12); 
2934         FcConfigSubstitute(0, pattern
, FcMatchPattern
); 
2935         FcDefaultSubstitute(pattern
); 
2937         if(xloadfont(&dc
.font
, pattern
)) 
2938                 die("st: can't open font %s\n", fontstr
); 
2940         if(usedfontsize 
< 0) { 
2941                 FcPatternGetDouble(dc
.font
.match
->pattern
, 
2942                                    FC_PIXEL_SIZE
, 0, &fontval
); 
2943                 usedfontsize 
= fontval
; 
2946         /* Setting character width and height. */ 
2947         xw
.cw 
= CEIL(dc
.font
.width 
* cwscale
); 
2948         xw
.ch 
= CEIL(dc
.font
.height 
* chscale
); 
2950         FcPatternDel(pattern
, FC_SLANT
); 
2951         FcPatternAddInteger(pattern
, FC_SLANT
, FC_SLANT_ITALIC
); 
2952         if(xloadfont(&dc
.ifont
, pattern
)) 
2953                 die("st: can't open font %s\n", fontstr
); 
2955         FcPatternDel(pattern
, FC_WEIGHT
); 
2956         FcPatternAddInteger(pattern
, FC_WEIGHT
, FC_WEIGHT_BOLD
); 
2957         if(xloadfont(&dc
.ibfont
, pattern
)) 
2958                 die("st: can't open font %s\n", fontstr
); 
2960         FcPatternDel(pattern
, FC_SLANT
); 
2961         FcPatternAddInteger(pattern
, FC_SLANT
, FC_SLANT_ROMAN
); 
2962         if(xloadfont(&dc
.bfont
, pattern
)) 
2963                 die("st: can't open font %s\n", fontstr
); 
2965         FcPatternDestroy(pattern
); 
2969 xloadfontset(Font 
*f
) { 
2972         if(!(f
->set 
= FcFontSort(0, f
->pattern
, FcTrue
, 0, &result
))) 
2978 xunloadfont(Font 
*f
) { 
2979         XftFontClose(xw
.dpy
, f
->match
); 
2980         FcPatternDestroy(f
->pattern
); 
2982                 FcFontSetDestroy(f
->set
); 
2986 xunloadfonts(void) { 
2987         /* Free the loaded fonts in the font cache.  */ 
2989                 XftFontClose(xw
.dpy
, frc
[--frclen
].font
); 
2991         xunloadfont(&dc
.font
); 
2992         xunloadfont(&dc
.bfont
); 
2993         xunloadfont(&dc
.ifont
); 
2994         xunloadfont(&dc
.ibfont
); 
2998 xzoom(const Arg 
*arg
) { 
3000         xloadfonts(usedfont
, usedfontsize 
+ arg
->i
); 
3010         pid_t thispid 
= getpid(); 
3012         if(!(xw
.dpy 
= XOpenDisplay(NULL
))) 
3013                 die("Can't open display\n"); 
3014         xw
.scr 
= XDefaultScreen(xw
.dpy
); 
3015         xw
.vis 
= XDefaultVisual(xw
.dpy
, xw
.scr
); 
3019                 die("Could not init fontconfig.\n"); 
3021         usedfont 
= (opt_font 
== NULL
)? font 
: opt_font
; 
3022         xloadfonts(usedfont
, 0); 
3025         xw
.cmap 
= XDefaultColormap(xw
.dpy
, xw
.scr
); 
3028         /* adjust fixed window geometry */ 
3029         xw
.w 
= 2 * borderpx 
+ term
.col 
* xw
.cw
; 
3030         xw
.h 
= 2 * borderpx 
+ term
.row 
* xw
.ch
; 
3031         if(xw
.gm 
& XNegative
) 
3032                 xw
.l 
+= DisplayWidth(xw
.dpy
, xw
.scr
) - xw
.w 
- 2; 
3033         if(xw
.gm 
& YNegative
) 
3034                 xw
.t 
+= DisplayWidth(xw
.dpy
, xw
.scr
) - xw
.h 
- 2; 
3037         xw
.attrs
.background_pixel 
= dc
.col
[defaultbg
].pixel
; 
3038         xw
.attrs
.border_pixel 
= dc
.col
[defaultbg
].pixel
; 
3039         xw
.attrs
.bit_gravity 
= NorthWestGravity
; 
3040         xw
.attrs
.event_mask 
= FocusChangeMask 
| KeyPressMask
 
3041                 | ExposureMask 
| VisibilityChangeMask 
| StructureNotifyMask
 
3042                 | ButtonMotionMask 
| ButtonPressMask 
| ButtonReleaseMask
; 
3043         xw
.attrs
.colormap 
= xw
.cmap
; 
3045         parent 
= opt_embed 
? strtol(opt_embed
, NULL
, 0) : \
 
3046                         XRootWindow(xw
.dpy
, xw
.scr
); 
3047         xw
.win 
= XCreateWindow(xw
.dpy
, parent
, xw
.l
, xw
.t
, 
3048                         xw
.w
, xw
.h
, 0, XDefaultDepth(xw
.dpy
, xw
.scr
), InputOutput
, 
3049                         xw
.vis
, CWBackPixel 
| CWBorderPixel 
| CWBitGravity
 
3050                         | CWEventMask 
| CWColormap
, &xw
.attrs
); 
3052         memset(&gcvalues
, 0, sizeof(gcvalues
)); 
3053         gcvalues
.graphics_exposures 
= False
; 
3054         dc
.gc 
= XCreateGC(xw
.dpy
, parent
, GCGraphicsExposures
, 
3056         xw
.buf 
= XCreatePixmap(xw
.dpy
, xw
.win
, xw
.w
, xw
.h
, 
3057                         DefaultDepth(xw
.dpy
, xw
.scr
)); 
3058         XSetForeground(xw
.dpy
, dc
.gc
, dc
.col
[defaultbg
].pixel
); 
3059         XFillRectangle(xw
.dpy
, xw
.buf
, dc
.gc
, 0, 0, xw
.w
, xw
.h
); 
3061         /* Xft rendering context */ 
3062         xw
.draw 
= XftDrawCreate(xw
.dpy
, xw
.buf
, xw
.vis
, xw
.cmap
); 
3065         if((xw
.xim 
= XOpenIM(xw
.dpy
, NULL
, NULL
, NULL
)) == NULL
) { 
3066                 XSetLocaleModifiers("@im=local"); 
3067                 if((xw
.xim 
=  XOpenIM(xw
.dpy
, NULL
, NULL
, NULL
)) == NULL
) { 
3068                         XSetLocaleModifiers("@im="); 
3069                         if((xw
.xim 
= XOpenIM(xw
.dpy
, 
3070                                         NULL
, NULL
, NULL
)) == NULL
) { 
3071                                 die("XOpenIM failed. Could not open input" 
3076         xw
.xic 
= XCreateIC(xw
.xim
, XNInputStyle
, XIMPreeditNothing
 
3077                                            | XIMStatusNothing
, XNClientWindow
, xw
.win
, 
3078                                            XNFocusWindow
, xw
.win
, NULL
); 
3080                 die("XCreateIC failed. Could not obtain input method.\n"); 
3082         /* white cursor, black outline */ 
3083         cursor 
= XCreateFontCursor(xw
.dpy
, XC_xterm
); 
3084         XDefineCursor(xw
.dpy
, xw
.win
, cursor
); 
3085         XRecolorCursor(xw
.dpy
, cursor
, 
3086                 &(XColor
){.red 
= 0xffff, .green 
= 0xffff, .blue 
= 0xffff}, 
3087                 &(XColor
){.red 
= 0x0000, .green 
= 0x0000, .blue 
= 0x0000}); 
3089         xw
.xembed 
= XInternAtom(xw
.dpy
, "_XEMBED", False
); 
3090         xw
.wmdeletewin 
= XInternAtom(xw
.dpy
, "WM_DELETE_WINDOW", False
); 
3091         xw
.netwmname 
= XInternAtom(xw
.dpy
, "_NET_WM_NAME", False
); 
3092         XSetWMProtocols(xw
.dpy
, xw
.win
, &xw
.wmdeletewin
, 1); 
3094         xw
.netwmpid 
= XInternAtom(xw
.dpy
, "_NET_WM_PID", False
); 
3095         XChangeProperty(xw
.dpy
, xw
.win
, xw
.netwmpid
, XA_CARDINAL
, 32, 
3096                         PropModeReplace
, (uchar 
*)&thispid
, 1); 
3099         XMapWindow(xw
.dpy
, xw
.win
); 
3101         XSync(xw
.dpy
, False
); 
3105 xdraws(char *s
, Glyph base
, int x
, int y
, int charlen
, int bytelen
) { 
3106         int winx 
= borderpx 
+ x 
* xw
.cw
, winy 
= borderpx 
+ y 
* xw
.ch
, 
3107             width 
= charlen 
* xw
.cw
, xp
, i
; 
3109         int u8fl
, u8fblen
, u8cblen
, doesexist
; 
3112         Font 
*font 
= &dc
.font
; 
3114         FcPattern 
*fcpattern
, *fontpattern
; 
3115         FcFontSet 
*fcsets
[] = { NULL 
}; 
3116         FcCharSet 
*fccharset
; 
3117         Color 
*fg
, *bg
, *temp
, revfg
, revbg
, truefg
, truebg
; 
3118         XRenderColor colfg
, colbg
; 
3122         frcflags 
= FRC_NORMAL
; 
3124         if(base
.mode 
& ATTR_ITALIC
) { 
3125                 if(base
.fg 
== defaultfg
) 
3126                         base
.fg 
= defaultitalic
; 
3128                 frcflags 
= FRC_ITALIC
; 
3129         } else if((base
.mode 
& ATTR_ITALIC
) && (base
.mode 
& ATTR_BOLD
)) { 
3130                 if(base
.fg 
== defaultfg
) 
3131                         base
.fg 
= defaultitalic
; 
3133                 frcflags 
= FRC_ITALICBOLD
; 
3134         } else if(base
.mode 
& ATTR_UNDERLINE
) { 
3135                 if(base
.fg 
== defaultfg
) 
3136                         base
.fg 
= defaultunderline
; 
3139         if(IS_TRUECOL(base
.fg
)) { 
3140                 colfg
.alpha 
= 0xffff; 
3141                 colfg
.red 
= TRUERED(base
.fg
); 
3142                 colfg
.green 
= TRUEGREEN(base
.fg
); 
3143                 colfg
.blue 
= TRUEBLUE(base
.fg
); 
3144                 XftColorAllocValue(xw
.dpy
, xw
.vis
, xw
.cmap
, &colfg
, &truefg
); 
3147                 fg 
= &dc
.col
[base
.fg
]; 
3150         if(IS_TRUECOL(base
.bg
)) { 
3151                 colbg
.alpha 
= 0xffff; 
3152                 colbg
.green 
= TRUEGREEN(base
.bg
); 
3153                 colbg
.red 
= TRUERED(base
.bg
); 
3154                 colbg
.blue 
= TRUEBLUE(base
.bg
); 
3155                 XftColorAllocValue(xw
.dpy
, xw
.vis
, xw
.cmap
, &colbg
, &truebg
); 
3158                 bg 
= &dc
.col
[base
.bg
]; 
3161         if(base
.mode 
& ATTR_BOLD
) { 
3163                  * change basic system colors [0-7] 
3164                  * to bright system colors [8-15] 
3166                 if(BETWEEN(base
.fg
, 0, 7)) 
3167                         fg 
= &dc
.col
[base
.fg 
+ 8]; 
3169                 if(base
.mode 
& ATTR_ITALIC
) { 
3171                         frcflags 
= FRC_ITALICBOLD
; 
3174                         frcflags 
= FRC_BOLD
; 
3178         if(IS_SET(MODE_REVERSE
)) { 
3179                 if(fg 
== &dc
.col
[defaultfg
]) { 
3180                         fg 
= &dc
.col
[defaultbg
]; 
3182                         colfg
.red 
= ~fg
->color
.red
; 
3183                         colfg
.green 
= ~fg
->color
.green
; 
3184                         colfg
.blue 
= ~fg
->color
.blue
; 
3185                         colfg
.alpha 
= fg
->color
.alpha
; 
3186                         XftColorAllocValue(xw
.dpy
, xw
.vis
, xw
.cmap
, &colfg
, 
3191                 if(bg 
== &dc
.col
[defaultbg
]) { 
3192                         bg 
= &dc
.col
[defaultfg
]; 
3194                         colbg
.red 
= ~bg
->color
.red
; 
3195                         colbg
.green 
= ~bg
->color
.green
; 
3196                         colbg
.blue 
= ~bg
->color
.blue
; 
3197                         colbg
.alpha 
= bg
->color
.alpha
; 
3198                         XftColorAllocValue(xw
.dpy
, xw
.vis
, xw
.cmap
, &colbg
, 
3204         if(base
.mode 
& ATTR_REVERSE
) { 
3210         if(base
.mode 
& ATTR_BLINK 
&& term
.mode 
& MODE_BLINK
) 
3213         /* Intelligent cleaning up of the borders. */ 
3215                 xclear(0, (y 
== 0)? 0 : winy
, borderpx
, 
3216                         winy 
+ xw
.ch 
+ ((y 
>= term
.row
-1)? xw
.h 
: 0)); 
3218         if(x 
+ charlen 
>= term
.col
) { 
3219                 xclear(winx 
+ width
, (y 
== 0)? 0 : winy
, xw
.w
, 
3220                         ((y 
>= term
.row
-1)? xw
.h 
: (winy 
+ xw
.ch
))); 
3223                 xclear(winx
, 0, winx 
+ width
, borderpx
); 
3225                 xclear(winx
, winy 
+ xw
.ch
, winx 
+ width
, xw
.h
); 
3227         /* Clean up the region we want to draw to. */ 
3228         XftDrawRect(xw
.draw
, bg
, winx
, winy
, width
, xw
.ch
); 
3230         /* Set the clip region because Xft is sometimes dirty. */ 
3235         XftDrawSetClipRectangles(xw
.draw
, winx
, winy
, &r
, 1); 
3237         for(xp 
= winx
; bytelen 
> 0;) { 
3239                  * Search for the range in the to be printed string of glyphs 
3240                  * that are in the main font. Then print that range. If 
3241                  * some glyph is found that is not in the font, do the 
3247                 oneatatime 
= font
->width 
!= xw
.cw
; 
3250                         u8cblen 
= utf8decode(s
, &unicodep
, UTF_SIZ
); 
3254                         doesexist 
= XftCharExists(xw
.dpy
, font
->match
, unicodep
); 
3255                         if(oneatatime 
|| !doesexist 
|| bytelen 
<= 0) { 
3256                                 if(oneatatime 
|| bytelen 
<= 0) { 
3264                                         XftDrawStringUtf8(xw
.draw
, fg
, 
3266                                                         winy 
+ font
->ascent
, 
3284                 /* Search the font cache. */ 
3285                 for(i 
= 0; i 
< frclen
; i
++) { 
3286                         if(XftCharExists(xw
.dpy
, frc
[i
].font
, unicodep
) 
3287                                         && frc
[i
].flags 
== frcflags
) { 
3292                 /* Nothing was found. */ 
3296                         fcsets
[0] = font
->set
; 
3299                          * Nothing was found in the cache. Now use 
3300                          * some dozen of Fontconfig calls to get the 
3301                          * font for one single character. 
3303                          * Xft and fontconfig are design failures. 
3305                         fcpattern 
= FcPatternDuplicate(font
->pattern
); 
3306                         fccharset 
= FcCharSetCreate(); 
3308                         FcCharSetAddChar(fccharset
, unicodep
); 
3309                         FcPatternAddCharSet(fcpattern
, FC_CHARSET
, 
3311                         FcPatternAddBool(fcpattern
, FC_SCALABLE
, 
3314                         FcConfigSubstitute(0, fcpattern
, 
3316                         FcDefaultSubstitute(fcpattern
); 
3318                         fontpattern 
= FcFontSetMatch(0, fcsets
, 
3319                                         FcTrue
, fcpattern
, &fcres
); 
3322                          * Overwrite or create the new cache entry. 
3324                         if(frclen 
>= LEN(frc
)) { 
3325                                 frclen 
= LEN(frc
) - 1; 
3326                                 XftFontClose(xw
.dpy
, frc
[frclen
].font
); 
3329                         frc
[frclen
].font 
= XftFontOpenPattern(xw
.dpy
, 
3331                         frc
[frclen
].flags 
= frcflags
; 
3336                         FcPatternDestroy(fcpattern
); 
3337                         FcCharSetDestroy(fccharset
); 
3340                 XftDrawStringUtf8(xw
.draw
, fg
, frc
[i
].font
, 
3341                                 xp
, winy 
+ frc
[i
].font
->ascent
, 
3342                                 (FcChar8 
*)u8c
, u8cblen
); 
3344                 xp 
+= xw
.cw 
* wcwidth(unicodep
); 
3348          * This is how the loop above actually should be. Why does the 
3349          * application have to care about font details? 
3351          * I have to repeat: Xft and Fontconfig are design failures. 
3354         XftDrawStringUtf8(xw.draw, fg, font->set, winx, 
3355                         winy + font->ascent, (FcChar8 *)s, bytelen); 
3358         if(base
.mode 
& ATTR_UNDERLINE
) { 
3359                 XftDrawRect(xw
.draw
, fg
, winx
, winy 
+ font
->ascent 
+ 1, 
3363         /* Reset clip to none. */ 
3364         XftDrawSetClip(xw
.draw
, 0); 
3369         static int oldx 
= 0, oldy 
= 0; 
3370         int sl
, width
, curx
; 
3371         Glyph g 
= {{' '}, ATTR_NULL
, defaultbg
, defaultcs
}; 
3373         LIMIT(oldx
, 0, term
.col
-1); 
3374         LIMIT(oldy
, 0, term
.row
-1); 
3378         /* adjust position if in dummy */ 
3379         if(term
.line
[oldy
][oldx
].mode 
& ATTR_WDUMMY
) 
3381         if(term
.line
[term
.c
.y
][curx
].mode 
& ATTR_WDUMMY
) 
3384         memcpy(g
.c
, term
.line
[term
.c
.y
][term
.c
.x
].c
, UTF_SIZ
); 
3386         /* remove the old cursor */ 
3387         sl 
= utf8len(term
.line
[oldy
][oldx
].c
); 
3388         width 
= (term
.line
[oldy
][oldx
].mode 
& ATTR_WIDE
)? 2 : 1; 
3389         xdraws(term
.line
[oldy
][oldx
].c
, term
.line
[oldy
][oldx
], oldx
, 
3392         /* draw the new one */ 
3393         if(!(IS_SET(MODE_HIDE
))) { 
3394                 if(xw
.state 
& WIN_FOCUSED
) { 
3395                         if(IS_SET(MODE_REVERSE
)) { 
3396                                 g
.mode 
|= ATTR_REVERSE
; 
3402                         width 
= (term
.line
[term
.c
.y
][curx
].mode 
& ATTR_WIDE
)\
 
3404                         xdraws(g
.c
, g
, term
.c
.x
, term
.c
.y
, width
, sl
); 
3406                         XftDrawRect(xw
.draw
, &dc
.col
[defaultcs
], 
3407                                         borderpx 
+ curx 
* xw
.cw
, 
3408                                         borderpx 
+ term
.c
.y 
* xw
.ch
, 
3410                         XftDrawRect(xw
.draw
, &dc
.col
[defaultcs
], 
3411                                         borderpx 
+ curx 
* xw
.cw
, 
3412                                         borderpx 
+ term
.c
.y 
* xw
.ch
, 
3414                         XftDrawRect(xw
.draw
, &dc
.col
[defaultcs
], 
3415                                         borderpx 
+ (curx 
+ 1) * xw
.cw 
- 1, 
3416                                         borderpx 
+ term
.c
.y 
* xw
.ch
, 
3418                         XftDrawRect(xw
.draw
, &dc
.col
[defaultcs
], 
3419                                         borderpx 
+ curx 
* xw
.cw
, 
3420                                         borderpx 
+ (term
.c
.y 
+ 1) * xw
.ch 
- 1, 
3423                 oldx 
= curx
, oldy 
= term
.c
.y
; 
3429 xsettitle(char *p
) { 
3432         Xutf8TextListToTextProperty(xw
.dpy
, &p
, 1, XUTF8StringStyle
, 
3434         XSetWMName(xw
.dpy
, xw
.win
, &prop
); 
3435         XSetTextProperty(xw
.dpy
, xw
.win
, &prop
, xw
.netwmname
); 
3441         xsettitle(opt_title 
? opt_title 
: "st"); 
3445 redraw(int timeout
) { 
3446         struct timespec tv 
= {0, timeout 
* 1000}; 
3452                 nanosleep(&tv
, NULL
); 
3453                 XSync(xw
.dpy
, False
); /* necessary for a good tput flash */ 
3459         drawregion(0, 0, term
.col
, term
.row
); 
3460         XCopyArea(xw
.dpy
, xw
.buf
, xw
.win
, dc
.gc
, 0, 0, xw
.w
, 
3462         XSetForeground(xw
.dpy
, dc
.gc
, 
3463                         dc
.col
[IS_SET(MODE_REVERSE
)? 
3464                                 defaultfg 
: defaultbg
].pixel
); 
3468 drawregion(int x1
, int y1
, int x2
, int y2
) { 
3469         int ic
, ib
, x
, y
, ox
, sl
; 
3471         char buf
[DRAW_BUF_SIZ
]; 
3472         bool ena_sel 
= sel
.ob
.x 
!= -1 && sel
.alt 
== IS_SET(MODE_ALTSCREEN
); 
3475         if(!(xw
.state 
& WIN_VISIBLE
)) 
3478         for(y 
= y1
; y 
< y2
; y
++) { 
3482                 xtermclear(0, y
, term
.col
, y
); 
3484                 base 
= term
.line
[y
][0]; 
3486                 for(x 
= x1
; x 
< x2
; x
++) { 
3487                         new = term
.line
[y
][x
]; 
3488                         if(new.mode 
== ATTR_WDUMMY
) 
3490                         if(ena_sel 
&& selected(x
, y
)) 
3491                                 new.mode 
^= ATTR_REVERSE
; 
3492                         if(ib 
> 0 && (ATTRCMP(base
, new) 
3493                                         || ib 
>= DRAW_BUF_SIZ
-UTF_SIZ
)) { 
3494                                 xdraws(buf
, base
, ox
, y
, ic
, ib
); 
3502                         sl 
= utf8decode(new.c
, &unicodep
, UTF_SIZ
); 
3503                         memcpy(buf
+ib
, new.c
, sl
); 
3505                         ic 
+= (new.mode 
& ATTR_WIDE
)? 2 : 1; 
3508                         xdraws(buf
, base
, ox
, y
, ic
, ib
); 
3514 expose(XEvent 
*ev
) { 
3515         XExposeEvent 
*e 
= &ev
->xexpose
; 
3517         if(xw
.state 
& WIN_REDRAW
) { 
3519                         xw
.state 
&= ~WIN_REDRAW
; 
3525 visibility(XEvent 
*ev
) { 
3526         XVisibilityEvent 
*e 
= &ev
->xvisibility
; 
3528         if(e
->state 
== VisibilityFullyObscured
) { 
3529                 xw
.state 
&= ~WIN_VISIBLE
; 
3530         } else if(!(xw
.state 
& WIN_VISIBLE
)) { 
3531                 /* need a full redraw for next Expose, not just a buf copy */ 
3532                 xw
.state 
|= WIN_VISIBLE 
| WIN_REDRAW
; 
3538         xw
.state 
&= ~WIN_VISIBLE
; 
3542 xsetpointermotion(int set
) { 
3543         MODBIT(xw
.attrs
.event_mask
, set
, PointerMotionMask
); 
3544         XChangeWindowAttributes(xw
.dpy
, xw
.win
, CWEventMask
, &xw
.attrs
); 
3548 xseturgency(int add
) { 
3549         XWMHints 
*h 
= XGetWMHints(xw
.dpy
, xw
.win
); 
3551         MODBIT(h
->flags
, add
, XUrgencyHint
); 
3552         XSetWMHints(xw
.dpy
, xw
.win
, h
); 
3558         XFocusChangeEvent 
*e 
= &ev
->xfocus
; 
3560         if(e
->mode 
== NotifyGrab
) 
3563         if(ev
->type 
== FocusIn
) { 
3564                 XSetICFocus(xw
.xic
); 
3565                 xw
.state 
|= WIN_FOCUSED
; 
3567                 if(IS_SET(MODE_FOCUS
)) 
3568                         ttywrite("\033[I", 3); 
3570                 XUnsetICFocus(xw
.xic
); 
3571                 xw
.state 
&= ~WIN_FOCUSED
; 
3572                 if(IS_SET(MODE_FOCUS
)) 
3573                         ttywrite("\033[O", 3); 
3578 match(uint mask
, uint state
) { 
3579         return mask 
== XK_ANY_MOD 
|| mask 
== (state 
& ~ignoremod
); 
3583 numlock(const Arg 
*dummy
) { 
3588 kmap(KeySym k
, uint state
) { 
3592         /* Check for mapped keys out of X11 function keys. */ 
3593         for(i 
= 0; i 
< LEN(mappedkeys
); i
++) { 
3594                 if(mappedkeys
[i
] == k
) 
3597         if(i 
== LEN(mappedkeys
)) { 
3598                 if((k 
& 0xFFFF) < 0xFD00) 
3602         for(kp 
= key
; kp 
< key 
+ LEN(key
); kp
++) { 
3606                 if(!match(kp
->mask
, state
)) 
3609                 if(IS_SET(MODE_APPKEYPAD
) ? kp
->appkey 
< 0 : kp
->appkey 
> 0) 
3611                 if(term
.numlock 
&& kp
->appkey 
== 2) 
3614                 if(IS_SET(MODE_APPCURSOR
) ? kp
->appcursor 
< 0 : kp
->appcursor 
> 0) 
3617                 if(IS_SET(MODE_CRLF
) ? kp
->crlf 
< 0 : kp
->crlf 
> 0) 
3627 kpress(XEvent 
*ev
) { 
3628         XKeyEvent 
*e 
= &ev
->xkey
; 
3630         char buf
[32], *customkey
; 
3636         if(IS_SET(MODE_KBDLOCK
)) 
3639         len 
= XmbLookupString(xw
.xic
, e
, buf
, sizeof buf
, &ksym
, &status
); 
3641         for(bp 
= shortcuts
; bp 
< shortcuts 
+ LEN(shortcuts
); bp
++) { 
3642                 if(ksym 
== bp
->keysym 
&& match(bp
->mod
, e
->state
)) { 
3643                         bp
->func(&(bp
->arg
)); 
3648         /* 2. custom keys from config.h */ 
3649         if((customkey 
= kmap(ksym
, e
->state
))) { 
3650                 ttysend(customkey
, strlen(customkey
)); 
3654         /* 3. composed string from input method */ 
3657         if(len 
== 1 && e
->state 
& Mod1Mask
) { 
3658                 if(IS_SET(MODE_8BIT
)) { 
3661                                 len 
= utf8encode(c
, buf
, UTF_SIZ
); 
3674 cmessage(XEvent 
*e
) { 
3677          *  http://standards.freedesktop.org/xembed-spec/xembed-spec-latest.html 
3679         if(e
->xclient
.message_type 
== xw
.xembed 
&& e
->xclient
.format 
== 32) { 
3680                 if(e
->xclient
.data
.l
[1] == XEMBED_FOCUS_IN
) { 
3681                         xw
.state 
|= WIN_FOCUSED
; 
3683                 } else if(e
->xclient
.data
.l
[1] == XEMBED_FOCUS_OUT
) { 
3684                         xw
.state 
&= ~WIN_FOCUSED
; 
3686         } else if(e
->xclient
.data
.l
[0] == xw
.wmdeletewin
) { 
3687                 /* Send SIGHUP to shell */ 
3694 cresize(int width
, int height
) { 
3702         col 
= (xw
.w 
- 2 * borderpx
) / xw
.cw
; 
3703         row 
= (xw
.h 
- 2 * borderpx
) / xw
.ch
; 
3712         if(e
->xconfigure
.width 
== xw
.w 
&& e
->xconfigure
.height 
== xw
.h
) 
3715         cresize(e
->xconfigure
.width
, e
->xconfigure
.height
); 
3721         int w 
= xw
.w
, h 
= xw
.h
; 
3723         int xfd 
= XConnectionNumber(xw
.dpy
), xev
, blinkset 
= 0, dodraw 
= 0; 
3724         struct timeval drawtimeout
, *tv 
= NULL
, now
, last
, lastblink
; 
3726         /* Waiting for window mapping */ 
3728                 XNextEvent(xw
.dpy
, &ev
); 
3729                 if(ev
.type 
== ConfigureNotify
) { 
3730                         w 
= ev
.xconfigure
.width
; 
3731                         h 
= ev
.xconfigure
.height
; 
3732                 } else if(ev
.type 
== MapNotify
) { 
3740         gettimeofday(&last
, NULL
); 
3743         for(xev 
= actionfps
;;) { 
3747                 FD_SET(cmdfd
, &rfd
); 
3750                 if(select(MAX(xfd
, cmdfd
)+1, &rfd
, NULL
, NULL
, tv
) < 0) { 
3753                         die("select failed: %s\n", strerror(errno
)); 
3755                 if(FD_ISSET(cmdfd
, &rfd
)) { 
3758                                 blinkset 
= tattrset(ATTR_BLINK
); 
3760                                         MODBIT(term
.mode
, 0, MODE_BLINK
); 
3764                 if(FD_ISSET(xfd
, &rfd
)) 
3767                 gettimeofday(&now
, NULL
); 
3768                 drawtimeout
.tv_sec 
= 0; 
3769                 drawtimeout
.tv_usec 
= (1000/xfps
) * 1000; 
3773                 if(blinktimeout 
&& TIMEDIFF(now
, lastblink
) > blinktimeout
) { 
3774                         tsetdirtattr(ATTR_BLINK
); 
3775                         term
.mode 
^= MODE_BLINK
; 
3779                 deltatime 
= TIMEDIFF(now
, last
); 
3780                 if(deltatime 
> (xev
? (1000/xfps
) : (1000/actionfps
)) 
3787                         while(XPending(xw
.dpy
)) { 
3788                                 XNextEvent(xw
.dpy
, &ev
); 
3789                                 if(XFilterEvent(&ev
, None
)) 
3791                                 if(handler
[ev
.type
]) 
3792                                         (handler
[ev
.type
])(&ev
); 
3798                         if(xev 
&& !FD_ISSET(xfd
, &rfd
)) 
3800                         if(!FD_ISSET(cmdfd
, &rfd
) && !FD_ISSET(xfd
, &rfd
)) { 
3802                                         if(TIMEDIFF(now
, lastblink
) \
 
3804                                                 drawtimeout
.tv_usec 
= 1; 
3806                                                 drawtimeout
.tv_usec 
= (1000 * \
 
3821         die("%s " VERSION 
" (c) 2010-2014 st engineers\n" \
 
3822         "usage: st [-a] [-v] [-c class] [-f font] [-g geometry] [-o file]" \
 
3823         " [-t title] [-w windowid] [-e command ...]\n", argv0
); 
3827 main(int argc
, char *argv
[]) { 
3829         uint cols 
= 80, rows 
= 24; 
3836                 allowaltscreen 
= false; 
3839                 opt_class 
= EARGF(usage()); 
3842                 /* eat all remaining arguments */ 
3845                         if(argv
[1] != NULL 
&& opt_title 
== NULL
) { 
3846                                 titles 
= xstrdup(argv
[1]); 
3847                                 opt_title 
= basename(titles
); 
3852                 opt_font 
= EARGF(usage()); 
3855                 xw
.gm 
= XParseGeometry(EARGF(usage()), 
3856                                 &xw
.l
, &xw
.t
, &cols
, &rows
); 
3862                 opt_io 
= EARGF(usage()); 
3865                 opt_title 
= EARGF(usage()); 
3868                 opt_embed 
= EARGF(usage()); 
3876         setlocale(LC_CTYPE
, ""); 
3877         XSetLocaleModifiers(""); 
3878         tnew(cols
? cols 
: 1, rows
? rows 
: 1);