Xinqi Bao's Git
   1 /* See LICENSE for license details. */ 
  14 #include <sys/ioctl.h> 
  15 #include <sys/select.h> 
  18 #include <sys/types.h> 
  24 #include <fontconfig/fontconfig.h> 
  28 #include <X11/cursorfont.h> 
  29 #include <X11/Xft/Xft.h> 
  41 #elif defined(__OpenBSD__) || defined(__NetBSD__) || defined(__APPLE__) 
  43 #elif defined(__FreeBSD__) || defined(__DragonFly__) 
  48 #define UTF_INVALID   0xFFFD 
  49 #define ESC_BUF_SIZ   (128*UTF_SIZ) 
  50 #define ESC_ARG_SIZ   16 
  51 #define STR_BUF_SIZ   ESC_BUF_SIZ 
  52 #define STR_ARG_SIZ   ESC_ARG_SIZ 
  55 #define NUMMAXLEN(x)            ((int)(sizeof(x) * 2.56 + 0.5) + 1) 
  56 #define DEFAULT(a, b)           (a) = (a) ? (a) : (b) 
  57 #define ISCONTROLC0(c)          (BETWEEN(c, 0, 0x1f) || (c) == '\177') 
  58 #define ISCONTROLC1(c)          (BETWEEN(c, 0x80, 0x9f)) 
  59 #define ISCONTROL(c)            (ISCONTROLC0(c) || ISCONTROLC1(c)) 
  60 #define ISDELIM(u)              (utf8strchr(worddelimiters, u) != NULL) 
  63 #define ISO14755CMD             "dmenu -w %lu -p codepoint: </dev/null" 
  65 enum cursor_movement 
{ 
  89         ESC_STR        
= 4,  /* OSC, PM, APC */ 
  91         ESC_STR_END    
= 16, /* a final string was encountered */ 
  92         ESC_TEST       
= 32, /* Enter in test mode */ 
  97 /* CSI Escape sequence structs */ 
  98 /* ESC '[' [[ [<priv>] <arg> [;]] <mode> [<mode>]] */ 
 100         char buf
[ESC_BUF_SIZ
]; /* raw string */ 
 101         int len
;               /* raw string length */ 
 103         int arg
[ESC_ARG_SIZ
]; 
 104         int narg
;              /* nb of args */ 
 108 /* STR Escape sequence structs */ 
 109 /* ESC type [[ [<priv>] <arg> [;]] <mode>] ESC '\' */ 
 111         char type
;             /* ESC type ... */ 
 112         char buf
[STR_BUF_SIZ
]; /* raw string */ 
 113         int len
;               /* raw string length */ 
 114         char *args
[STR_ARG_SIZ
]; 
 115         int narg
;              /* nb of args */ 
 122         /* three valued logic variables: 0 indifferent, 1 on, -1 off */ 
 123         signed char appkey
;    /* application keypad */ 
 124         signed char appcursor
; /* application cursor */ 
 125         signed char crlf
;      /* crlf mode          */ 
 128 /* function definitions used in config.h */ 
 129 static void clipcopy(const Arg 
*); 
 130 static void clippaste(const Arg 
*); 
 131 static void numlock(const Arg 
*); 
 132 static void selpaste(const Arg 
*); 
 133 static void zoom(const Arg 
*); 
 134 static void zoomabs(const Arg 
*); 
 135 static void zoomreset(const Arg 
*); 
 136 static void printsel(const Arg 
*); 
 137 static void printscreen(const Arg 
*) ; 
 138 static void iso14755(const Arg 
*); 
 139 static void toggleprinter(const Arg 
*); 
 140 static void sendbreak(const Arg 
*); 
 142 /* config.h for applying patches and the configuration. */ 
 145 static void execsh(void); 
 146 static void stty(void); 
 147 static void sigchld(int); 
 149 static void csidump(void); 
 150 static void csihandle(void); 
 151 static void csiparse(void); 
 152 static void csireset(void); 
 153 static int eschandle(uchar
); 
 154 static void strdump(void); 
 155 static void strhandle(void); 
 156 static void strparse(void); 
 157 static void strreset(void); 
 159 static void tprinter(char *, size_t); 
 160 static void tdumpsel(void); 
 161 static void tdumpline(int); 
 162 static void tdump(void); 
 163 static void tclearregion(int, int, int, int); 
 164 static void tcursor(int); 
 165 static void tdeletechar(int); 
 166 static void tdeleteline(int); 
 167 static void tinsertblank(int); 
 168 static void tinsertblankline(int); 
 169 static int tlinelen(int); 
 170 static void tmoveto(int, int); 
 171 static void tmoveato(int, int); 
 172 static void tnewline(int); 
 173 static void tputtab(int); 
 174 static void tputc(Rune
); 
 175 static void treset(void); 
 176 static void tresize(int, int); 
 177 static void tscrollup(int, int); 
 178 static void tscrolldown(int, int); 
 179 static void tsetattr(int *, int); 
 180 static void tsetchar(Rune
, Glyph 
*, int, int); 
 181 static void tsetscroll(int, int); 
 182 static void tswapscreen(void); 
 183 static void tsetmode(int, int, int *, int); 
 184 static void tfulldirt(void); 
 185 static void techo(Rune
); 
 186 static void tcontrolcode(uchar 
); 
 187 static void tdectest(char ); 
 188 static void tdefutf8(char); 
 189 static int32_t tdefcolor(int *, int *, int); 
 190 static void tdeftran(char); 
 191 static void tstrsequence(uchar
); 
 193 static void selscroll(int, int); 
 194 static void selsnap(int *, int *, int); 
 196 static Rune 
utf8decodebyte(char, size_t *); 
 197 static char utf8encodebyte(Rune
, size_t); 
 198 static char *utf8strchr(char *s
, Rune u
); 
 199 static size_t utf8validate(Rune 
*, size_t); 
 201 static char *base64dec(const char *); 
 203 static ssize_t 
xwrite(int, const char *, size_t); 
 204 static void *xrealloc(void *, size_t); 
 212 char **opt_cmd  
= NULL
; 
 213 char *opt_class 
= NULL
; 
 214 char *opt_embed 
= NULL
; 
 215 char *opt_font  
= NULL
; 
 217 char *opt_line  
= NULL
; 
 218 char *opt_name  
= NULL
; 
 219 char *opt_title 
= NULL
; 
 220 int oldbutton   
= 3; /* button event on startup: 3 = release */ 
 222 static CSIEscape csiescseq
; 
 223 static STREscape strescseq
; 
 226 char *usedfont 
= NULL
; 
 227 double usedfontsize 
= 0; 
 228 double defaultfontsize 
= 0; 
 230 static uchar utfbyte
[UTF_SIZ 
+ 1] = {0x80,    0, 0xC0, 0xE0, 0xF0}; 
 231 static uchar utfmask
[UTF_SIZ 
+ 1] = {0xC0, 0x80, 0xE0, 0xF0, 0xF8}; 
 232 static Rune utfmin
[UTF_SIZ 
+ 1] = {       0,    0,  0x80,  0x800,  0x10000}; 
 233 static Rune utfmax
[UTF_SIZ 
+ 1] = {0x10FFFF, 0x7F, 0x7FF, 0xFFFF, 0x10FFFF}; 
 235 /* config.h array lengths */ 
 236 size_t colornamelen 
= LEN(colorname
); 
 237 size_t mshortcutslen 
= LEN(mshortcuts
); 
 238 size_t shortcutslen 
= LEN(shortcuts
); 
 239 size_t selmaskslen 
= LEN(selmasks
); 
 242 xwrite(int fd
, const char *s
, size_t len
) 
 248                 r 
= write(fd
, s
, len
); 
 261         void *p 
= malloc(len
); 
 264                 die("Out of memory\n"); 
 270 xrealloc(void *p
, size_t len
) 
 272         if ((p 
= realloc(p
, len
)) == NULL
) 
 273                 die("Out of memory\n"); 
 281         if ((s 
= strdup(s
)) == NULL
) 
 282                 die("Out of memory\n"); 
 288 utf8decode(char *c
, Rune 
*u
, size_t clen
) 
 290         size_t i
, j
, len
, type
; 
 296         udecoded 
= utf8decodebyte(c
[0], &len
); 
 297         if (!BETWEEN(len
, 1, UTF_SIZ
)) 
 299         for (i 
= 1, j 
= 1; i 
< clen 
&& j 
< len
; ++i
, ++j
) { 
 300                 udecoded 
= (udecoded 
<< 6) | utf8decodebyte(c
[i
], &type
); 
 307         utf8validate(u
, len
); 
 313 utf8decodebyte(char c
, size_t *i
) 
 315         for (*i 
= 0; *i 
< LEN(utfmask
); ++(*i
)) 
 316                 if (((uchar
)c 
& utfmask
[*i
]) == utfbyte
[*i
]) 
 317                         return (uchar
)c 
& ~utfmask
[*i
]; 
 323 utf8encode(Rune u
, char *c
) 
 327         len 
= utf8validate(&u
, 0); 
 331         for (i 
= len 
- 1; i 
!= 0; --i
) { 
 332                 c
[i
] = utf8encodebyte(u
, 0); 
 335         c
[0] = utf8encodebyte(u
, len
); 
 341 utf8encodebyte(Rune u
, size_t i
) 
 343         return utfbyte
[i
] | (u 
& ~utfmask
[i
]); 
 347 utf8strchr(char *s
, Rune u
) 
 353         for (i 
= 0, j 
= 0; i 
< len
; i 
+= j
) { 
 354                 if (!(j 
= utf8decode(&s
[i
], &r
, len 
- i
))) 
 364 utf8validate(Rune 
*u
, size_t i
) 
 366         if (!BETWEEN(*u
, utfmin
[i
], utfmax
[i
]) || BETWEEN(*u
, 0xD800, 0xDFFF)) 
 368         for (i 
= 1; *u 
> utfmax
[i
]; ++i
) 
 374 static const char base64_digits
[] = { 
 375         0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 
 376         0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 62, 0, 0, 0, 
 377         63, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 0, 0, 0, -1, 0, 0, 0, 0, 1, 
 378         2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 
 379         22, 23, 24, 25, 0, 0, 0, 0, 0, 0, 26, 27, 28, 29, 30, 31, 32, 33, 34, 
 380         35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 0, 
 381         0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 
 382         0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 
 383         0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 
 384         0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 
 385         0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 
 386         0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 
 390 base64dec_getc(const char **src
) 
 392         while (**src 
&& !isprint(**src
)) (*src
)++; 
 397 base64dec(const char *src
) 
 399         size_t in_len 
= strlen(src
); 
 403                 in_len 
+= 4 - (in_len 
% 4); 
 404         result 
= dst 
= xmalloc(in_len 
/ 4 * 3 + 1); 
 406                 int a 
= base64_digits
[(unsigned char) base64dec_getc(&src
)]; 
 407                 int b 
= base64_digits
[(unsigned char) base64dec_getc(&src
)]; 
 408                 int c 
= base64_digits
[(unsigned char) base64dec_getc(&src
)]; 
 409                 int d 
= base64_digits
[(unsigned char) base64dec_getc(&src
)]; 
 411                 *dst
++ = (a 
<< 2) | ((b 
& 0x30) >> 4); 
 414                 *dst
++ = ((b 
& 0x0f) << 4) | ((c 
& 0x3c) >> 2); 
 417                 *dst
++ = ((c 
& 0x03) << 6) | d
; 
 426         clock_gettime(CLOCK_MONOTONIC
, &sel
.tclick1
); 
 427         clock_gettime(CLOCK_MONOTONIC
, &sel
.tclick2
); 
 432         sel
.clipboard 
= NULL
; 
 441         return LIMIT(x
, 0, term
.col
-1); 
 450         return LIMIT(y
, 0, term
.row
-1); 
 458         if (term
.line
[y
][i 
- 1].mode 
& ATTR_WRAP
) 
 461         while (i 
> 0 && term
.line
[y
][i 
- 1].u 
== ' ') 
 472         if (sel
.type 
== SEL_REGULAR 
&& sel
.ob
.y 
!= sel
.oe
.y
) { 
 473                 sel
.nb
.x 
= sel
.ob
.y 
< sel
.oe
.y 
? sel
.ob
.x 
: sel
.oe
.x
; 
 474                 sel
.ne
.x 
= sel
.ob
.y 
< sel
.oe
.y 
? sel
.oe
.x 
: sel
.ob
.x
; 
 476                 sel
.nb
.x 
= MIN(sel
.ob
.x
, sel
.oe
.x
); 
 477                 sel
.ne
.x 
= MAX(sel
.ob
.x
, sel
.oe
.x
); 
 479         sel
.nb
.y 
= MIN(sel
.ob
.y
, sel
.oe
.y
); 
 480         sel
.ne
.y 
= MAX(sel
.ob
.y
, sel
.oe
.y
); 
 482         selsnap(&sel
.nb
.x
, &sel
.nb
.y
, -1); 
 483         selsnap(&sel
.ne
.x
, &sel
.ne
.y
, +1); 
 485         /* expand selection over line breaks */ 
 486         if (sel
.type 
== SEL_RECTANGULAR
) 
 488         i 
= tlinelen(sel
.nb
.y
); 
 491         if (tlinelen(sel
.ne
.y
) <= sel
.ne
.x
) 
 492                 sel
.ne
.x 
= term
.col 
- 1; 
 496 selected(int x
, int y
) 
 498         if (sel
.mode 
== SEL_EMPTY
) 
 501         if (sel
.type 
== SEL_RECTANGULAR
) 
 502                 return BETWEEN(y
, sel
.nb
.y
, sel
.ne
.y
) 
 503                     && BETWEEN(x
, sel
.nb
.x
, sel
.ne
.x
); 
 505         return BETWEEN(y
, sel
.nb
.y
, sel
.ne
.y
) 
 506             && (y 
!= sel
.nb
.y 
|| x 
>= sel
.nb
.x
) 
 507             && (y 
!= sel
.ne
.y 
|| x 
<= sel
.ne
.x
); 
 511 selsnap(int *x
, int *y
, int direction
) 
 513         int newx
, newy
, xt
, yt
; 
 514         int delim
, prevdelim
; 
 520                  * Snap around if the word wraps around at the end or 
 521                  * beginning of a line. 
 523                 prevgp 
= &term
.line
[*y
][*x
]; 
 524                 prevdelim 
= ISDELIM(prevgp
->u
); 
 526                         newx 
= *x 
+ direction
; 
 528                         if (!BETWEEN(newx
, 0, term
.col 
- 1)) { 
 530                                 newx 
= (newx 
+ term
.col
) % term
.col
; 
 531                                 if (!BETWEEN(newy
, 0, term
.row 
- 1)) 
 537                                         yt 
= newy
, xt 
= newx
; 
 538                                 if (!(term
.line
[yt
][xt
].mode 
& ATTR_WRAP
)) 
 542                         if (newx 
>= tlinelen(newy
)) 
 545                         gp 
= &term
.line
[newy
][newx
]; 
 546                         delim 
= ISDELIM(gp
->u
); 
 547                         if (!(gp
->mode 
& ATTR_WDUMMY
) && (delim 
!= prevdelim
 
 548                                         || (delim 
&& gp
->u 
!= prevgp
->u
))) 
 559                  * Snap around if the the previous line or the current one 
 560                  * has set ATTR_WRAP at its end. Then the whole next or 
 561                  * previous line will be selected. 
 563                 *x 
= (direction 
< 0) ? 0 : term
.col 
- 1; 
 565                         for (; *y 
> 0; *y 
+= direction
) { 
 566                                 if (!(term
.line
[*y
-1][term
.col
-1].mode
 
 571                 } else if (direction 
> 0) { 
 572                         for (; *y 
< term
.row
-1; *y 
+= direction
) { 
 573                                 if (!(term
.line
[*y
][term
.col
-1].mode
 
 587         int y
, bufsize
, lastx
, linelen
; 
 593         bufsize 
= (term
.col
+1) * (sel
.ne
.y
-sel
.nb
.y
+1) * UTF_SIZ
; 
 594         ptr 
= str 
= xmalloc(bufsize
); 
 596         /* append every set & selected glyph to the selection */ 
 597         for (y 
= sel
.nb
.y
; y 
<= sel
.ne
.y
; y
++) { 
 598                 if ((linelen 
= tlinelen(y
)) == 0) { 
 603                 if (sel
.type 
== SEL_RECTANGULAR
) { 
 604                         gp 
= &term
.line
[y
][sel
.nb
.x
]; 
 607                         gp 
= &term
.line
[y
][sel
.nb
.y 
== y 
? sel
.nb
.x 
: 0]; 
 608                         lastx 
= (sel
.ne
.y 
== y
) ? sel
.ne
.x 
: term
.col
-1; 
 610                 last 
= &term
.line
[y
][MIN(lastx
, linelen
-1)]; 
 611                 while (last 
>= gp 
&& last
->u 
== ' ') 
 614                 for ( ; gp 
<= last
; ++gp
) { 
 615                         if (gp
->mode 
& ATTR_WDUMMY
) 
 618                         ptr 
+= utf8encode(gp
->u
, ptr
); 
 622                  * Copy and pasting of line endings is inconsistent 
 623                  * in the inconsistent terminal and GUI world. 
 624                  * The best solution seems like to produce '\n' when 
 625                  * something is copied from st and convert '\n' to 
 626                  * '\r', when something to be pasted is received by 
 628                  * FIXME: Fix the computer world. 
 630                 if ((y 
< sel
.ne
.y 
|| lastx 
>= linelen
) && !(last
->mode 
& ATTR_WRAP
)) 
 638 selpaste(const Arg 
*dummy
) 
 644 clipcopy(const Arg 
*dummy
) 
 650 clippaste(const Arg 
*dummy
) 
 662         tsetdirt(sel
.nb
.y
, sel
.ne
.y
); 
 666 die(const char *errstr
, ...) 
 670         va_start(ap
, errstr
); 
 671         vfprintf(stderr
, errstr
, ap
); 
 679         char **args
, *sh
, *prog
; 
 680         const struct passwd 
*pw
; 
 683         if ((pw 
= getpwuid(getuid())) == NULL
) { 
 685                         die("getpwuid:%s\n", strerror(errno
)); 
 687                         die("who are you?\n"); 
 690         if ((sh 
= getenv("SHELL")) == NULL
) 
 691                 sh 
= (pw
->pw_shell
[0]) ? pw
->pw_shell 
: shell
; 
 699         args 
= (opt_cmd
) ? opt_cmd 
: (char *[]) {prog
, NULL
}; 
 704         setenv("LOGNAME", pw
->pw_name
, 1); 
 705         setenv("USER", pw
->pw_name
, 1); 
 706         setenv("SHELL", sh
, 1); 
 707         setenv("HOME", pw
->pw_dir
, 1); 
 708         setenv("TERM", termname
, 1); 
 711         signal(SIGCHLD
, SIG_DFL
); 
 712         signal(SIGHUP
, SIG_DFL
); 
 713         signal(SIGINT
, SIG_DFL
); 
 714         signal(SIGQUIT
, SIG_DFL
); 
 715         signal(SIGTERM
, SIG_DFL
); 
 716         signal(SIGALRM
, SIG_DFL
); 
 728         if ((p 
= waitpid(pid
, &stat
, WNOHANG
)) < 0) 
 729                 die("Waiting for pid %hd failed: %s\n", pid
, strerror(errno
)); 
 734         if (!WIFEXITED(stat
) || WEXITSTATUS(stat
)) 
 735                 die("child finished with error '%d'\n", stat
); 
 743         char cmd
[_POSIX_ARG_MAX
], **p
, *q
, *s
; 
 746         if ((n 
= strlen(stty_args
)) > sizeof(cmd
)-1) 
 747                 die("incorrect stty parameters\n"); 
 748         memcpy(cmd
, stty_args
, n
); 
 750         siz 
= sizeof(cmd
) - n
; 
 751         for (p 
= opt_cmd
; p 
&& (s 
= *p
); ++p
) { 
 752                 if ((n 
= strlen(s
)) > siz
-1) 
 753                         die("stty parameter length too long\n"); 
 760         if (system(cmd
) != 0) 
 761             perror("Couldn't call stty"); 
 768         struct winsize w 
= {term
.row
, term
.col
, 0, 0}; 
 771                 term
.mode 
|= MODE_PRINT
; 
 772                 iofd 
= (!strcmp(opt_io
, "-")) ? 
 773                           1 : open(opt_io
, O_WRONLY 
| O_CREAT
, 0666); 
 775                         fprintf(stderr
, "Error opening %s:%s\n", 
 776                                 opt_io
, strerror(errno
)); 
 781                 if ((cmdfd 
= open(opt_line
, O_RDWR
)) < 0) 
 782                         die("open line failed: %s\n", strerror(errno
)); 
 788         /* seems to work fine on linux, openbsd and freebsd */ 
 789         if (openpty(&m
, &s
, NULL
, NULL
, &w
) < 0) 
 790                 die("openpty failed: %s\n", strerror(errno
)); 
 792         switch (pid 
= fork()) { 
 794                 die("fork failed\n"); 
 798                 setsid(); /* create a new process group */ 
 802                 if (ioctl(s
, TIOCSCTTY
, NULL
) < 0) 
 803                         die("ioctl TIOCSCTTY failed: %s\n", strerror(errno
)); 
 811                 signal(SIGCHLD
, sigchld
); 
 819         static char buf
[BUFSIZ
]; 
 820         static int buflen 
= 0; 
 822         int charsize
; /* size of utf8 char in bytes */ 
 826         /* append read bytes to unprocessed bytes */ 
 827         if ((ret 
= read(cmdfd
, buf
+buflen
, LEN(buf
)-buflen
)) < 0) 
 828                 die("Couldn't read from shell: %s\n", strerror(errno
)); 
 834                 if (IS_SET(MODE_UTF8
) && !IS_SET(MODE_SIXEL
)) { 
 835                         /* process a complete utf8 char */ 
 836                         charsize 
= utf8decode(ptr
, &unicodep
, buflen
); 
 846                         tputc(*ptr
++ & 0xFF); 
 850         /* keep any uncomplete utf8 char for the next call */ 
 852                 memmove(buf
, ptr
, buflen
); 
 858 ttywrite(const char *s
, size_t n
) 
 865          * Remember that we are using a pty, which might be a modem line. 
 866          * Writing too much will clog the line. That's why we are doing this 
 868          * FIXME: Migrate the world to Plan 9. 
 876                 /* Check if we can write. */ 
 877                 if (pselect(cmdfd
+1, &rfd
, &wfd
, NULL
, NULL
, NULL
) < 0) { 
 880                         die("select failed: %s\n", strerror(errno
)); 
 882                 if (FD_ISSET(cmdfd
, &wfd
)) { 
 884                          * Only write the bytes written by ttywrite() or the 
 885                          * default of 256. This seems to be a reasonable value 
 886                          * for a serial line. Bigger values might clog the I/O. 
 888                         if ((r 
= write(cmdfd
, s
, (n 
< lim
)? n 
: lim
)) < 0) 
 892                                  * We weren't able to write out everything. 
 893                                  * This means the buffer is getting full 
 901                                 /* All bytes have been written. */ 
 905                 if (FD_ISSET(cmdfd
, &rfd
)) 
 911         die("write error on tty: %s\n", strerror(errno
)); 
 915 ttysend(char *s
, size_t n
) 
 922         if (!IS_SET(MODE_ECHO
)) 
 926         for (t 
= s
; t 
< lim
; t 
+= len
) { 
 927                 if (IS_SET(MODE_UTF8
) && !IS_SET(MODE_SIXEL
)) { 
 928                         len 
= utf8decode(t
, &u
, n
); 
 947         w
.ws_xpixel 
= win
.tw
; 
 948         w
.ws_ypixel 
= win
.th
; 
 949         if (ioctl(cmdfd
, TIOCSWINSZ
, &w
) < 0) 
 950                 fprintf(stderr
, "Couldn't set window size: %s\n", strerror(errno
)); 
 958         for (i 
= 0; i 
< term
.row
-1; i
++) { 
 959                 for (j 
= 0; j 
< term
.col
-1; j
++) { 
 960                         if (term
.line
[i
][j
].mode 
& attr
) 
 969 tsetdirt(int top
, int bot
) 
 973         LIMIT(top
, 0, term
.row
-1); 
 974         LIMIT(bot
, 0, term
.row
-1); 
 976         for (i 
= top
; i 
<= bot
; i
++) 
 981 tsetdirtattr(int attr
) 
 985         for (i 
= 0; i 
< term
.row
-1; i
++) { 
 986                 for (j 
= 0; j 
< term
.col
-1; j
++) { 
 987                         if (term
.line
[i
][j
].mode 
& attr
) { 
 998         tsetdirt(0, term
.row
-1); 
1004         static TCursor c
[2]; 
1005         int alt 
= IS_SET(MODE_ALTSCREEN
); 
1007         if (mode 
== CURSOR_SAVE
) { 
1009         } else if (mode 
== CURSOR_LOAD
) { 
1011                 tmoveto(c
[alt
].x
, c
[alt
].y
); 
1020         term
.c 
= (TCursor
){{ 
1024         }, .x 
= 0, .y 
= 0, .state 
= CURSOR_DEFAULT
}; 
1026         memset(term
.tabs
, 0, term
.col 
* sizeof(*term
.tabs
)); 
1027         for (i 
= tabspaces
; i 
< term
.col
; i 
+= tabspaces
) 
1030         term
.bot 
= term
.row 
- 1; 
1031         term
.mode 
= MODE_WRAP
|MODE_UTF8
; 
1032         memset(term
.trantbl
, CS_USA
, sizeof(term
.trantbl
)); 
1035         for (i 
= 0; i 
< 2; i
++) { 
1037                 tcursor(CURSOR_SAVE
); 
1038                 tclearregion(0, 0, term
.col
-1, term
.row
-1); 
1044 tnew(int col
, int row
) 
1046         term 
= (Term
){ .c 
= { .attr 
= { .fg 
= defaultfg
, .bg 
= defaultbg 
} } }; 
1056         Line 
*tmp 
= term
.line
; 
1058         term
.line 
= term
.alt
; 
1060         term
.mode 
^= MODE_ALTSCREEN
; 
1065 tscrolldown(int orig
, int n
) 
1070         LIMIT(n
, 0, term
.bot
-orig
+1); 
1072         tsetdirt(orig
, term
.bot
-n
); 
1073         tclearregion(0, term
.bot
-n
+1, term
.col
-1, term
.bot
); 
1075         for (i 
= term
.bot
; i 
>= orig
+n
; i
--) { 
1076                 temp 
= term
.line
[i
]; 
1077                 term
.line
[i
] = term
.line
[i
-n
]; 
1078                 term
.line
[i
-n
] = temp
; 
1085 tscrollup(int orig
, int n
) 
1090         LIMIT(n
, 0, term
.bot
-orig
+1); 
1092         tclearregion(0, orig
, term
.col
-1, orig
+n
-1); 
1093         tsetdirt(orig
+n
, term
.bot
); 
1095         for (i 
= orig
; i 
<= term
.bot
-n
; i
++) { 
1096                 temp 
= term
.line
[i
]; 
1097                 term
.line
[i
] = term
.line
[i
+n
]; 
1098                 term
.line
[i
+n
] = temp
; 
1101         selscroll(orig
, -n
); 
1105 selscroll(int orig
, int n
) 
1110         if (BETWEEN(sel
.ob
.y
, orig
, term
.bot
) || BETWEEN(sel
.oe
.y
, orig
, term
.bot
)) { 
1111                 if ((sel
.ob
.y 
+= n
) > term
.bot 
|| (sel
.oe
.y 
+= n
) < term
.top
) { 
1115                 if (sel
.type 
== SEL_RECTANGULAR
) { 
1116                         if (sel
.ob
.y 
< term
.top
) 
1117                                 sel
.ob
.y 
= term
.top
; 
1118                         if (sel
.oe
.y 
> term
.bot
) 
1119                                 sel
.oe
.y 
= term
.bot
; 
1121                         if (sel
.ob
.y 
< term
.top
) { 
1122                                 sel
.ob
.y 
= term
.top
; 
1125                         if (sel
.oe
.y 
> term
.bot
) { 
1126                                 sel
.oe
.y 
= term
.bot
; 
1127                                 sel
.oe
.x 
= term
.col
; 
1135 tnewline(int first_col
) 
1139         if (y 
== term
.bot
) { 
1140                 tscrollup(term
.top
, 1); 
1144         tmoveto(first_col 
? 0 : term
.c
.x
, y
); 
1150         char *p 
= csiescseq
.buf
, *np
; 
1159         csiescseq
.buf
[csiescseq
.len
] = '\0'; 
1160         while (p 
< csiescseq
.buf
+csiescseq
.len
) { 
1162                 v 
= strtol(p
, &np
, 10); 
1165                 if (v 
== LONG_MAX 
|| v 
== LONG_MIN
) 
1167                 csiescseq
.arg
[csiescseq
.narg
++] = v
; 
1169                 if (*p 
!= ';' || csiescseq
.narg 
== ESC_ARG_SIZ
) 
1173         csiescseq
.mode
[0] = *p
++; 
1174         csiescseq
.mode
[1] = (p 
< csiescseq
.buf
+csiescseq
.len
) ? *p 
: '\0'; 
1177 /* for absolute user moves, when decom is set */ 
1179 tmoveato(int x
, int y
) 
1181         tmoveto(x
, y 
+ ((term
.c
.state 
& CURSOR_ORIGIN
) ? term
.top
: 0)); 
1185 tmoveto(int x
, int y
) 
1189         if (term
.c
.state 
& CURSOR_ORIGIN
) { 
1194                 maxy 
= term
.row 
- 1; 
1196         term
.c
.state 
&= ~CURSOR_WRAPNEXT
; 
1197         term
.c
.x 
= LIMIT(x
, 0, term
.col
-1); 
1198         term
.c
.y 
= LIMIT(y
, miny
, maxy
); 
1202 tsetchar(Rune u
, Glyph 
*attr
, int x
, int y
) 
1204         static char *vt100_0
[62] = { /* 0x41 - 0x7e */ 
1205                 "↑", "↓", "→", "←", "█", "▚", "☃", /* A - G */ 
1206                 0, 0, 0, 0, 0, 0, 0, 0, /* H - O */ 
1207                 0, 0, 0, 0, 0, 0, 0, 0, /* P - W */ 
1208                 0, 0, 0, 0, 0, 0, 0, " ", /* X - _ */ 
1209                 "◆", "▒", "␉", "␌", "␍", "␊", "°", "±", /* ` - g */ 
1210                 "", "␋", "┘", "┐", "┌", "└", "┼", "⎺", /* h - o */ 
1211                 "⎻", "─", "⎼", "⎽", "├", "┤", "┴", "┬", /* p - w */ 
1212                 "│", "≤", "≥", "π", "≠", "£", "·", /* x - ~ */ 
1216          * The table is proudly stolen from rxvt. 
1218         if (term
.trantbl
[term
.charset
] == CS_GRAPHIC0 
&& 
1219            BETWEEN(u
, 0x41, 0x7e) && vt100_0
[u 
- 0x41]) 
1220                 utf8decode(vt100_0
[u 
- 0x41], &u
, UTF_SIZ
); 
1222         if (term
.line
[y
][x
].mode 
& ATTR_WIDE
) { 
1223                 if (x
+1 < term
.col
) { 
1224                         term
.line
[y
][x
+1].u 
= ' '; 
1225                         term
.line
[y
][x
+1].mode 
&= ~ATTR_WDUMMY
; 
1227         } else if (term
.line
[y
][x
].mode 
& ATTR_WDUMMY
) { 
1228                 term
.line
[y
][x
-1].u 
= ' '; 
1229                 term
.line
[y
][x
-1].mode 
&= ~ATTR_WIDE
; 
1233         term
.line
[y
][x
] = *attr
; 
1234         term
.line
[y
][x
].u 
= u
; 
1238 tclearregion(int x1
, int y1
, int x2
, int y2
) 
1244                 temp 
= x1
, x1 
= x2
, x2 
= temp
; 
1246                 temp 
= y1
, y1 
= y2
, y2 
= temp
; 
1248         LIMIT(x1
, 0, term
.col
-1); 
1249         LIMIT(x2
, 0, term
.col
-1); 
1250         LIMIT(y1
, 0, term
.row
-1); 
1251         LIMIT(y2
, 0, term
.row
-1); 
1253         for (y 
= y1
; y 
<= y2
; y
++) { 
1255                 for (x 
= x1
; x 
<= x2
; x
++) { 
1256                         gp 
= &term
.line
[y
][x
]; 
1259                         gp
->fg 
= term
.c
.attr
.fg
; 
1260                         gp
->bg 
= term
.c
.attr
.bg
; 
1273         LIMIT(n
, 0, term
.col 
- term
.c
.x
); 
1277         size 
= term
.col 
- src
; 
1278         line 
= term
.line
[term
.c
.y
]; 
1280         memmove(&line
[dst
], &line
[src
], size 
* sizeof(Glyph
)); 
1281         tclearregion(term
.col
-n
, term
.c
.y
, term
.col
-1, term
.c
.y
); 
1290         LIMIT(n
, 0, term
.col 
- term
.c
.x
); 
1294         size 
= term
.col 
- dst
; 
1295         line 
= term
.line
[term
.c
.y
]; 
1297         memmove(&line
[dst
], &line
[src
], size 
* sizeof(Glyph
)); 
1298         tclearregion(src
, term
.c
.y
, dst 
- 1, term
.c
.y
); 
1302 tinsertblankline(int n
) 
1304         if (BETWEEN(term
.c
.y
, term
.top
, term
.bot
)) 
1305                 tscrolldown(term
.c
.y
, n
); 
1311         if (BETWEEN(term
.c
.y
, term
.top
, term
.bot
)) 
1312                 tscrollup(term
.c
.y
, n
); 
1316 tdefcolor(int *attr
, int *npar
, int l
) 
1321         switch (attr
[*npar 
+ 1]) { 
1322         case 2: /* direct color in RGB space */ 
1323                 if (*npar 
+ 4 >= l
) { 
1325                                 "erresc(38): Incorrect number of parameters (%d)\n", 
1329                 r 
= attr
[*npar 
+ 2]; 
1330                 g 
= attr
[*npar 
+ 3]; 
1331                 b 
= attr
[*npar 
+ 4]; 
1333                 if (!BETWEEN(r
, 0, 255) || !BETWEEN(g
, 0, 255) || !BETWEEN(b
, 0, 255)) 
1334                         fprintf(stderr
, "erresc: bad rgb color (%u,%u,%u)\n", 
1337                         idx 
= TRUECOLOR(r
, g
, b
); 
1339         case 5: /* indexed color */ 
1340                 if (*npar 
+ 2 >= l
) { 
1342                                 "erresc(38): Incorrect number of parameters (%d)\n", 
1347                 if (!BETWEEN(attr
[*npar
], 0, 255)) 
1348                         fprintf(stderr
, "erresc: bad fgcolor %d\n", attr
[*npar
]); 
1352         case 0: /* implemented defined (only foreground) */ 
1353         case 1: /* transparent */ 
1354         case 3: /* direct color in CMY space */ 
1355         case 4: /* direct color in CMYK space */ 
1358                         "erresc(38): gfx attr %d unknown\n", attr
[*npar
]); 
1366 tsetattr(int *attr
, int l
) 
1371         for (i 
= 0; i 
< l
; i
++) { 
1374                         term
.c
.attr
.mode 
&= ~( 
1383                         term
.c
.attr
.fg 
= defaultfg
; 
1384                         term
.c
.attr
.bg 
= defaultbg
; 
1387                         term
.c
.attr
.mode 
|= ATTR_BOLD
; 
1390                         term
.c
.attr
.mode 
|= ATTR_FAINT
; 
1393                         term
.c
.attr
.mode 
|= ATTR_ITALIC
; 
1396                         term
.c
.attr
.mode 
|= ATTR_UNDERLINE
; 
1398                 case 5: /* slow blink */ 
1400                 case 6: /* rapid blink */ 
1401                         term
.c
.attr
.mode 
|= ATTR_BLINK
; 
1404                         term
.c
.attr
.mode 
|= ATTR_REVERSE
; 
1407                         term
.c
.attr
.mode 
|= ATTR_INVISIBLE
; 
1410                         term
.c
.attr
.mode 
|= ATTR_STRUCK
; 
1413                         term
.c
.attr
.mode 
&= ~(ATTR_BOLD 
| ATTR_FAINT
); 
1416                         term
.c
.attr
.mode 
&= ~ATTR_ITALIC
; 
1419                         term
.c
.attr
.mode 
&= ~ATTR_UNDERLINE
; 
1422                         term
.c
.attr
.mode 
&= ~ATTR_BLINK
; 
1425                         term
.c
.attr
.mode 
&= ~ATTR_REVERSE
; 
1428                         term
.c
.attr
.mode 
&= ~ATTR_INVISIBLE
; 
1431                         term
.c
.attr
.mode 
&= ~ATTR_STRUCK
; 
1434                         if ((idx 
= tdefcolor(attr
, &i
, l
)) >= 0) 
1435                                 term
.c
.attr
.fg 
= idx
; 
1438                         term
.c
.attr
.fg 
= defaultfg
; 
1441                         if ((idx 
= tdefcolor(attr
, &i
, l
)) >= 0) 
1442                                 term
.c
.attr
.bg 
= idx
; 
1445                         term
.c
.attr
.bg 
= defaultbg
; 
1448                         if (BETWEEN(attr
[i
], 30, 37)) { 
1449                                 term
.c
.attr
.fg 
= attr
[i
] - 30; 
1450                         } else if (BETWEEN(attr
[i
], 40, 47)) { 
1451                                 term
.c
.attr
.bg 
= attr
[i
] - 40; 
1452                         } else if (BETWEEN(attr
[i
], 90, 97)) { 
1453                                 term
.c
.attr
.fg 
= attr
[i
] - 90 + 8; 
1454                         } else if (BETWEEN(attr
[i
], 100, 107)) { 
1455                                 term
.c
.attr
.bg 
= attr
[i
] - 100 + 8; 
1458                                         "erresc(default): gfx attr %d unknown\n", 
1459                                         attr
[i
]), csidump(); 
1467 tsetscroll(int t
, int b
) 
1471         LIMIT(t
, 0, term
.row
-1); 
1472         LIMIT(b
, 0, term
.row
-1); 
1483 tsetmode(int priv
, int set
, int *args
, int narg
) 
1488         for (lim 
= args 
+ narg
; args 
< lim
; ++args
) { 
1491                         case 1: /* DECCKM -- Cursor key */ 
1492                                 MODBIT(term
.mode
, set
, MODE_APPCURSOR
); 
1494                         case 5: /* DECSCNM -- Reverse video */ 
1496                                 MODBIT(term
.mode
, set
, MODE_REVERSE
); 
1497                                 if (mode 
!= term
.mode
) 
1500                         case 6: /* DECOM -- Origin */ 
1501                                 MODBIT(term
.c
.state
, set
, CURSOR_ORIGIN
); 
1504                         case 7: /* DECAWM -- Auto wrap */ 
1505                                 MODBIT(term
.mode
, set
, MODE_WRAP
); 
1507                         case 0:  /* Error (IGNORED) */ 
1508                         case 2:  /* DECANM -- ANSI/VT52 (IGNORED) */ 
1509                         case 3:  /* DECCOLM -- Column  (IGNORED) */ 
1510                         case 4:  /* DECSCLM -- Scroll (IGNORED) */ 
1511                         case 8:  /* DECARM -- Auto repeat (IGNORED) */ 
1512                         case 18: /* DECPFF -- Printer feed (IGNORED) */ 
1513                         case 19: /* DECPEX -- Printer extent (IGNORED) */ 
1514                         case 42: /* DECNRCM -- National characters (IGNORED) */ 
1515                         case 12: /* att610 -- Start blinking cursor (IGNORED) */ 
1517                         case 25: /* DECTCEM -- Text Cursor Enable Mode */ 
1518                                 MODBIT(term
.mode
, !set
, MODE_HIDE
); 
1520                         case 9:    /* X10 mouse compatibility mode */ 
1521                                 xsetpointermotion(0); 
1522                                 MODBIT(term
.mode
, 0, MODE_MOUSE
); 
1523                                 MODBIT(term
.mode
, set
, MODE_MOUSEX10
); 
1525                         case 1000: /* 1000: report button press */ 
1526                                 xsetpointermotion(0); 
1527                                 MODBIT(term
.mode
, 0, MODE_MOUSE
); 
1528                                 MODBIT(term
.mode
, set
, MODE_MOUSEBTN
); 
1530                         case 1002: /* 1002: report motion on button press */ 
1531                                 xsetpointermotion(0); 
1532                                 MODBIT(term
.mode
, 0, MODE_MOUSE
); 
1533                                 MODBIT(term
.mode
, set
, MODE_MOUSEMOTION
); 
1535                         case 1003: /* 1003: enable all mouse motions */ 
1536                                 xsetpointermotion(set
); 
1537                                 MODBIT(term
.mode
, 0, MODE_MOUSE
); 
1538                                 MODBIT(term
.mode
, set
, MODE_MOUSEMANY
); 
1540                         case 1004: /* 1004: send focus events to tty */ 
1541                                 MODBIT(term
.mode
, set
, MODE_FOCUS
); 
1543                         case 1006: /* 1006: extended reporting mode */ 
1544                                 MODBIT(term
.mode
, set
, MODE_MOUSESGR
); 
1547                                 MODBIT(term
.mode
, set
, MODE_8BIT
); 
1549                         case 1049: /* swap screen & set/restore cursor as xterm */ 
1550                                 if (!allowaltscreen
) 
1552                                 tcursor((set
) ? CURSOR_SAVE 
: CURSOR_LOAD
); 
1554                         case 47: /* swap screen */ 
1556                                 if (!allowaltscreen
) 
1558                                 alt 
= IS_SET(MODE_ALTSCREEN
); 
1560                                         tclearregion(0, 0, term
.col
-1, 
1563                                 if (set 
^ alt
) /* set is always 1 or 0 */ 
1569                                 tcursor((set
) ? CURSOR_SAVE 
: CURSOR_LOAD
); 
1571                         case 2004: /* 2004: bracketed paste mode */ 
1572                                 MODBIT(term
.mode
, set
, MODE_BRCKTPASTE
); 
1574                         /* Not implemented mouse modes. See comments there. */ 
1575                         case 1001: /* mouse highlight mode; can hang the 
1576                                       terminal by design when implemented. */ 
1577                         case 1005: /* UTF-8 mouse mode; will confuse 
1578                                       applications not supporting UTF-8 
1580                         case 1015: /* urxvt mangled mouse mode; incompatible 
1581                                       and can be mistaken for other control 
1585                                         "erresc: unknown private set/reset mode %d\n", 
1591                         case 0:  /* Error (IGNORED) */ 
1593                         case 2:  /* KAM -- keyboard action */ 
1594                                 MODBIT(term
.mode
, set
, MODE_KBDLOCK
); 
1596                         case 4:  /* IRM -- Insertion-replacement */ 
1597                                 MODBIT(term
.mode
, set
, MODE_INSERT
); 
1599                         case 12: /* SRM -- Send/Receive */ 
1600                                 MODBIT(term
.mode
, !set
, MODE_ECHO
); 
1602                         case 20: /* LNM -- Linefeed/new line */ 
1603                                 MODBIT(term
.mode
, set
, MODE_CRLF
); 
1607                                         "erresc: unknown set/reset mode %d\n", 
1621         switch (csiescseq
.mode
[0]) { 
1624                 fprintf(stderr
, "erresc: unknown csi "); 
1628         case '@': /* ICH -- Insert <n> blank char */ 
1629                 DEFAULT(csiescseq
.arg
[0], 1); 
1630                 tinsertblank(csiescseq
.arg
[0]); 
1632         case 'A': /* CUU -- Cursor <n> Up */ 
1633                 DEFAULT(csiescseq
.arg
[0], 1); 
1634                 tmoveto(term
.c
.x
, term
.c
.y
-csiescseq
.arg
[0]); 
1636         case 'B': /* CUD -- Cursor <n> Down */ 
1637         case 'e': /* VPR --Cursor <n> Down */ 
1638                 DEFAULT(csiescseq
.arg
[0], 1); 
1639                 tmoveto(term
.c
.x
, term
.c
.y
+csiescseq
.arg
[0]); 
1641         case 'i': /* MC -- Media Copy */ 
1642                 switch (csiescseq
.arg
[0]) { 
1647                         tdumpline(term
.c
.y
); 
1653                         term
.mode 
&= ~MODE_PRINT
; 
1656                         term
.mode 
|= MODE_PRINT
; 
1660         case 'c': /* DA -- Device Attributes */ 
1661                 if (csiescseq
.arg
[0] == 0) 
1662                         ttywrite(vtiden
, sizeof(vtiden
) - 1); 
1664         case 'C': /* CUF -- Cursor <n> Forward */ 
1665         case 'a': /* HPR -- Cursor <n> Forward */ 
1666                 DEFAULT(csiescseq
.arg
[0], 1); 
1667                 tmoveto(term
.c
.x
+csiescseq
.arg
[0], term
.c
.y
); 
1669         case 'D': /* CUB -- Cursor <n> Backward */ 
1670                 DEFAULT(csiescseq
.arg
[0], 1); 
1671                 tmoveto(term
.c
.x
-csiescseq
.arg
[0], term
.c
.y
); 
1673         case 'E': /* CNL -- Cursor <n> Down and first col */ 
1674                 DEFAULT(csiescseq
.arg
[0], 1); 
1675                 tmoveto(0, term
.c
.y
+csiescseq
.arg
[0]); 
1677         case 'F': /* CPL -- Cursor <n> Up and first col */ 
1678                 DEFAULT(csiescseq
.arg
[0], 1); 
1679                 tmoveto(0, term
.c
.y
-csiescseq
.arg
[0]); 
1681         case 'g': /* TBC -- Tabulation clear */ 
1682                 switch (csiescseq
.arg
[0]) { 
1683                 case 0: /* clear current tab stop */ 
1684                         term
.tabs
[term
.c
.x
] = 0; 
1686                 case 3: /* clear all the tabs */ 
1687                         memset(term
.tabs
, 0, term
.col 
* sizeof(*term
.tabs
)); 
1693         case 'G': /* CHA -- Move to <col> */ 
1695                 DEFAULT(csiescseq
.arg
[0], 1); 
1696                 tmoveto(csiescseq
.arg
[0]-1, term
.c
.y
); 
1698         case 'H': /* CUP -- Move to <row> <col> */ 
1700                 DEFAULT(csiescseq
.arg
[0], 1); 
1701                 DEFAULT(csiescseq
.arg
[1], 1); 
1702                 tmoveato(csiescseq
.arg
[1]-1, csiescseq
.arg
[0]-1); 
1704         case 'I': /* CHT -- Cursor Forward Tabulation <n> tab stops */ 
1705                 DEFAULT(csiescseq
.arg
[0], 1); 
1706                 tputtab(csiescseq
.arg
[0]); 
1708         case 'J': /* ED -- Clear screen */ 
1710                 switch (csiescseq
.arg
[0]) { 
1712                         tclearregion(term
.c
.x
, term
.c
.y
, term
.col
-1, term
.c
.y
); 
1713                         if (term
.c
.y 
< term
.row
-1) { 
1714                                 tclearregion(0, term
.c
.y
+1, term
.col
-1, 
1720                                 tclearregion(0, 0, term
.col
-1, term
.c
.y
-1); 
1721                         tclearregion(0, term
.c
.y
, term
.c
.x
, term
.c
.y
); 
1724                         tclearregion(0, 0, term
.col
-1, term
.row
-1); 
1730         case 'K': /* EL -- Clear line */ 
1731                 switch (csiescseq
.arg
[0]) { 
1733                         tclearregion(term
.c
.x
, term
.c
.y
, term
.col
-1, 
1737                         tclearregion(0, term
.c
.y
, term
.c
.x
, term
.c
.y
); 
1740                         tclearregion(0, term
.c
.y
, term
.col
-1, term
.c
.y
); 
1744         case 'S': /* SU -- Scroll <n> line up */ 
1745                 DEFAULT(csiescseq
.arg
[0], 1); 
1746                 tscrollup(term
.top
, csiescseq
.arg
[0]); 
1748         case 'T': /* SD -- Scroll <n> line down */ 
1749                 DEFAULT(csiescseq
.arg
[0], 1); 
1750                 tscrolldown(term
.top
, csiescseq
.arg
[0]); 
1752         case 'L': /* IL -- Insert <n> blank lines */ 
1753                 DEFAULT(csiescseq
.arg
[0], 1); 
1754                 tinsertblankline(csiescseq
.arg
[0]); 
1756         case 'l': /* RM -- Reset Mode */ 
1757                 tsetmode(csiescseq
.priv
, 0, csiescseq
.arg
, csiescseq
.narg
); 
1759         case 'M': /* DL -- Delete <n> lines */ 
1760                 DEFAULT(csiescseq
.arg
[0], 1); 
1761                 tdeleteline(csiescseq
.arg
[0]); 
1763         case 'X': /* ECH -- Erase <n> char */ 
1764                 DEFAULT(csiescseq
.arg
[0], 1); 
1765                 tclearregion(term
.c
.x
, term
.c
.y
, 
1766                                 term
.c
.x 
+ csiescseq
.arg
[0] - 1, term
.c
.y
); 
1768         case 'P': /* DCH -- Delete <n> char */ 
1769                 DEFAULT(csiescseq
.arg
[0], 1); 
1770                 tdeletechar(csiescseq
.arg
[0]); 
1772         case 'Z': /* CBT -- Cursor Backward Tabulation <n> tab stops */ 
1773                 DEFAULT(csiescseq
.arg
[0], 1); 
1774                 tputtab(-csiescseq
.arg
[0]); 
1776         case 'd': /* VPA -- Move to <row> */ 
1777                 DEFAULT(csiescseq
.arg
[0], 1); 
1778                 tmoveato(term
.c
.x
, csiescseq
.arg
[0]-1); 
1780         case 'h': /* SM -- Set terminal mode */ 
1781                 tsetmode(csiescseq
.priv
, 1, csiescseq
.arg
, csiescseq
.narg
); 
1783         case 'm': /* SGR -- Terminal attribute (color) */ 
1784                 tsetattr(csiescseq
.arg
, csiescseq
.narg
); 
1786         case 'n': /* DSR – Device Status Report (cursor position) */ 
1787                 if (csiescseq
.arg
[0] == 6) { 
1788                         len 
= snprintf(buf
, sizeof(buf
),"\033[%i;%iR", 
1789                                         term
.c
.y
+1, term
.c
.x
+1); 
1793         case 'r': /* DECSTBM -- Set Scrolling Region */ 
1794                 if (csiescseq
.priv
) { 
1797                         DEFAULT(csiescseq
.arg
[0], 1); 
1798                         DEFAULT(csiescseq
.arg
[1], term
.row
); 
1799                         tsetscroll(csiescseq
.arg
[0]-1, csiescseq
.arg
[1]-1); 
1803         case 's': /* DECSC -- Save cursor position (ANSI.SYS) */ 
1804                 tcursor(CURSOR_SAVE
); 
1806         case 'u': /* DECRC -- Restore cursor position (ANSI.SYS) */ 
1807                 tcursor(CURSOR_LOAD
); 
1810                 switch (csiescseq
.mode
[1]) { 
1811                 case 'q': /* DECSCUSR -- Set Cursor Style */ 
1812                         DEFAULT(csiescseq
.arg
[0], 1); 
1813                         if (!BETWEEN(csiescseq
.arg
[0], 0, 6)) { 
1816                         win
.cursor 
= csiescseq
.arg
[0]; 
1831         fprintf(stderr
, "ESC["); 
1832         for (i 
= 0; i 
< csiescseq
.len
; i
++) { 
1833                 c 
= csiescseq
.buf
[i
] & 0xff; 
1836                 } else if (c 
== '\n') { 
1837                         fprintf(stderr
, "(\\n)"); 
1838                 } else if (c 
== '\r') { 
1839                         fprintf(stderr
, "(\\r)"); 
1840                 } else if (c 
== 0x1b) { 
1841                         fprintf(stderr
, "(\\e)"); 
1843                         fprintf(stderr
, "(%02x)", c
); 
1852         memset(&csiescseq
, 0, sizeof(csiescseq
)); 
1861         term
.esc 
&= ~(ESC_STR_END
|ESC_STR
); 
1863         par 
= (narg 
= strescseq
.narg
) ? atoi(strescseq
.args
[0]) : 0; 
1865         switch (strescseq
.type
) { 
1866         case ']': /* OSC -- Operating System Command */ 
1872                                 xsettitle(strescseq
.args
[1]); 
1878                                 dec 
= base64dec(strescseq
.args
[2]); 
1880                                         xsetsel(dec
, CurrentTime
); 
1883                                         fprintf(stderr
, "erresc: invalid base64\n"); 
1887                 case 4: /* color set */ 
1890                         p 
= strescseq
.args
[2]; 
1892                 case 104: /* color reset, here p = NULL */ 
1893                         j 
= (narg 
> 1) ? atoi(strescseq
.args
[1]) : -1; 
1894                         if (xsetcolorname(j
, p
)) { 
1895                                 fprintf(stderr
, "erresc: invalid color %s\n", p
); 
1898                                  * TODO if defaultbg color is changed, borders 
1906         case 'k': /* old title set compatibility */ 
1907                 xsettitle(strescseq
.args
[0]); 
1909         case 'P': /* DCS -- Device Control String */ 
1910                 term
.mode 
|= ESC_DCS
; 
1911         case '_': /* APC -- Application Program Command */ 
1912         case '^': /* PM -- Privacy Message */ 
1916         fprintf(stderr
, "erresc: unknown str "); 
1924         char *p 
= strescseq
.buf
; 
1927         strescseq
.buf
[strescseq
.len
] = '\0'; 
1932         while (strescseq
.narg 
< STR_ARG_SIZ
) { 
1933                 strescseq
.args
[strescseq
.narg
++] = p
; 
1934                 while ((c 
= *p
) != ';' && c 
!= '\0') 
1948         fprintf(stderr
, "ESC%c", strescseq
.type
); 
1949         for (i 
= 0; i 
< strescseq
.len
; i
++) { 
1950                 c 
= strescseq
.buf
[i
] & 0xff; 
1954                 } else if (isprint(c
)) { 
1956                 } else if (c 
== '\n') { 
1957                         fprintf(stderr
, "(\\n)"); 
1958                 } else if (c 
== '\r') { 
1959                         fprintf(stderr
, "(\\r)"); 
1960                 } else if (c 
== 0x1b) { 
1961                         fprintf(stderr
, "(\\e)"); 
1963                         fprintf(stderr
, "(%02x)", c
); 
1966         fprintf(stderr
, "ESC\\\n"); 
1972         memset(&strescseq
, 0, sizeof(strescseq
)); 
1976 sendbreak(const Arg 
*arg
) 
1978         if (tcsendbreak(cmdfd
, 0)) 
1979                 perror("Error sending break"); 
1983 tprinter(char *s
, size_t len
) 
1985         if (iofd 
!= -1 && xwrite(iofd
, s
, len
) < 0) { 
1986                 fprintf(stderr
, "Error writing in %s:%s\n", 
1987                         opt_io
, strerror(errno
)); 
1994 iso14755(const Arg 
*arg
) 
1996         unsigned long id 
= xwinid(); 
1997         char cmd
[sizeof(ISO14755CMD
) + NUMMAXLEN(id
)]; 
1999         char *us
, *e
, codepoint
[9], uc
[UTF_SIZ
]; 
2000         unsigned long utf32
; 
2002         snprintf(cmd
, sizeof(cmd
), ISO14755CMD
, id
); 
2003         if (!(p 
= popen(cmd
, "r"))) 
2006         us 
= fgets(codepoint
, sizeof(codepoint
), p
); 
2009         if (!us 
|| *us 
== '\0' || *us 
== '-' || strlen(us
) > 7) 
2011         if ((utf32 
= strtoul(us
, &e
, 16)) == ULONG_MAX 
|| 
2012             (*e 
!= '\n' && *e 
!= '\0')) 
2015         ttysend(uc
, utf8encode(utf32
, uc
)); 
2019 toggleprinter(const Arg 
*arg
) 
2021         term
.mode 
^= MODE_PRINT
; 
2025 printscreen(const Arg 
*arg
) 
2031 printsel(const Arg 
*arg
) 
2041         if ((ptr 
= getsel())) { 
2042                 tprinter(ptr
, strlen(ptr
)); 
2053         bp 
= &term
.line
[n
][0]; 
2054         end 
= &bp
[MIN(tlinelen(n
), term
.col
) - 1]; 
2055         if (bp 
!= end 
|| bp
->u 
!= ' ') { 
2056                 for ( ;bp 
<= end
; ++bp
) 
2057                         tprinter(buf
, utf8encode(bp
->u
, buf
)); 
2067         for (i 
= 0; i 
< term
.row
; ++i
) 
2077                 while (x 
< term
.col 
&& n
--) 
2078                         for (++x
; x 
< term
.col 
&& !term
.tabs
[x
]; ++x
) 
2081                 while (x 
> 0 && n
++) 
2082                         for (--x
; x 
> 0 && !term
.tabs
[x
]; --x
) 
2085         term
.c
.x 
= LIMIT(x
, 0, term
.col
-1); 
2091         if (ISCONTROL(u
)) { /* control code */ 
2096                 } else if (u 
!= '\n' && u 
!= '\r' && u 
!= '\t') { 
2105 tdefutf8(char ascii
) 
2108                 term
.mode 
|= MODE_UTF8
; 
2109         else if (ascii 
== '@') 
2110                 term
.mode 
&= ~MODE_UTF8
; 
2114 tdeftran(char ascii
) 
2116         static char cs
[] = "0B"; 
2117         static int vcs
[] = {CS_GRAPHIC0
, CS_USA
}; 
2120         if ((p 
= strchr(cs
, ascii
)) == NULL
) { 
2121                 fprintf(stderr
, "esc unhandled charset: ESC ( %c\n", ascii
); 
2123                 term
.trantbl
[term
.icharset
] = vcs
[p 
- cs
]; 
2132         if (c 
== '8') { /* DEC screen alignment test. */ 
2133                 for (x 
= 0; x 
< term
.col
; ++x
) { 
2134                         for (y 
= 0; y 
< term
.row
; ++y
) 
2135                                 tsetchar('E', &term
.c
.attr
, x
, y
); 
2141 tstrsequence(uchar c
) 
2146         case 0x90:   /* DCS -- Device Control String */ 
2148                 term
.esc 
|= ESC_DCS
; 
2150         case 0x9f:   /* APC -- Application Program Command */ 
2153         case 0x9e:   /* PM -- Privacy Message */ 
2156         case 0x9d:   /* OSC -- Operating System Command */ 
2161         term
.esc 
|= ESC_STR
; 
2165 tcontrolcode(uchar ascii
) 
2172                 tmoveto(term
.c
.x
-1, term
.c
.y
); 
2175                 tmoveto(0, term
.c
.y
); 
2180                 /* go to first col if the mode is set */ 
2181                 tnewline(IS_SET(MODE_CRLF
)); 
2183         case '\a':   /* BEL */ 
2184                 if (term
.esc 
& ESC_STR_END
) { 
2185                         /* backwards compatibility to xterm */ 
2188                         if (!(win
.state 
& WIN_FOCUSED
)) 
2194         case '\033': /* ESC */ 
2196                 term
.esc 
&= ~(ESC_CSI
|ESC_ALTCHARSET
|ESC_TEST
); 
2197                 term
.esc 
|= ESC_START
; 
2199         case '\016': /* SO (LS1 -- Locking shift 1) */ 
2200         case '\017': /* SI (LS0 -- Locking shift 0) */ 
2201                 term
.charset 
= 1 - (ascii 
- '\016'); 
2203         case '\032': /* SUB */ 
2204                 tsetchar('?', &term
.c
.attr
, term
.c
.x
, term
.c
.y
); 
2205         case '\030': /* CAN */ 
2208         case '\005': /* ENQ (IGNORED) */ 
2209         case '\000': /* NUL (IGNORED) */ 
2210         case '\021': /* XON (IGNORED) */ 
2211         case '\023': /* XOFF (IGNORED) */ 
2212         case 0177:   /* DEL (IGNORED) */ 
2214         case 0x80:   /* TODO: PAD */ 
2215         case 0x81:   /* TODO: HOP */ 
2216         case 0x82:   /* TODO: BPH */ 
2217         case 0x83:   /* TODO: NBH */ 
2218         case 0x84:   /* TODO: IND */ 
2220         case 0x85:   /* NEL -- Next line */ 
2221                 tnewline(1); /* always go to first col */ 
2223         case 0x86:   /* TODO: SSA */ 
2224         case 0x87:   /* TODO: ESA */ 
2226         case 0x88:   /* HTS -- Horizontal tab stop */ 
2227                 term
.tabs
[term
.c
.x
] = 1; 
2229         case 0x89:   /* TODO: HTJ */ 
2230         case 0x8a:   /* TODO: VTS */ 
2231         case 0x8b:   /* TODO: PLD */ 
2232         case 0x8c:   /* TODO: PLU */ 
2233         case 0x8d:   /* TODO: RI */ 
2234         case 0x8e:   /* TODO: SS2 */ 
2235         case 0x8f:   /* TODO: SS3 */ 
2236         case 0x91:   /* TODO: PU1 */ 
2237         case 0x92:   /* TODO: PU2 */ 
2238         case 0x93:   /* TODO: STS */ 
2239         case 0x94:   /* TODO: CCH */ 
2240         case 0x95:   /* TODO: MW */ 
2241         case 0x96:   /* TODO: SPA */ 
2242         case 0x97:   /* TODO: EPA */ 
2243         case 0x98:   /* TODO: SOS */ 
2244         case 0x99:   /* TODO: SGCI */ 
2246         case 0x9a:   /* DECID -- Identify Terminal */ 
2247                 ttywrite(vtiden
, sizeof(vtiden
) - 1); 
2249         case 0x9b:   /* TODO: CSI */ 
2250         case 0x9c:   /* TODO: ST */ 
2252         case 0x90:   /* DCS -- Device Control String */ 
2253         case 0x9d:   /* OSC -- Operating System Command */ 
2254         case 0x9e:   /* PM -- Privacy Message */ 
2255         case 0x9f:   /* APC -- Application Program Command */ 
2256                 tstrsequence(ascii
); 
2259         /* only CAN, SUB, \a and C1 chars interrupt a sequence */ 
2260         term
.esc 
&= ~(ESC_STR_END
|ESC_STR
); 
2264  * returns 1 when the sequence is finished and it hasn't to read 
2265  * more characters for this sequence, otherwise 0 
2268 eschandle(uchar ascii
) 
2272                 term
.esc 
|= ESC_CSI
; 
2275                 term
.esc 
|= ESC_TEST
; 
2278                 term
.esc 
|= ESC_UTF8
; 
2280         case 'P': /* DCS -- Device Control String */ 
2281         case '_': /* APC -- Application Program Command */ 
2282         case '^': /* PM -- Privacy Message */ 
2283         case ']': /* OSC -- Operating System Command */ 
2284         case 'k': /* old title set compatibility */ 
2285                 tstrsequence(ascii
); 
2287         case 'n': /* LS2 -- Locking shift 2 */ 
2288         case 'o': /* LS3 -- Locking shift 3 */ 
2289                 term
.charset 
= 2 + (ascii 
- 'n'); 
2291         case '(': /* GZD4 -- set primary charset G0 */ 
2292         case ')': /* G1D4 -- set secondary charset G1 */ 
2293         case '*': /* G2D4 -- set tertiary charset G2 */ 
2294         case '+': /* G3D4 -- set quaternary charset G3 */ 
2295                 term
.icharset 
= ascii 
- '('; 
2296                 term
.esc 
|= ESC_ALTCHARSET
; 
2298         case 'D': /* IND -- Linefeed */ 
2299                 if (term
.c
.y 
== term
.bot
) { 
2300                         tscrollup(term
.top
, 1); 
2302                         tmoveto(term
.c
.x
, term
.c
.y
+1); 
2305         case 'E': /* NEL -- Next line */ 
2306                 tnewline(1); /* always go to first col */ 
2308         case 'H': /* HTS -- Horizontal tab stop */ 
2309                 term
.tabs
[term
.c
.x
] = 1; 
2311         case 'M': /* RI -- Reverse index */ 
2312                 if (term
.c
.y 
== term
.top
) { 
2313                         tscrolldown(term
.top
, 1); 
2315                         tmoveto(term
.c
.x
, term
.c
.y
-1); 
2318         case 'Z': /* DECID -- Identify Terminal */ 
2319                 ttywrite(vtiden
, sizeof(vtiden
) - 1); 
2321         case 'c': /* RIS -- Reset to inital state */ 
2326         case '=': /* DECPAM -- Application keypad */ 
2327                 term
.mode 
|= MODE_APPKEYPAD
; 
2329         case '>': /* DECPNM -- Normal keypad */ 
2330                 term
.mode 
&= ~MODE_APPKEYPAD
; 
2332         case '7': /* DECSC -- Save Cursor */ 
2333                 tcursor(CURSOR_SAVE
); 
2335         case '8': /* DECRC -- Restore Cursor */ 
2336                 tcursor(CURSOR_LOAD
); 
2338         case '\\': /* ST -- String Terminator */ 
2339                 if (term
.esc 
& ESC_STR_END
) 
2343                 fprintf(stderr
, "erresc: unknown sequence ESC 0x%02X '%c'\n", 
2344                         (uchar
) ascii
, isprint(ascii
)? ascii
:'.'); 
2358         control 
= ISCONTROL(u
); 
2359         if (!IS_SET(MODE_UTF8
) && !IS_SET(MODE_SIXEL
)) { 
2363                 len 
= utf8encode(u
, c
); 
2364                 if (!control 
&& (width 
= wcwidth(u
)) == -1) { 
2365                         memcpy(c
, "\357\277\275", 4); /* UTF_INVALID */ 
2370         if (IS_SET(MODE_PRINT
)) 
2374          * STR sequence must be checked before anything else 
2375          * because it uses all following characters until it 
2376          * receives a ESC, a SUB, a ST or any other C1 control 
2379         if (term
.esc 
& ESC_STR
) { 
2380                 if (u 
== '\a' || u 
== 030 || u 
== 032 || u 
== 033 || 
2382                         term
.esc 
&= ~(ESC_START
|ESC_STR
|ESC_DCS
); 
2383                         if (IS_SET(MODE_SIXEL
)) { 
2384                                 /* TODO: render sixel */; 
2385                                 term
.mode 
&= ~MODE_SIXEL
; 
2388                         term
.esc 
|= ESC_STR_END
; 
2389                         goto check_control_code
; 
2393                 if (IS_SET(MODE_SIXEL
)) { 
2394                         /* TODO: implement sixel mode */ 
2397                 if (term
.esc
&ESC_DCS 
&& strescseq
.len 
== 0 && u 
== 'q') 
2398                         term
.mode 
|= MODE_SIXEL
; 
2400                 if (strescseq
.len
+len 
>= sizeof(strescseq
.buf
)-1) { 
2402                          * Here is a bug in terminals. If the user never sends 
2403                          * some code to stop the str or esc command, then st 
2404                          * will stop responding. But this is better than 
2405                          * silently failing with unknown characters. At least 
2406                          * then users will report back. 
2408                          * In the case users ever get fixed, here is the code: 
2417                 memmove(&strescseq
.buf
[strescseq
.len
], c
, len
); 
2418                 strescseq
.len 
+= len
; 
2424          * Actions of control codes must be performed as soon they arrive 
2425          * because they can be embedded inside a control sequence, and 
2426          * they must not cause conflicts with sequences. 
2431                  * control codes are not shown ever 
2434         } else if (term
.esc 
& ESC_START
) { 
2435                 if (term
.esc 
& ESC_CSI
) { 
2436                         csiescseq
.buf
[csiescseq
.len
++] = u
; 
2437                         if (BETWEEN(u
, 0x40, 0x7E) 
2438                                         || csiescseq
.len 
>= \
 
2439                                         sizeof(csiescseq
.buf
)-1) { 
2445                 } else if (term
.esc 
& ESC_UTF8
) { 
2447                 } else if (term
.esc 
& ESC_ALTCHARSET
) { 
2449                 } else if (term
.esc 
& ESC_TEST
) { 
2454                         /* sequence already finished */ 
2458                  * All characters which form part of a sequence are not 
2463         if (sel
.ob
.x 
!= -1 && BETWEEN(term
.c
.y
, sel
.ob
.y
, sel
.oe
.y
)) 
2466         gp 
= &term
.line
[term
.c
.y
][term
.c
.x
]; 
2467         if (IS_SET(MODE_WRAP
) && (term
.c
.state 
& CURSOR_WRAPNEXT
)) { 
2468                 gp
->mode 
|= ATTR_WRAP
; 
2470                 gp 
= &term
.line
[term
.c
.y
][term
.c
.x
]; 
2473         if (IS_SET(MODE_INSERT
) && term
.c
.x
+width 
< term
.col
) 
2474                 memmove(gp
+width
, gp
, (term
.col 
- term
.c
.x 
- width
) * sizeof(Glyph
)); 
2476         if (term
.c
.x
+width 
> term
.col
) { 
2478                 gp 
= &term
.line
[term
.c
.y
][term
.c
.x
]; 
2481         tsetchar(u
, &term
.c
.attr
, term
.c
.x
, term
.c
.y
); 
2484                 gp
->mode 
|= ATTR_WIDE
; 
2485                 if (term
.c
.x
+1 < term
.col
) { 
2487                         gp
[1].mode 
= ATTR_WDUMMY
; 
2490         if (term
.c
.x
+width 
< term
.col
) { 
2491                 tmoveto(term
.c
.x
+width
, term
.c
.y
); 
2493                 term
.c
.state 
|= CURSOR_WRAPNEXT
; 
2498 tresize(int col
, int row
) 
2501         int minrow 
= MIN(row
, term
.row
); 
2502         int mincol 
= MIN(col
, term
.col
); 
2506         if (col 
< 1 || row 
< 1) { 
2508                         "tresize: error resizing to %dx%d\n", col
, row
); 
2513          * slide screen to keep cursor where we expect it - 
2514          * tscrollup would work here, but we can optimize to 
2515          * memmove because we're freeing the earlier lines 
2517         for (i 
= 0; i 
<= term
.c
.y 
- row
; i
++) { 
2521         /* ensure that both src and dst are not NULL */ 
2523                 memmove(term
.line
, term
.line 
+ i
, row 
* sizeof(Line
)); 
2524                 memmove(term
.alt
, term
.alt 
+ i
, row 
* sizeof(Line
)); 
2526         for (i 
+= row
; i 
< term
.row
; i
++) { 
2531         /* resize to new width */ 
2532         term
.specbuf 
= xrealloc(term
.specbuf
, col 
* sizeof(GlyphFontSpec
)); 
2534         /* resize to new height */ 
2535         term
.line 
= xrealloc(term
.line
, row 
* sizeof(Line
)); 
2536         term
.alt  
= xrealloc(term
.alt
,  row 
* sizeof(Line
)); 
2537         term
.dirty 
= xrealloc(term
.dirty
, row 
* sizeof(*term
.dirty
)); 
2538         term
.tabs 
= xrealloc(term
.tabs
, col 
* sizeof(*term
.tabs
)); 
2540         /* resize each row to new width, zero-pad if needed */ 
2541         for (i 
= 0; i 
< minrow
; i
++) { 
2542                 term
.line
[i
] = xrealloc(term
.line
[i
], col 
* sizeof(Glyph
)); 
2543                 term
.alt
[i
]  = xrealloc(term
.alt
[i
],  col 
* sizeof(Glyph
)); 
2546         /* allocate any new rows */ 
2547         for (/* i = minrow */; i 
< row
; i
++) { 
2548                 term
.line
[i
] = xmalloc(col 
* sizeof(Glyph
)); 
2549                 term
.alt
[i
] = xmalloc(col 
* sizeof(Glyph
)); 
2551         if (col 
> term
.col
) { 
2552                 bp 
= term
.tabs 
+ term
.col
; 
2554                 memset(bp
, 0, sizeof(*term
.tabs
) * (col 
- term
.col
)); 
2555                 while (--bp 
> term
.tabs 
&& !*bp
) 
2557                 for (bp 
+= tabspaces
; bp 
< term
.tabs 
+ col
; bp 
+= tabspaces
) 
2560         /* update terminal size */ 
2563         /* reset scrolling region */ 
2564         tsetscroll(0, row
-1); 
2565         /* make use of the LIMIT in tmoveto */ 
2566         tmoveto(term
.c
.x
, term
.c
.y
); 
2567         /* Clearing both screens (it makes dirty all lines) */ 
2569         for (i 
= 0; i 
< 2; i
++) { 
2570                 if (mincol 
< col 
&& 0 < minrow
) { 
2571                         tclearregion(mincol
, 0, col 
- 1, minrow 
- 1); 
2573                 if (0 < col 
&& minrow 
< row
) { 
2574                         tclearregion(0, minrow
, col 
- 1, row 
- 1); 
2577                 tcursor(CURSOR_LOAD
); 
2583 zoom(const Arg 
*arg
) 
2587         larg
.f 
= usedfontsize 
+ arg
->f
; 
2592 zoomabs(const Arg 
*arg
) 
2595         xloadfonts(usedfont
, arg
->f
); 
2603 zoomreset(const Arg 
*arg
) 
2607         if (defaultfontsize 
> 0) { 
2608                 larg
.f 
= defaultfontsize
; 
2616         xsettitle(opt_title 
? opt_title 
: "st"); 
2627 match(uint mask
, uint state
) 
2629         return mask 
== XK_ANY_MOD 
|| mask 
== (state 
& ~ignoremod
); 
2633 numlock(const Arg 
*dummy
) 
2639 kmap(KeySym k
, uint state
) 
2644         /* Check for mapped keys out of X11 function keys. */ 
2645         for (i 
= 0; i 
< LEN(mappedkeys
); i
++) { 
2646                 if (mappedkeys
[i
] == k
) 
2649         if (i 
== LEN(mappedkeys
)) { 
2650                 if ((k 
& 0xFFFF) < 0xFD00) 
2654         for (kp 
= key
; kp 
< key 
+ LEN(key
); kp
++) { 
2658                 if (!match(kp
->mask
, state
)) 
2661                 if (IS_SET(MODE_APPKEYPAD
) ? kp
->appkey 
< 0 : kp
->appkey 
> 0) 
2663                 if (term
.numlock 
&& kp
->appkey 
== 2) 
2666                 if (IS_SET(MODE_APPCURSOR
) ? kp
->appcursor 
< 0 : kp
->appcursor 
> 0) 
2669                 if (IS_SET(MODE_CRLF
) ? kp
->crlf 
< 0 : kp
->crlf 
> 0) 
2679 cresize(int width
, int height
) 
2688         col 
= (win
.w 
- 2 * borderpx
) / win
.cw
; 
2689         row 
= (win
.h 
- 2 * borderpx
) / win
.ch
; 
2698         die("usage: %s [-aiv] [-c class] [-f font] [-g geometry]" 
2699             " [-n name] [-o file]\n" 
2700             "          [-T title] [-t title] [-w windowid]" 
2701             " [[-e] command [args ...]]\n" 
2702             "       %s [-aiv] [-c class] [-f font] [-g geometry]" 
2703             " [-n name] [-o file]\n" 
2704             "          [-T title] [-t title] [-w windowid] -l line" 
2705             " [stty_args ...]\n", argv0
, argv0
);