st.c - st - Simple Terminal
 (HTM) git clone git://r-36.net/st
 (DIR) Log
 (DIR) Files
 (DIR) Refs
 (DIR) README
 (DIR) LICENSE
       ---
       st.c (100946B)
       ---
            1 /* See LICENSE for license details. */
            2 #include <ctype.h>
            3 #include <errno.h>
            4 #include <fcntl.h>
            5 #include <limits.h>
            6 #include <locale.h>
            7 #include <pwd.h>
            8 #include <stdarg.h>
            9 #include <stdio.h>
           10 #include <stdlib.h>
           11 #include <string.h>
           12 #include <signal.h>
           13 #include <stdint.h>
           14 #include <sys/ioctl.h>
           15 #include <sys/select.h>
           16 #include <sys/stat.h>
           17 #include <sys/time.h>
           18 #include <sys/types.h>
           19 #include <sys/wait.h>
           20 #include <termios.h>
           21 #include <time.h>
           22 #include <unistd.h>
           23 #include <libgen.h>
           24 #include <X11/Xatom.h>
           25 #include <X11/Xlib.h>
           26 #include <X11/Xutil.h>
           27 #include <X11/cursorfont.h>
           28 #include <X11/keysym.h>
           29 #include <X11/Xft/Xft.h>
           30 #include <X11/XKBlib.h>
           31 #include <fontconfig/fontconfig.h>
           32 #include <wchar.h>
           33 
           34 #include "arg.h"
           35 
           36 char *argv0;
           37 
           38 #define Glyph Glyph_
           39 #define Font Font_
           40 
           41 #if   defined(__linux)
           42  #include <pty.h>
           43 #elif defined(__OpenBSD__) || defined(__NetBSD__) || defined(__APPLE__)
           44  #include <util.h>
           45 #elif defined(__FreeBSD__) || defined(__DragonFly__)
           46  #include <libutil.h>
           47 #endif
           48 
           49 
           50 /* XEMBED messages */
           51 #define XEMBED_FOCUS_IN  4
           52 #define XEMBED_FOCUS_OUT 5
           53 
           54 /* Arbitrary sizes */
           55 #define UTF_INVALID   0xFFFD
           56 #define UTF_SIZ       4
           57 #define ESC_BUF_SIZ   (128*UTF_SIZ)
           58 #define ESC_ARG_SIZ   16
           59 #define STR_BUF_SIZ   ESC_BUF_SIZ
           60 #define STR_ARG_SIZ   ESC_ARG_SIZ
           61 #define XK_ANY_MOD    UINT_MAX
           62 #define XK_NO_MOD     0
           63 #define XK_SWITCH_MOD (1<<13)
           64 
           65 /* macros */
           66 #define MIN(a, b)                ((a) < (b) ? (a) : (b))
           67 #define MAX(a, b)                ((a) < (b) ? (b) : (a))
           68 #define LEN(a)                        (sizeof(a) / sizeof(a)[0])
           69 #define NUMMAXLEN(x)                ((int)(sizeof(x) * 2.56 + 0.5) + 1)
           70 #define DEFAULT(a, b)                (a) = (a) ? (a) : (b)
           71 #define BETWEEN(x, a, b)        ((a) <= (x) && (x) <= (b))
           72 #define DIVCEIL(n, d)                (((n) + ((d) - 1)) / (d))
           73 #define ISCONTROLC0(c)                (BETWEEN(c, 0, 0x1f) || (c) == '\177')
           74 #define ISCONTROLC1(c)                (BETWEEN(c, 0x80, 0x9f))
           75 #define ISCONTROL(c)                (ISCONTROLC0(c) || ISCONTROLC1(c))
           76 #define ISDELIM(u)                (utf8strchr(worddelimiters, u) != NULL)
           77 #define LIMIT(x, a, b)                (x) = (x) < (a) ? (a) : (x) > (b) ? (b) : (x)
           78 #define ATTRCMP(a, b)                ((a).mode != (b).mode || (a).fg != (b).fg || \
           79                                 (a).bg != (b).bg)
           80 #define IS_SET(flag)                ((term.mode & (flag)) != 0)
           81 #define TIMEDIFF(t1, t2)        ((t1.tv_sec-t2.tv_sec)*1000 + \
           82                                 (t1.tv_nsec-t2.tv_nsec)/1E6)
           83 #define MODBIT(x, set, bit)        ((set) ? ((x) |= (bit)) : ((x) &= ~(bit)))
           84 
           85 #define TRUECOLOR(r,g,b)        (1 << 24 | (r) << 16 | (g) << 8 | (b))
           86 #define IS_TRUECOL(x)                (1 << 24 & (x))
           87 #define TRUERED(x)                (((x) & 0xff0000) >> 8)
           88 #define TRUEGREEN(x)                (((x) & 0xff00))
           89 #define TRUEBLUE(x)                (((x) & 0xff) << 8)
           90 
           91 /* constants */
           92 #define ISO14755CMD                "dmenu -w %lu -p codepoint: </dev/null"
           93 
           94 enum glyph_attribute {
           95         ATTR_NULL       = 0,
           96         ATTR_BOLD       = 1 << 0,
           97         ATTR_FAINT      = 1 << 1,
           98         ATTR_ITALIC     = 1 << 2,
           99         ATTR_UNDERLINE  = 1 << 3,
          100         ATTR_BLINK      = 1 << 4,
          101         ATTR_REVERSE    = 1 << 5,
          102         ATTR_INVISIBLE  = 1 << 6,
          103         ATTR_STRUCK     = 1 << 7,
          104         ATTR_WRAP       = 1 << 8,
          105         ATTR_WIDE       = 1 << 9,
          106         ATTR_WDUMMY     = 1 << 10,
          107         ATTR_BOLD_FAINT = ATTR_BOLD | ATTR_FAINT,
          108 };
          109 
          110 enum cursor_movement {
          111         CURSOR_SAVE,
          112         CURSOR_LOAD
          113 };
          114 
          115 enum cursor_state {
          116         CURSOR_DEFAULT  = 0,
          117         CURSOR_WRAPNEXT = 1,
          118         CURSOR_ORIGIN   = 2
          119 };
          120 
          121 enum term_mode {
          122         MODE_WRAP        = 1 << 0,
          123         MODE_INSERT      = 1 << 1,
          124         MODE_APPKEYPAD   = 1 << 2,
          125         MODE_ALTSCREEN   = 1 << 3,
          126         MODE_CRLF        = 1 << 4,
          127         MODE_MOUSEBTN    = 1 << 5,
          128         MODE_MOUSEMOTION = 1 << 6,
          129         MODE_REVERSE     = 1 << 7,
          130         MODE_KBDLOCK     = 1 << 8,
          131         MODE_HIDE        = 1 << 9,
          132         MODE_ECHO        = 1 << 10,
          133         MODE_APPCURSOR   = 1 << 11,
          134         MODE_MOUSESGR    = 1 << 12,
          135         MODE_8BIT        = 1 << 13,
          136         MODE_BLINK       = 1 << 14,
          137         MODE_FBLINK      = 1 << 15,
          138         MODE_FOCUS       = 1 << 16,
          139         MODE_MOUSEX10    = 1 << 17,
          140         MODE_MOUSEMANY   = 1 << 18,
          141         MODE_BRCKTPASTE  = 1 << 19,
          142         MODE_PRINT       = 1 << 20,
          143         MODE_UTF8        = 1 << 21,
          144         MODE_SIXEL       = 1 << 22,
          145         MODE_MOUSE       = MODE_MOUSEBTN|MODE_MOUSEMOTION|MODE_MOUSEX10\
          146                           |MODE_MOUSEMANY,
          147 };
          148 
          149 enum charset {
          150         CS_GRAPHIC0,
          151         CS_GRAPHIC1,
          152         CS_UK,
          153         CS_USA,
          154         CS_MULTI,
          155         CS_GER,
          156         CS_FIN
          157 };
          158 
          159 enum escape_state {
          160         ESC_START      = 1,
          161         ESC_CSI        = 2,
          162         ESC_STR        = 4,  /* OSC, PM, APC */
          163         ESC_ALTCHARSET = 8,
          164         ESC_STR_END    = 16, /* a final string was encountered */
          165         ESC_TEST       = 32, /* Enter in test mode */
          166         ESC_UTF8       = 64,
          167         ESC_DCS        =128,
          168 };
          169 
          170 enum window_state {
          171         WIN_VISIBLE = 1,
          172         WIN_FOCUSED = 2
          173 };
          174 
          175 enum selection_mode {
          176         SEL_IDLE = 0,
          177         SEL_EMPTY = 1,
          178         SEL_READY = 2
          179 };
          180 
          181 enum selection_type {
          182         SEL_REGULAR = 1,
          183         SEL_RECTANGULAR = 2
          184 };
          185 
          186 enum selection_snap {
          187         SNAP_WORD = 1,
          188         SNAP_LINE = 2
          189 };
          190 
          191 typedef unsigned char uchar;
          192 typedef unsigned int uint;
          193 typedef unsigned long ulong;
          194 typedef unsigned short ushort;
          195 
          196 typedef uint_least32_t Rune;
          197 
          198 typedef XftDraw *Draw;
          199 typedef XftColor Color;
          200 
          201 typedef struct {
          202         Rune u;           /* character code */
          203         ushort mode;      /* attribute flags */
          204         uint32_t fg;      /* foreground  */
          205         uint32_t bg;      /* background  */
          206 } Glyph;
          207 
          208 typedef Glyph *Line;
          209 
          210 typedef struct {
          211         Glyph attr; /* current char attributes */
          212         int x;
          213         int y;
          214         char state;
          215 } TCursor;
          216 
          217 /* CSI Escape sequence structs */
          218 /* ESC '[' [[ [<priv>] <arg> [;]] <mode> [<mode>]] */
          219 typedef struct {
          220         char buf[ESC_BUF_SIZ]; /* raw string */
          221         int len;               /* raw string length */
          222         char priv;
          223         int arg[ESC_ARG_SIZ];
          224         int narg;              /* nb of args */
          225         char mode[2];
          226 } CSIEscape;
          227 
          228 /* STR Escape sequence structs */
          229 /* ESC type [[ [<priv>] <arg> [;]] <mode>] ESC '\' */
          230 typedef struct {
          231         char type;             /* ESC type ... */
          232         char buf[STR_BUF_SIZ]; /* raw string */
          233         int len;               /* raw string length */
          234         char *args[STR_ARG_SIZ];
          235         int narg;              /* nb of args */
          236 } STREscape;
          237 
          238 /* Internal representation of the screen */
          239 typedef struct {
          240         int row;      /* nb row */
          241         int col;      /* nb col */
          242         Line *line;   /* screen */
          243         Line *alt;    /* alternate screen */
          244         int *dirty;  /* dirtyness of lines */
          245         XftGlyphFontSpec *specbuf; /* font spec buffer used for rendering */
          246         TCursor c;    /* cursor */
          247         int top;      /* top    scroll limit */
          248         int bot;      /* bottom scroll limit */
          249         int mode;     /* terminal mode flags */
          250         int esc;      /* escape state flags */
          251         char trantbl[4]; /* charset table translation */
          252         int charset;  /* current charset */
          253         int icharset; /* selected charset for sequence */
          254         int numlock; /* lock numbers in keyboard */
          255         int *tabs;
          256 } Term;
          257 
          258 /* Purely graphic info */
          259 typedef struct {
          260         Display *dpy;
          261         Colormap cmap;
          262         Window win;
          263         Drawable buf;
          264         Atom xembed, wmdeletewin, netwmname, netwmpid;
          265         XIM xim;
          266         XIC xic;
          267         Draw draw;
          268         Visual *vis;
          269         XSetWindowAttributes attrs;
          270         int scr;
          271         int isfixed; /* is fixed geometry? */
          272         int l, t; /* left and top offset */
          273         int gm; /* geometry mask */
          274         int tw, th; /* tty width and height */
          275         int w, h; /* window width and height */
          276         int ch; /* char height */
          277         int cw; /* char width  */
          278         char state; /* focus, redraw, visible */
          279         int cursor; /* cursor style */
          280 } XWindow;
          281 
          282 typedef struct {
          283         uint b;
          284         uint mask;
          285         char *s;
          286 } MouseShortcut;
          287 
          288 typedef struct {
          289         KeySym k;
          290         uint mask;
          291         char *s;
          292         /* three valued logic variables: 0 indifferent, 1 on, -1 off */
          293         signed char appkey;    /* application keypad */
          294         signed char appcursor; /* application cursor */
          295         signed char crlf;      /* crlf mode          */
          296 } Key;
          297 
          298 typedef struct {
          299         int mode;
          300         int type;
          301         int snap;
          302         /*
          303          * Selection variables:
          304          * nb – normalized coordinates of the beginning of the selection
          305          * ne – normalized coordinates of the end of the selection
          306          * ob – original coordinates of the beginning of the selection
          307          * oe – original coordinates of the end of the selection
          308          */
          309         struct {
          310                 int x, y;
          311         } nb, ne, ob, oe;
          312 
          313         char *primary, *clipboard;
          314         Atom xtarget;
          315         int alt;
          316         struct timespec tclick1;
          317         struct timespec tclick2;
          318 } Selection;
          319 
          320 typedef union {
          321         int i;
          322         uint ui;
          323         float f;
          324         const void *v;
          325 } Arg;
          326 
          327 typedef struct {
          328         uint mod;
          329         KeySym keysym;
          330         void (*func)(const Arg *);
          331         const Arg arg;
          332 } Shortcut;
          333 
          334 /* function definitions used in config.h */
          335 static void clipcopy(const Arg *);
          336 static void clippaste(const Arg *);
          337 static void numlock(const Arg *);
          338 static void selpaste(const Arg *);
          339 static void xzoom(const Arg *);
          340 static void xzoomabs(const Arg *);
          341 static void xzoomreset(const Arg *);
          342 static void printsel(const Arg *);
          343 static void printscreen(const Arg *) ;
          344 static void iso14755(const Arg *);
          345 static void toggleprinter(const Arg *);
          346 static void sendbreak(const Arg *);
          347 static void externalpipe(const Arg *);
          348 
          349 /* Config.h for applying patches and the configuration. */
          350 #include "config.h"
          351 
          352 /* Font structure */
          353 typedef struct {
          354         int height;
          355         int width;
          356         int ascent;
          357         int descent;
          358         int badslant;
          359         int badweight;
          360         short lbearing;
          361         short rbearing;
          362         XftFont *match;
          363         FcFontSet *set;
          364         FcPattern *pattern;
          365 } Font;
          366 
          367 /* Drawing Context */
          368 typedef struct {
          369         Color col[MAX(LEN(colorname), 256)];
          370         Font font, bfont, ifont, ibfont;
          371         GC gc;
          372 } DC;
          373 
          374 static void die(const char *, ...);
          375 static void draw(void);
          376 static void redraw(void);
          377 static void drawregion(int, int, int, int);
          378 static void execsh(void);
          379 static void stty(void);
          380 static void sigchld(int);
          381 static void run(void);
          382 
          383 static void csidump(void);
          384 static void csihandle(void);
          385 static void csiparse(void);
          386 static void csireset(void);
          387 static int eschandle(uchar);
          388 static void strdump(void);
          389 static void strhandle(void);
          390 static void strparse(void);
          391 static void strreset(void);
          392 
          393 static int tattrset(int);
          394 static void tprinter(char *, size_t);
          395 static void tdumpsel(void);
          396 static void tdumpline(int);
          397 static void tdump(void);
          398 static void tclearregion(int, int, int, int);
          399 static void tcursor(int);
          400 static void tdeletechar(int);
          401 static void tdeleteline(int);
          402 static void tinsertblank(int);
          403 static void tinsertblankline(int);
          404 static int tlinelen(int);
          405 static void tmoveto(int, int);
          406 static void tmoveato(int, int);
          407 static void tnew(int, int);
          408 static void tnewline(int);
          409 static void tputtab(int);
          410 static void tputc(Rune);
          411 static void treset(void);
          412 static void tresize(int, int);
          413 static void tscrollup(int, int);
          414 static void tscrolldown(int, int);
          415 static void tsetattr(int *, int);
          416 static void tsetchar(Rune, Glyph *, int, int);
          417 static void tsetscroll(int, int);
          418 static void tswapscreen(void);
          419 static void tsetdirt(int, int);
          420 static void tsetdirtattr(int);
          421 static void tsetmode(int, int, int *, int);
          422 static void tfulldirt(void);
          423 static void techo(Rune);
          424 static void tcontrolcode(uchar );
          425 static void tdectest(char );
          426 static void tdefutf8(char);
          427 static int32_t tdefcolor(int *, int *, int);
          428 static void tdeftran(char);
          429 static inline int match(uint, uint);
          430 static void ttynew(void);
          431 static size_t ttyread(void);
          432 static void ttyresize(void);
          433 static void ttysend(char *, size_t);
          434 static void ttywrite(const char *, size_t);
          435 static void tstrsequence(uchar);
          436 
          437 static inline ushort sixd_to_16bit(int);
          438 static int xmakeglyphfontspecs(XftGlyphFontSpec *, const Glyph *, int, int, int);
          439 static void xdrawglyphfontspecs(const XftGlyphFontSpec *, Glyph, int, int, int);
          440 static void xdrawglyph(Glyph, int, int);
          441 static void xhints(void);
          442 static void xclear(int, int, int, int);
          443 static void xdrawcursor(void);
          444 static void xinit(void);
          445 static void xloadcols(void);
          446 static int xsetcolorname(int, const char *);
          447 static int xgeommasktogravity(int);
          448 static int xloadfont(Font *, FcPattern *);
          449 static void xloadfonts(char *, double);
          450 static void xsettitle(char *);
          451 static void xresettitle(void);
          452 static void xsetpointermotion(int);
          453 static void xseturgency(int);
          454 static void xsetsel(char *, Time);
          455 static void xunloadfont(Font *);
          456 static void xunloadfonts(void);
          457 static void xresize(int, int);
          458 
          459 static void expose(XEvent *);
          460 static void visibility(XEvent *);
          461 static void unmap(XEvent *);
          462 static char *kmap(KeySym, uint);
          463 static void kpress(XEvent *);
          464 static void cmessage(XEvent *);
          465 static void cresize(int, int);
          466 static void resize(XEvent *);
          467 static void focus(XEvent *);
          468 static void brelease(XEvent *);
          469 static void bpress(XEvent *);
          470 static void bmotion(XEvent *);
          471 static void propnotify(XEvent *);
          472 static void selnotify(XEvent *);
          473 static void selclear(XEvent *);
          474 static void selrequest(XEvent *);
          475 
          476 static void selinit(void);
          477 static void selnormalize(void);
          478 static inline int selected(int, int);
          479 static char *getsel(void);
          480 static void selcopy(Time);
          481 static void selscroll(int, int);
          482 static void selsnap(int *, int *, int);
          483 static int x2col(int);
          484 static int y2row(int);
          485 static void getbuttoninfo(XEvent *);
          486 static void mousereport(XEvent *);
          487 
          488 static size_t utf8decode(char *, Rune *, size_t);
          489 static Rune utf8decodebyte(char, size_t *);
          490 static size_t utf8encode(Rune, char *);
          491 static char utf8encodebyte(Rune, size_t);
          492 static char *utf8strchr(char *s, Rune u);
          493 static size_t utf8validate(Rune *, size_t);
          494 
          495 static ssize_t xwrite(int, const char *, size_t);
          496 static void *xmalloc(size_t);
          497 static void *xrealloc(void *, size_t);
          498 static char *xstrdup(char *);
          499 
          500 static void usage(void);
          501 
          502 static void (*handler[LASTEvent])(XEvent *) = {
          503         [KeyPress] = kpress,
          504         [ClientMessage] = cmessage,
          505         [ConfigureNotify] = resize,
          506         [VisibilityNotify] = visibility,
          507         [UnmapNotify] = unmap,
          508         [Expose] = expose,
          509         [FocusIn] = focus,
          510         [FocusOut] = focus,
          511         [MotionNotify] = bmotion,
          512         [ButtonPress] = bpress,
          513         [ButtonRelease] = brelease,
          514 /*
          515  * Uncomment if you want the selection to disappear when you select something
          516  * different in another window.
          517  */
          518 /*        [SelectionClear] = selclear, */
          519         [SelectionNotify] = selnotify,
          520 /*
          521  * PropertyNotify is only turned on when there is some INCR transfer happening
          522  * for the selection retrieval.
          523  */
          524         [PropertyNotify] = propnotify,
          525         [SelectionRequest] = selrequest,
          526 };
          527 
          528 /* Globals */
          529 static DC dc;
          530 static XWindow xw;
          531 static Term term;
          532 static CSIEscape csiescseq;
          533 static STREscape strescseq;
          534 static int cmdfd;
          535 static pid_t pid;
          536 static Selection sel;
          537 static int iofd = 1;
          538 static char **opt_cmd  = NULL;
          539 static char *opt_class = NULL;
          540 static char *opt_embed = NULL;
          541 static char *opt_font  = NULL;
          542 static char *opt_io    = NULL;
          543 static char *opt_line  = NULL;
          544 static char *opt_name  = NULL;
          545 static char *opt_title = NULL;
          546 static int oldbutton   = 3; /* button event on startup: 3 = release */
          547 
          548 static char *usedfont = NULL;
          549 static double usedfontsize = 0;
          550 static double defaultfontsize = 0;
          551 
          552 static uchar utfbyte[UTF_SIZ + 1] = {0x80,    0, 0xC0, 0xE0, 0xF0};
          553 static uchar utfmask[UTF_SIZ + 1] = {0xC0, 0x80, 0xE0, 0xF0, 0xF8};
          554 static Rune utfmin[UTF_SIZ + 1] = {       0,    0,  0x80,  0x800,  0x10000};
          555 static Rune utfmax[UTF_SIZ + 1] = {0x10FFFF, 0x7F, 0x7FF, 0xFFFF, 0x10FFFF};
          556 
          557 /* Font Ring Cache */
          558 enum {
          559         FRC_NORMAL,
          560         FRC_ITALIC,
          561         FRC_BOLD,
          562         FRC_ITALICBOLD
          563 };
          564 
          565 typedef struct {
          566         XftFont *font;
          567         int flags;
          568         Rune unicodep;
          569 } Fontcache;
          570 
          571 /* Fontcache is an array now. A new font will be appended to the array. */
          572 static Fontcache frc[16];
          573 static int frclen = 0;
          574 
          575 ssize_t
          576 xwrite(int fd, const char *s, size_t len)
          577 {
          578         size_t aux = len;
          579         ssize_t r;
          580 
          581         while (len > 0) {
          582                 r = write(fd, s, len);
          583                 if (r < 0)
          584                         return r;
          585                 len -= r;
          586                 s += r;
          587         }
          588 
          589         return aux;
          590 }
          591 
          592 void *
          593 xmalloc(size_t len)
          594 {
          595         void *p = malloc(len);
          596 
          597         if (!p)
          598                 die("Out of memory\n");
          599 
          600         return p;
          601 }
          602 
          603 void *
          604 xrealloc(void *p, size_t len)
          605 {
          606         if ((p = realloc(p, len)) == NULL)
          607                 die("Out of memory\n");
          608 
          609         return p;
          610 }
          611 
          612 char *
          613 xstrdup(char *s)
          614 {
          615         if ((s = strdup(s)) == NULL)
          616                 die("Out of memory\n");
          617 
          618         return s;
          619 }
          620 
          621 size_t
          622 utf8decode(char *c, Rune *u, size_t clen)
          623 {
          624         size_t i, j, len, type;
          625         Rune udecoded;
          626 
          627         *u = UTF_INVALID;
          628         if (!clen)
          629                 return 0;
          630         udecoded = utf8decodebyte(c[0], &len);
          631         if (!BETWEEN(len, 1, UTF_SIZ))
          632                 return 1;
          633         for (i = 1, j = 1; i < clen && j < len; ++i, ++j) {
          634                 udecoded = (udecoded << 6) | utf8decodebyte(c[i], &type);
          635                 if (type != 0)
          636                         return j;
          637         }
          638         if (j < len)
          639                 return 0;
          640         *u = udecoded;
          641         utf8validate(u, len);
          642 
          643         return len;
          644 }
          645 
          646 Rune
          647 utf8decodebyte(char c, size_t *i)
          648 {
          649         for (*i = 0; *i < LEN(utfmask); ++(*i))
          650                 if (((uchar)c & utfmask[*i]) == utfbyte[*i])
          651                         return (uchar)c & ~utfmask[*i];
          652 
          653         return 0;
          654 }
          655 
          656 size_t
          657 utf8encode(Rune u, char *c)
          658 {
          659         size_t len, i;
          660 
          661         len = utf8validate(&u, 0);
          662         if (len > UTF_SIZ)
          663                 return 0;
          664 
          665         for (i = len - 1; i != 0; --i) {
          666                 c[i] = utf8encodebyte(u, 0);
          667                 u >>= 6;
          668         }
          669         c[0] = utf8encodebyte(u, len);
          670 
          671         return len;
          672 }
          673 
          674 char
          675 utf8encodebyte(Rune u, size_t i)
          676 {
          677         return utfbyte[i] | (u & ~utfmask[i]);
          678 }
          679 
          680 char *
          681 utf8strchr(char *s, Rune u)
          682 {
          683         Rune r;
          684         size_t i, j, len;
          685 
          686         len = strlen(s);
          687         for (i = 0, j = 0; i < len; i += j) {
          688                 if (!(j = utf8decode(&s[i], &r, len - i)))
          689                         break;
          690                 if (r == u)
          691                         return &(s[i]);
          692         }
          693 
          694         return NULL;
          695 }
          696 
          697 size_t
          698 utf8validate(Rune *u, size_t i)
          699 {
          700         if (!BETWEEN(*u, utfmin[i], utfmax[i]) || BETWEEN(*u, 0xD800, 0xDFFF))
          701                 *u = UTF_INVALID;
          702         for (i = 1; *u > utfmax[i]; ++i)
          703                 ;
          704 
          705         return i;
          706 }
          707 
          708 void
          709 selinit(void)
          710 {
          711         clock_gettime(CLOCK_MONOTONIC, &sel.tclick1);
          712         clock_gettime(CLOCK_MONOTONIC, &sel.tclick2);
          713         sel.mode = SEL_IDLE;
          714         sel.snap = 0;
          715         sel.ob.x = -1;
          716         sel.primary = NULL;
          717         sel.clipboard = NULL;
          718         sel.xtarget = XInternAtom(xw.dpy, "UTF8_STRING", 0);
          719         if (sel.xtarget == None)
          720                 sel.xtarget = XA_STRING;
          721 }
          722 
          723 int
          724 x2col(int x)
          725 {
          726         x -= borderpx;
          727         x /= xw.cw;
          728 
          729         return LIMIT(x, 0, term.col-1);
          730 }
          731 
          732 int
          733 y2row(int y)
          734 {
          735         y -= borderpx;
          736         y /= xw.ch;
          737 
          738         return LIMIT(y, 0, term.row-1);
          739 }
          740 
          741 int
          742 tlinelen(int y)
          743 {
          744         int i = term.col;
          745 
          746         if (term.line[y][i - 1].mode & ATTR_WRAP)
          747                 return i;
          748 
          749         while (i > 0 && term.line[y][i - 1].u == ' ')
          750                 --i;
          751 
          752         return i;
          753 }
          754 
          755 void
          756 selnormalize(void)
          757 {
          758         int i;
          759 
          760         if (sel.type == SEL_REGULAR && sel.ob.y != sel.oe.y) {
          761                 sel.nb.x = sel.ob.y < sel.oe.y ? sel.ob.x : sel.oe.x;
          762                 sel.ne.x = sel.ob.y < sel.oe.y ? sel.oe.x : sel.ob.x;
          763         } else {
          764                 sel.nb.x = MIN(sel.ob.x, sel.oe.x);
          765                 sel.ne.x = MAX(sel.ob.x, sel.oe.x);
          766         }
          767         sel.nb.y = MIN(sel.ob.y, sel.oe.y);
          768         sel.ne.y = MAX(sel.ob.y, sel.oe.y);
          769 
          770         selsnap(&sel.nb.x, &sel.nb.y, -1);
          771         selsnap(&sel.ne.x, &sel.ne.y, +1);
          772 
          773         /* expand selection over line breaks */
          774         if (sel.type == SEL_RECTANGULAR)
          775                 return;
          776         i = tlinelen(sel.nb.y);
          777         if (i < sel.nb.x)
          778                 sel.nb.x = i;
          779         if (tlinelen(sel.ne.y) <= sel.ne.x)
          780                 sel.ne.x = term.col - 1;
          781 }
          782 
          783 int
          784 selected(int x, int y)
          785 {
          786         if (sel.mode == SEL_EMPTY)
          787                 return 0;
          788 
          789         if (sel.type == SEL_RECTANGULAR)
          790                 return BETWEEN(y, sel.nb.y, sel.ne.y)
          791                     && BETWEEN(x, sel.nb.x, sel.ne.x);
          792 
          793         return BETWEEN(y, sel.nb.y, sel.ne.y)
          794             && (y != sel.nb.y || x >= sel.nb.x)
          795             && (y != sel.ne.y || x <= sel.ne.x);
          796 }
          797 
          798 void
          799 selsnap(int *x, int *y, int direction)
          800 {
          801         int newx, newy, xt, yt;
          802         int delim, prevdelim;
          803         Glyph *gp, *prevgp;
          804 
          805         switch (sel.snap) {
          806         case SNAP_WORD:
          807                 /*
          808                  * Snap around if the word wraps around at the end or
          809                  * beginning of a line.
          810                  */
          811                 prevgp = &term.line[*y][*x];
          812                 prevdelim = ISDELIM(prevgp->u);
          813                 for (;;) {
          814                         newx = *x + direction;
          815                         newy = *y;
          816                         if (!BETWEEN(newx, 0, term.col - 1)) {
          817                                 newy += direction;
          818                                 newx = (newx + term.col) % term.col;
          819                                 if (!BETWEEN(newy, 0, term.row - 1))
          820                                         break;
          821 
          822                                 if (direction > 0)
          823                                         yt = *y, xt = *x;
          824                                 else
          825                                         yt = newy, xt = newx;
          826                                 if (!(term.line[yt][xt].mode & ATTR_WRAP))
          827                                         break;
          828                         }
          829 
          830                         if (newx >= tlinelen(newy))
          831                                 break;
          832 
          833                         gp = &term.line[newy][newx];
          834                         delim = ISDELIM(gp->u);
          835                         if (!(gp->mode & ATTR_WDUMMY) && (delim != prevdelim
          836                                         || (delim && gp->u != prevgp->u)))
          837                                 break;
          838 
          839                         *x = newx;
          840                         *y = newy;
          841                         prevgp = gp;
          842                         prevdelim = delim;
          843                 }
          844                 break;
          845         case SNAP_LINE:
          846                 /*
          847                  * Snap around if the the previous line or the current one
          848                  * has set ATTR_WRAP at its end. Then the whole next or
          849                  * previous line will be selected.
          850                  */
          851                 *x = (direction < 0) ? 0 : term.col - 1;
          852                 if (direction < 0) {
          853                         for (; *y > 0; *y += direction) {
          854                                 if (!(term.line[*y-1][term.col-1].mode
          855                                                 & ATTR_WRAP)) {
          856                                         break;
          857                                 }
          858                         }
          859                 } else if (direction > 0) {
          860                         for (; *y < term.row-1; *y += direction) {
          861                                 if (!(term.line[*y][term.col-1].mode
          862                                                 & ATTR_WRAP)) {
          863                                         break;
          864                                 }
          865                         }
          866                 }
          867                 break;
          868         }
          869 }
          870 
          871 void
          872 getbuttoninfo(XEvent *e)
          873 {
          874         int type;
          875         uint state = e->xbutton.state & ~(Button1Mask | forceselmod);
          876 
          877         sel.alt = IS_SET(MODE_ALTSCREEN);
          878 
          879         sel.oe.x = x2col(e->xbutton.x);
          880         sel.oe.y = y2row(e->xbutton.y);
          881         selnormalize();
          882 
          883         sel.type = SEL_REGULAR;
          884         for (type = 1; type < LEN(selmasks); ++type) {
          885                 if (match(selmasks[type], state)) {
          886                         sel.type = type;
          887                         break;
          888                 }
          889         }
          890 }
          891 
          892 void
          893 mousereport(XEvent *e)
          894 {
          895         int x = x2col(e->xbutton.x), y = y2row(e->xbutton.y),
          896             button = e->xbutton.button, state = e->xbutton.state,
          897             len;
          898         char buf[40];
          899         static int ox, oy;
          900 
          901         /* from urxvt */
          902         if (e->xbutton.type == MotionNotify) {
          903                 if (x == ox && y == oy)
          904                         return;
          905                 if (!IS_SET(MODE_MOUSEMOTION) && !IS_SET(MODE_MOUSEMANY))
          906                         return;
          907                 /* MOUSE_MOTION: no reporting if no button is pressed */
          908                 if (IS_SET(MODE_MOUSEMOTION) && oldbutton == 3)
          909                         return;
          910 
          911                 button = oldbutton + 32;
          912                 ox = x;
          913                 oy = y;
          914         } else {
          915                 if (!IS_SET(MODE_MOUSESGR) && e->xbutton.type == ButtonRelease) {
          916                         button = 3;
          917                 } else {
          918                         button -= Button1;
          919                         if (button >= 3)
          920                                 button += 64 - 3;
          921                 }
          922                 if (e->xbutton.type == ButtonPress) {
          923                         oldbutton = button;
          924                         ox = x;
          925                         oy = y;
          926                 } else if (e->xbutton.type == ButtonRelease) {
          927                         oldbutton = 3;
          928                         /* MODE_MOUSEX10: no button release reporting */
          929                         if (IS_SET(MODE_MOUSEX10))
          930                                 return;
          931                         if (button == 64 || button == 65)
          932                                 return;
          933                 }
          934         }
          935 
          936         if (!IS_SET(MODE_MOUSEX10)) {
          937                 button += ((state & ShiftMask  ) ? 4  : 0)
          938                         + ((state & Mod4Mask   ) ? 8  : 0)
          939                         + ((state & ControlMask) ? 16 : 0);
          940         }
          941 
          942         if (IS_SET(MODE_MOUSESGR)) {
          943                 len = snprintf(buf, sizeof(buf), "\033[<%d;%d;%d%c",
          944                                 button, x+1, y+1,
          945                                 e->xbutton.type == ButtonRelease ? 'm' : 'M');
          946         } else if (x < 223 && y < 223) {
          947                 len = snprintf(buf, sizeof(buf), "\033[M%c%c%c",
          948                                 32+button, 32+x+1, 32+y+1);
          949         } else {
          950                 return;
          951         }
          952 
          953         ttywrite(buf, len);
          954 }
          955 
          956 void
          957 bpress(XEvent *e)
          958 {
          959         struct timespec now;
          960         MouseShortcut *ms;
          961 
          962         if (IS_SET(MODE_MOUSE) && !(e->xbutton.state & forceselmod)) {
          963                 mousereport(e);
          964                 return;
          965         }
          966 
          967         for (ms = mshortcuts; ms < mshortcuts + LEN(mshortcuts); ms++) {
          968                 if (e->xbutton.button == ms->b
          969                                 && match(ms->mask, e->xbutton.state)) {
          970                         ttysend(ms->s, strlen(ms->s));
          971                         return;
          972                 }
          973         }
          974 
          975         if (e->xbutton.button == Button1) {
          976                 clock_gettime(CLOCK_MONOTONIC, &now);
          977 
          978                 /* Clear previous selection, logically and visually. */
          979                 selclear(NULL);
          980                 sel.mode = SEL_EMPTY;
          981                 sel.type = SEL_REGULAR;
          982                 sel.oe.x = sel.ob.x = x2col(e->xbutton.x);
          983                 sel.oe.y = sel.ob.y = y2row(e->xbutton.y);
          984 
          985                 /*
          986                  * If the user clicks below predefined timeouts specific
          987                  * snapping behaviour is exposed.
          988                  */
          989                 if (TIMEDIFF(now, sel.tclick2) <= tripleclicktimeout) {
          990                         sel.snap = SNAP_LINE;
          991                 } else if (TIMEDIFF(now, sel.tclick1) <= doubleclicktimeout) {
          992                         sel.snap = SNAP_WORD;
          993                 } else {
          994                         sel.snap = 0;
          995                 }
          996                 selnormalize();
          997 
          998                 if (sel.snap != 0)
          999                         sel.mode = SEL_READY;
         1000                 tsetdirt(sel.nb.y, sel.ne.y);
         1001                 sel.tclick2 = sel.tclick1;
         1002                 sel.tclick1 = now;
         1003         }
         1004 }
         1005 
         1006 char *
         1007 getsel(void)
         1008 {
         1009         char *str, *ptr;
         1010         int y, bufsize, lastx, linelen;
         1011         Glyph *gp, *last;
         1012 
         1013         if (sel.ob.x == -1)
         1014                 return NULL;
         1015 
         1016         bufsize = (term.col+1) * (sel.ne.y-sel.nb.y+1) * UTF_SIZ;
         1017         ptr = str = xmalloc(bufsize);
         1018 
         1019         /* append every set & selected glyph to the selection */
         1020         for (y = sel.nb.y; y <= sel.ne.y; y++) {
         1021                 if ((linelen = tlinelen(y)) == 0) {
         1022                         *ptr++ = '\n';
         1023                         continue;
         1024                 }
         1025 
         1026                 if (sel.type == SEL_RECTANGULAR) {
         1027                         gp = &term.line[y][sel.nb.x];
         1028                         lastx = sel.ne.x;
         1029                 } else {
         1030                         gp = &term.line[y][sel.nb.y == y ? sel.nb.x : 0];
         1031                         lastx = (sel.ne.y == y) ? sel.ne.x : term.col-1;
         1032                 }
         1033                 last = &term.line[y][MIN(lastx, linelen-1)];
         1034                 while (last >= gp && last->u == ' ')
         1035                         --last;
         1036 
         1037                 for ( ; gp <= last; ++gp) {
         1038                         if (gp->mode & ATTR_WDUMMY)
         1039                                 continue;
         1040 
         1041                         ptr += utf8encode(gp->u, ptr);
         1042                 }
         1043 
         1044                 /*
         1045                  * Copy and pasting of line endings is inconsistent
         1046                  * in the inconsistent terminal and GUI world.
         1047                  * The best solution seems like to produce '\n' when
         1048                  * something is copied from st and convert '\n' to
         1049                  * '\r', when something to be pasted is received by
         1050                  * st.
         1051                  * FIXME: Fix the computer world.
         1052                  */
         1053                 if ((y < sel.ne.y || lastx >= linelen) && !(last->mode & ATTR_WRAP))
         1054                         *ptr++ = '\n';
         1055         }
         1056         *ptr = 0;
         1057         return str;
         1058 }
         1059 
         1060 void
         1061 selcopy(Time t)
         1062 {
         1063         xsetsel(getsel(), t);
         1064 }
         1065 
         1066 void
         1067 propnotify(XEvent *e)
         1068 {
         1069         XPropertyEvent *xpev;
         1070         Atom clipboard = XInternAtom(xw.dpy, "CLIPBOARD", 0);
         1071 
         1072         xpev = &e->xproperty;
         1073         if (xpev->state == PropertyNewValue &&
         1074                         (xpev->atom == XA_PRIMARY ||
         1075                          xpev->atom == clipboard)) {
         1076                 selnotify(e);
         1077         }
         1078 }
         1079 
         1080 void
         1081 selnotify(XEvent *e)
         1082 {
         1083         ulong nitems, ofs, rem;
         1084         int format;
         1085         uchar *data, *last, *repl;
         1086         Atom type, incratom, property;
         1087 
         1088         incratom = XInternAtom(xw.dpy, "INCR", 0);
         1089 
         1090         ofs = 0;
         1091         if (e->type == SelectionNotify) {
         1092                 property = e->xselection.property;
         1093         } else if(e->type == PropertyNotify) {
         1094                 property = e->xproperty.atom;
         1095         } else {
         1096                 return;
         1097         }
         1098         if (property == None)
         1099                 return;
         1100 
         1101         do {
         1102                 if (XGetWindowProperty(xw.dpy, xw.win, property, ofs,
         1103                                         BUFSIZ/4, False, AnyPropertyType,
         1104                                         &type, &format, &nitems, &rem,
         1105                                         &data)) {
         1106                         fprintf(stderr, "Clipboard allocation failed\n");
         1107                         return;
         1108                 }
         1109 
         1110                 if (e->type == PropertyNotify && nitems == 0 && rem == 0) {
         1111                         /*
         1112                          * If there is some PropertyNotify with no data, then
         1113                          * this is the signal of the selection owner that all
         1114                          * data has been transferred. We won't need to receive
         1115                          * PropertyNotify events anymore.
         1116                          */
         1117                         MODBIT(xw.attrs.event_mask, 0, PropertyChangeMask);
         1118                         XChangeWindowAttributes(xw.dpy, xw.win, CWEventMask,
         1119                                         &xw.attrs);
         1120                 }
         1121 
         1122                 if (type == incratom) {
         1123                         /*
         1124                          * Activate the PropertyNotify events so we receive
         1125                          * when the selection owner does send us the next
         1126                          * chunk of data.
         1127                          */
         1128                         MODBIT(xw.attrs.event_mask, 1, PropertyChangeMask);
         1129                         XChangeWindowAttributes(xw.dpy, xw.win, CWEventMask,
         1130                                         &xw.attrs);
         1131 
         1132                         /*
         1133                          * Deleting the property is the transfer start signal.
         1134                          */
         1135                         XDeleteProperty(xw.dpy, xw.win, (int)property);
         1136                         continue;
         1137                 }
         1138 
         1139                 /*
         1140                  * As seen in getsel:
         1141                  * Line endings are inconsistent in the terminal and GUI world
         1142                  * copy and pasting. When receiving some selection data,
         1143                  * replace all '\n' with '\r'.
         1144                  * FIXME: Fix the computer world.
         1145                  */
         1146                 repl = data;
         1147                 last = data + nitems * format / 8;
         1148                 while ((repl = memchr(repl, '\n', last - repl))) {
         1149                         *repl++ = '\r';
         1150                 }
         1151 
         1152                 if (IS_SET(MODE_BRCKTPASTE) && ofs == 0)
         1153                         ttywrite("\033[200~", 6);
         1154                 ttysend((char *)data, nitems * format / 8);
         1155                 if (IS_SET(MODE_BRCKTPASTE) && rem == 0)
         1156                         ttywrite("\033[201~", 6);
         1157                 XFree(data);
         1158                 /* number of 32-bit chunks returned */
         1159                 ofs += nitems * format / 32;
         1160         } while (rem > 0);
         1161 
         1162         /*
         1163          * Deleting the property again tells the selection owner to send the
         1164          * next data chunk in the property.
         1165          */
         1166         XDeleteProperty(xw.dpy, xw.win, (int)property);
         1167 }
         1168 
         1169 void
         1170 selpaste(const Arg *dummy)
         1171 {
         1172         XConvertSelection(xw.dpy, XA_PRIMARY, sel.xtarget, XA_PRIMARY,
         1173                         xw.win, CurrentTime);
         1174 }
         1175 
         1176 void
         1177 clipcopy(const Arg *dummy)
         1178 {
         1179         Atom clipboard;
         1180 
         1181         if (sel.clipboard != NULL)
         1182                 free(sel.clipboard);
         1183 
         1184         if (sel.primary != NULL) {
         1185                 sel.clipboard = xstrdup(sel.primary);
         1186                 clipboard = XInternAtom(xw.dpy, "CLIPBOARD", 0);
         1187                 XSetSelectionOwner(xw.dpy, clipboard, xw.win, CurrentTime);
         1188         }
         1189 }
         1190 
         1191 void
         1192 clippaste(const Arg *dummy)
         1193 {
         1194         Atom clipboard;
         1195 
         1196         clipboard = XInternAtom(xw.dpy, "CLIPBOARD", 0);
         1197         XConvertSelection(xw.dpy, clipboard, sel.xtarget, clipboard,
         1198                         xw.win, CurrentTime);
         1199 }
         1200 
         1201 void
         1202 selclear(XEvent *e)
         1203 {
         1204         if (sel.ob.x == -1)
         1205                 return;
         1206         sel.mode = SEL_IDLE;
         1207         sel.ob.x = -1;
         1208         tsetdirt(sel.nb.y, sel.ne.y);
         1209 }
         1210 
         1211 void
         1212 selrequest(XEvent *e)
         1213 {
         1214         XSelectionRequestEvent *xsre;
         1215         XSelectionEvent xev;
         1216         Atom xa_targets, string, clipboard;
         1217         char *seltext;
         1218 
         1219         xsre = (XSelectionRequestEvent *) e;
         1220         xev.type = SelectionNotify;
         1221         xev.requestor = xsre->requestor;
         1222         xev.selection = xsre->selection;
         1223         xev.target = xsre->target;
         1224         xev.time = xsre->time;
         1225         if (xsre->property == None)
         1226                 xsre->property = xsre->target;
         1227 
         1228         /* reject */
         1229         xev.property = None;
         1230 
         1231         xa_targets = XInternAtom(xw.dpy, "TARGETS", 0);
         1232         if (xsre->target == xa_targets) {
         1233                 /* respond with the supported type */
         1234                 string = sel.xtarget;
         1235                 XChangeProperty(xsre->display, xsre->requestor, xsre->property,
         1236                                 XA_ATOM, 32, PropModeReplace,
         1237                                 (uchar *) &string, 1);
         1238                 xev.property = xsre->property;
         1239         } else if (xsre->target == sel.xtarget || xsre->target == XA_STRING) {
         1240                 /*
         1241                  * xith XA_STRING non ascii characters may be incorrect in the
         1242                  * requestor. It is not our problem, use utf8.
         1243                  */
         1244                 clipboard = XInternAtom(xw.dpy, "CLIPBOARD", 0);
         1245                 if (xsre->selection == XA_PRIMARY) {
         1246                         seltext = sel.primary;
         1247                 } else if (xsre->selection == clipboard) {
         1248                         seltext = sel.clipboard;
         1249                 } else {
         1250                         fprintf(stderr,
         1251                                 "Unhandled clipboard selection 0x%lx\n",
         1252                                 xsre->selection);
         1253                         return;
         1254                 }
         1255                 if (seltext != NULL) {
         1256                         XChangeProperty(xsre->display, xsre->requestor,
         1257                                         xsre->property, xsre->target,
         1258                                         8, PropModeReplace,
         1259                                         (uchar *)seltext, strlen(seltext));
         1260                         xev.property = xsre->property;
         1261                 }
         1262         }
         1263 
         1264         /* all done, send a notification to the listener */
         1265         if (!XSendEvent(xsre->display, xsre->requestor, 1, 0, (XEvent *) &xev))
         1266                 fprintf(stderr, "Error sending SelectionNotify event\n");
         1267 }
         1268 
         1269 void
         1270 xsetsel(char *str, Time t)
         1271 {
         1272         free(sel.primary);
         1273         sel.primary = str;
         1274 
         1275         XSetSelectionOwner(xw.dpy, XA_PRIMARY, xw.win, t);
         1276         if (XGetSelectionOwner(xw.dpy, XA_PRIMARY) != xw.win)
         1277                 selclear(0);
         1278 }
         1279 
         1280 void
         1281 brelease(XEvent *e)
         1282 {
         1283         if (IS_SET(MODE_MOUSE) && !(e->xbutton.state & forceselmod)) {
         1284                 mousereport(e);
         1285                 return;
         1286         }
         1287 
         1288         if (e->xbutton.button == Button2) {
         1289                 selpaste(NULL);
         1290         } else if (e->xbutton.button == Button1) {
         1291                 if (sel.mode == SEL_READY) {
         1292                         getbuttoninfo(e);
         1293                         selcopy(e->xbutton.time);
         1294                 } else
         1295                         selclear(NULL);
         1296                 sel.mode = SEL_IDLE;
         1297                 tsetdirt(sel.nb.y, sel.ne.y);
         1298         }
         1299 }
         1300 
         1301 void
         1302 bmotion(XEvent *e)
         1303 {
         1304         int oldey, oldex, oldsby, oldsey;
         1305 
         1306         if (IS_SET(MODE_MOUSE) && !(e->xbutton.state & forceselmod)) {
         1307                 mousereport(e);
         1308                 return;
         1309         }
         1310 
         1311         if (!sel.mode)
         1312                 return;
         1313 
         1314         sel.mode = SEL_READY;
         1315         oldey = sel.oe.y;
         1316         oldex = sel.oe.x;
         1317         oldsby = sel.nb.y;
         1318         oldsey = sel.ne.y;
         1319         getbuttoninfo(e);
         1320 
         1321         if (oldey != sel.oe.y || oldex != sel.oe.x)
         1322                 tsetdirt(MIN(sel.nb.y, oldsby), MAX(sel.ne.y, oldsey));
         1323 }
         1324 
         1325 void
         1326 die(const char *errstr, ...)
         1327 {
         1328         va_list ap;
         1329 
         1330         va_start(ap, errstr);
         1331         vfprintf(stderr, errstr, ap);
         1332         va_end(ap);
         1333         exit(1);
         1334 }
         1335 
         1336 void
         1337 execsh(void)
         1338 {
         1339         char **args, *sh, *prog;
         1340         const struct passwd *pw;
         1341         char buf[sizeof(long) * 8 + 1];
         1342 
         1343         errno = 0;
         1344         if ((pw = getpwuid(getuid())) == NULL) {
         1345                 if (errno)
         1346                         die("getpwuid:%s\n", strerror(errno));
         1347                 else
         1348                         die("who are you?\n");
         1349         }
         1350 
         1351         if ((sh = getenv("SHELL")) == NULL)
         1352                 sh = (pw->pw_shell[0]) ? pw->pw_shell : shell;
         1353 
         1354         if (opt_cmd)
         1355                 prog = opt_cmd[0];
         1356         else if (utmp)
         1357                 prog = utmp;
         1358         else
         1359                 prog = sh;
         1360         args = (opt_cmd) ? opt_cmd : (char *[]) {prog, NULL};
         1361 
         1362         snprintf(buf, sizeof(buf), "%lu", xw.win);
         1363 
         1364         unsetenv("COLUMNS");
         1365         unsetenv("LINES");
         1366         unsetenv("TERMCAP");
         1367         setenv("LOGNAME", pw->pw_name, 1);
         1368         setenv("USER", pw->pw_name, 1);
         1369         setenv("SHELL", sh, 1);
         1370         setenv("HOME", pw->pw_dir, 1);
         1371         setenv("TERM", termname, 1);
         1372         setenv("WINDOWID", buf, 1);
         1373 
         1374         signal(SIGCHLD, SIG_DFL);
         1375         signal(SIGHUP, SIG_DFL);
         1376         signal(SIGINT, SIG_DFL);
         1377         signal(SIGQUIT, SIG_DFL);
         1378         signal(SIGTERM, SIG_DFL);
         1379         signal(SIGALRM, SIG_DFL);
         1380 
         1381         execvp(prog, args);
         1382         _exit(1);
         1383 }
         1384 
         1385 void
         1386 sigchld(int a)
         1387 {
         1388         int stat;
         1389         pid_t p;
         1390 
         1391         if ((p = waitpid(pid, &stat, WNOHANG)) < 0)
         1392                 die("Waiting for pid %hd failed: %s\n", pid, strerror(errno));
         1393 
         1394         if (pid != p)
         1395                 return;
         1396 
         1397         if (!WIFEXITED(stat) || WEXITSTATUS(stat))
         1398                 die("child finished with error '%d'\n", stat);
         1399         exit(0);
         1400 }
         1401 
         1402 
         1403 void
         1404 stty(void)
         1405 {
         1406         char cmd[_POSIX_ARG_MAX], **p, *q, *s;
         1407         size_t n, siz;
         1408 
         1409         if ((n = strlen(stty_args)) > sizeof(cmd)-1)
         1410                 die("incorrect stty parameters\n");
         1411         memcpy(cmd, stty_args, n);
         1412         q = cmd + n;
         1413         siz = sizeof(cmd) - n;
         1414         for (p = opt_cmd; p && (s = *p); ++p) {
         1415                 if ((n = strlen(s)) > siz-1)
         1416                         die("stty parameter length too long\n");
         1417                 *q++ = ' ';
         1418                 memcpy(q, s, n);
         1419                 q += n;
         1420                 siz -= n + 1;
         1421         }
         1422         *q = '\0';
         1423         if (system(cmd) != 0)
         1424             perror("Couldn't call stty");
         1425 }
         1426 
         1427 void
         1428 ttynew(void)
         1429 {
         1430         int m, s;
         1431         struct winsize w = {term.row, term.col, 0, 0};
         1432 
         1433         if (opt_io) {
         1434                 term.mode |= MODE_PRINT;
         1435                 iofd = (!strcmp(opt_io, "-")) ?
         1436                           1 : open(opt_io, O_WRONLY | O_CREAT, 0666);
         1437                 if (iofd < 0) {
         1438                         fprintf(stderr, "Error opening %s:%s\n",
         1439                                 opt_io, strerror(errno));
         1440                 }
         1441         }
         1442 
         1443         if (opt_line) {
         1444                 if ((cmdfd = open(opt_line, O_RDWR)) < 0)
         1445                         die("open line failed: %s\n", strerror(errno));
         1446                 dup2(cmdfd, 0);
         1447                 stty();
         1448                 return;
         1449         }
         1450 
         1451         /* seems to work fine on linux, openbsd and freebsd */
         1452         if (openpty(&m, &s, NULL, NULL, &w) < 0)
         1453                 die("openpty failed: %s\n", strerror(errno));
         1454 
         1455         switch (pid = fork()) {
         1456         case -1:
         1457                 die("fork failed\n");
         1458                 break;
         1459         case 0:
         1460                 close(iofd);
         1461                 setsid(); /* create a new process group */
         1462                 dup2(s, 0);
         1463                 dup2(s, 1);
         1464                 dup2(s, 2);
         1465                 if (ioctl(s, TIOCSCTTY, NULL) < 0)
         1466                         die("ioctl TIOCSCTTY failed: %s\n", strerror(errno));
         1467                 close(s);
         1468                 close(m);
         1469                 execsh();
         1470                 break;
         1471         default:
         1472                 close(s);
         1473                 cmdfd = m;
         1474                 signal(SIGCHLD, sigchld);
         1475                 break;
         1476         }
         1477 }
         1478 
         1479 size_t
         1480 ttyread(void)
         1481 {
         1482         static char buf[BUFSIZ];
         1483         static int buflen = 0;
         1484         char *ptr;
         1485         int charsize; /* size of utf8 char in bytes */
         1486         Rune unicodep;
         1487         int ret;
         1488 
         1489         /* append read bytes to unprocessed bytes */
         1490         if ((ret = read(cmdfd, buf+buflen, LEN(buf)-buflen)) < 0)
         1491                 die("Couldn't read from shell: %s\n", strerror(errno));
         1492 
         1493         buflen += ret;
         1494         ptr = buf;
         1495 
         1496         for (;;) {
         1497                 if (IS_SET(MODE_UTF8) && !IS_SET(MODE_SIXEL)) {
         1498                         /* process a complete utf8 char */
         1499                         charsize = utf8decode(ptr, &unicodep, buflen);
         1500                         if (charsize == 0)
         1501                                 break;
         1502                         tputc(unicodep);
         1503                         ptr += charsize;
         1504                         buflen -= charsize;
         1505 
         1506                 } else {
         1507                         if (buflen <= 0)
         1508                                 break;
         1509                         tputc(*ptr++ & 0xFF);
         1510                         buflen--;
         1511                 }
         1512         }
         1513         /* keep any uncomplete utf8 char for the next call */
         1514         if (buflen > 0)
         1515                 memmove(buf, ptr, buflen);
         1516 
         1517         return ret;
         1518 }
         1519 
         1520 void
         1521 ttywrite(const char *s, size_t n)
         1522 {
         1523         fd_set wfd, rfd;
         1524         ssize_t r;
         1525         size_t lim = 256;
         1526 
         1527         /*
         1528          * Remember that we are using a pty, which might be a modem line.
         1529          * Writing too much will clog the line. That's why we are doing this
         1530          * dance.
         1531          * FIXME: Migrate the world to Plan 9.
         1532          */
         1533         while (n > 0) {
         1534                 FD_ZERO(&wfd);
         1535                 FD_ZERO(&rfd);
         1536                 FD_SET(cmdfd, &wfd);
         1537                 FD_SET(cmdfd, &rfd);
         1538 
         1539                 /* Check if we can write. */
         1540                 if (pselect(cmdfd+1, &rfd, &wfd, NULL, NULL, NULL) < 0) {
         1541                         if (errno == EINTR)
         1542                                 continue;
         1543                         die("select failed: %s\n", strerror(errno));
         1544                 }
         1545                 if (FD_ISSET(cmdfd, &wfd)) {
         1546                         /*
         1547                          * Only write the bytes written by ttywrite() or the
         1548                          * default of 256. This seems to be a reasonable value
         1549                          * for a serial line. Bigger values might clog the I/O.
         1550                          */
         1551                         if ((r = write(cmdfd, s, (n < lim)? n : lim)) < 0)
         1552                                 goto write_error;
         1553                         if (r < n) {
         1554                                 /*
         1555                                  * We weren't able to write out everything.
         1556                                  * This means the buffer is getting full
         1557                                  * again. Empty it.
         1558                                  */
         1559                                 if (n < lim)
         1560                                         lim = ttyread();
         1561                                 n -= r;
         1562                                 s += r;
         1563                         } else {
         1564                                 /* All bytes have been written. */
         1565                                 break;
         1566                         }
         1567                 }
         1568                 if (FD_ISSET(cmdfd, &rfd))
         1569                         lim = ttyread();
         1570         }
         1571         return;
         1572 
         1573 write_error:
         1574         die("write error on tty: %s\n", strerror(errno));
         1575 }
         1576 
         1577 void
         1578 ttysend(char *s, size_t n)
         1579 {
         1580         int len;
         1581         char *t, *lim;
         1582         Rune u;
         1583 
         1584         ttywrite(s, n);
         1585         if (!IS_SET(MODE_ECHO))
         1586                 return;
         1587 
         1588         lim = &s[n];
         1589         for (t = s; t < lim; t += len) {
         1590                 if (IS_SET(MODE_UTF8) && !IS_SET(MODE_SIXEL)) {
         1591                         len = utf8decode(t, &u, n);
         1592                 } else {
         1593                         u = *t & 0xFF;
         1594                         len = 1;
         1595                 }
         1596                 if (len <= 0)
         1597                         break;
         1598                 techo(u);
         1599                 n -= len;
         1600         }
         1601 }
         1602 
         1603 void
         1604 ttyresize(void)
         1605 {
         1606         struct winsize w;
         1607 
         1608         w.ws_row = term.row;
         1609         w.ws_col = term.col;
         1610         w.ws_xpixel = xw.tw;
         1611         w.ws_ypixel = xw.th;
         1612         if (ioctl(cmdfd, TIOCSWINSZ, &w) < 0)
         1613                 fprintf(stderr, "Couldn't set window size: %s\n", strerror(errno));
         1614 }
         1615 
         1616 int
         1617 tattrset(int attr)
         1618 {
         1619         int i, j;
         1620 
         1621         for (i = 0; i < term.row-1; i++) {
         1622                 for (j = 0; j < term.col-1; j++) {
         1623                         if (term.line[i][j].mode & attr)
         1624                                 return 1;
         1625                 }
         1626         }
         1627 
         1628         return 0;
         1629 }
         1630 
         1631 void
         1632 tsetdirt(int top, int bot)
         1633 {
         1634         int i;
         1635 
         1636         LIMIT(top, 0, term.row-1);
         1637         LIMIT(bot, 0, term.row-1);
         1638 
         1639         for (i = top; i <= bot; i++)
         1640                 term.dirty[i] = 1;
         1641 }
         1642 
         1643 void
         1644 tsetdirtattr(int attr)
         1645 {
         1646         int i, j;
         1647 
         1648         for (i = 0; i < term.row-1; i++) {
         1649                 for (j = 0; j < term.col-1; j++) {
         1650                         if (term.line[i][j].mode & attr) {
         1651                                 tsetdirt(i, i);
         1652                                 break;
         1653                         }
         1654                 }
         1655         }
         1656 }
         1657 
         1658 void
         1659 tfulldirt(void)
         1660 {
         1661         tsetdirt(0, term.row-1);
         1662 }
         1663 
         1664 void
         1665 tcursor(int mode)
         1666 {
         1667         static TCursor c[2];
         1668         int alt = IS_SET(MODE_ALTSCREEN);
         1669 
         1670         if (mode == CURSOR_SAVE) {
         1671                 c[alt] = term.c;
         1672         } else if (mode == CURSOR_LOAD) {
         1673                 term.c = c[alt];
         1674                 tmoveto(c[alt].x, c[alt].y);
         1675         }
         1676 }
         1677 
         1678 void
         1679 treset(void)
         1680 {
         1681         uint i;
         1682 
         1683         term.c = (TCursor){{
         1684                 .mode = ATTR_NULL,
         1685                 .fg = defaultfg,
         1686                 .bg = defaultbg
         1687         }, .x = 0, .y = 0, .state = CURSOR_DEFAULT};
         1688 
         1689         memset(term.tabs, 0, term.col * sizeof(*term.tabs));
         1690         for (i = tabspaces; i < term.col; i += tabspaces)
         1691                 term.tabs[i] = 1;
         1692         term.top = 0;
         1693         term.bot = term.row - 1;
         1694         term.mode = MODE_WRAP|MODE_UTF8;
         1695         memset(term.trantbl, CS_USA, sizeof(term.trantbl));
         1696         term.charset = 0;
         1697 
         1698         for (i = 0; i < 2; i++) {
         1699                 tmoveto(0, 0);
         1700                 tcursor(CURSOR_SAVE);
         1701                 tclearregion(0, 0, term.col-1, term.row-1);
         1702                 tswapscreen();
         1703         }
         1704 }
         1705 
         1706 void
         1707 tnew(int col, int row)
         1708 {
         1709         term = (Term){ .c = { .attr = { .fg = defaultfg, .bg = defaultbg } } };
         1710         tresize(col, row);
         1711         term.numlock = 1;
         1712 
         1713         treset();
         1714 }
         1715 
         1716 void
         1717 tswapscreen(void)
         1718 {
         1719         Line *tmp = term.line;
         1720 
         1721         term.line = term.alt;
         1722         term.alt = tmp;
         1723         term.mode ^= MODE_ALTSCREEN;
         1724         tfulldirt();
         1725 }
         1726 
         1727 void
         1728 tscrolldown(int orig, int n)
         1729 {
         1730         int i;
         1731         Line temp;
         1732 
         1733         LIMIT(n, 0, term.bot-orig+1);
         1734 
         1735         tsetdirt(orig, term.bot-n);
         1736         tclearregion(0, term.bot-n+1, term.col-1, term.bot);
         1737 
         1738         for (i = term.bot; i >= orig+n; i--) {
         1739                 temp = term.line[i];
         1740                 term.line[i] = term.line[i-n];
         1741                 term.line[i-n] = temp;
         1742         }
         1743 
         1744         selscroll(orig, n);
         1745 }
         1746 
         1747 void
         1748 tscrollup(int orig, int n)
         1749 {
         1750         int i;
         1751         Line temp;
         1752 
         1753         LIMIT(n, 0, term.bot-orig+1);
         1754 
         1755         tclearregion(0, orig, term.col-1, orig+n-1);
         1756         tsetdirt(orig+n, term.bot);
         1757 
         1758         for (i = orig; i <= term.bot-n; i++) {
         1759                 temp = term.line[i];
         1760                 term.line[i] = term.line[i+n];
         1761                 term.line[i+n] = temp;
         1762         }
         1763 
         1764         selscroll(orig, -n);
         1765 }
         1766 
         1767 void
         1768 selscroll(int orig, int n)
         1769 {
         1770         if (sel.ob.x == -1)
         1771                 return;
         1772 
         1773         if (BETWEEN(sel.ob.y, orig, term.bot) || BETWEEN(sel.oe.y, orig, term.bot)) {
         1774                 if ((sel.ob.y += n) > term.bot || (sel.oe.y += n) < term.top) {
         1775                         selclear(NULL);
         1776                         return;
         1777                 }
         1778                 if (sel.type == SEL_RECTANGULAR) {
         1779                         if (sel.ob.y < term.top)
         1780                                 sel.ob.y = term.top;
         1781                         if (sel.oe.y > term.bot)
         1782                                 sel.oe.y = term.bot;
         1783                 } else {
         1784                         if (sel.ob.y < term.top) {
         1785                                 sel.ob.y = term.top;
         1786                                 sel.ob.x = 0;
         1787                         }
         1788                         if (sel.oe.y > term.bot) {
         1789                                 sel.oe.y = term.bot;
         1790                                 sel.oe.x = term.col;
         1791                         }
         1792                 }
         1793                 selnormalize();
         1794         }
         1795 }
         1796 
         1797 void
         1798 tnewline(int first_col)
         1799 {
         1800         int y = term.c.y;
         1801 
         1802         if (y == term.bot) {
         1803                 tscrollup(term.top, 1);
         1804         } else {
         1805                 y++;
         1806         }
         1807         tmoveto(first_col ? 0 : term.c.x, y);
         1808 }
         1809 
         1810 void
         1811 csiparse(void)
         1812 {
         1813         char *p = csiescseq.buf, *np;
         1814         long int v;
         1815 
         1816         csiescseq.narg = 0;
         1817         if (*p == '?') {
         1818                 csiescseq.priv = 1;
         1819                 p++;
         1820         }
         1821 
         1822         csiescseq.buf[csiescseq.len] = '\0';
         1823         while (p < csiescseq.buf+csiescseq.len) {
         1824                 np = NULL;
         1825                 v = strtol(p, &np, 10);
         1826                 if (np == p)
         1827                         v = 0;
         1828                 if (v == LONG_MAX || v == LONG_MIN)
         1829                         v = -1;
         1830                 csiescseq.arg[csiescseq.narg++] = v;
         1831                 p = np;
         1832                 if (*p != ';' || csiescseq.narg == ESC_ARG_SIZ)
         1833                         break;
         1834                 p++;
         1835         }
         1836         csiescseq.mode[0] = *p++;
         1837         csiescseq.mode[1] = (p < csiescseq.buf+csiescseq.len) ? *p : '\0';
         1838 }
         1839 
         1840 /* for absolute user moves, when decom is set */
         1841 void
         1842 tmoveato(int x, int y)
         1843 {
         1844         tmoveto(x, y + ((term.c.state & CURSOR_ORIGIN) ? term.top: 0));
         1845 }
         1846 
         1847 void
         1848 tmoveto(int x, int y)
         1849 {
         1850         int miny, maxy;
         1851 
         1852         if (term.c.state & CURSOR_ORIGIN) {
         1853                 miny = term.top;
         1854                 maxy = term.bot;
         1855         } else {
         1856                 miny = 0;
         1857                 maxy = term.row - 1;
         1858         }
         1859         term.c.state &= ~CURSOR_WRAPNEXT;
         1860         term.c.x = LIMIT(x, 0, term.col-1);
         1861         term.c.y = LIMIT(y, miny, maxy);
         1862 }
         1863 
         1864 void
         1865 tsetchar(Rune u, Glyph *attr, int x, int y)
         1866 {
         1867         static char *vt100_0[62] = { /* 0x41 - 0x7e */
         1868                 "↑", "↓", "→", "←", "█", "▚", "☃", /* A - G */
         1869                 0, 0, 0, 0, 0, 0, 0, 0, /* H - O */
         1870                 0, 0, 0, 0, 0, 0, 0, 0, /* P - W */
         1871                 0, 0, 0, 0, 0, 0, 0, " ", /* X - _ */
         1872                 "◆", "▒", "␉", "␌", "␍", "␊", "°", "±", /* ` - g */
         1873                 "␤", "␋", "┘", "┐", "┌", "└", "┼", "⎺", /* h - o */
         1874                 "⎻", "─", "⎼", "⎽", "├", "┤", "┴", "┬", /* p - w */
         1875                 "│", "≤", "≥", "π", "≠", "£", "·", /* x - ~ */
         1876         };
         1877 
         1878         /*
         1879          * The table is proudly stolen from rxvt.
         1880          */
         1881         if (term.trantbl[term.charset] == CS_GRAPHIC0 &&
         1882            BETWEEN(u, 0x41, 0x7e) && vt100_0[u - 0x41])
         1883                 utf8decode(vt100_0[u - 0x41], &u, UTF_SIZ);
         1884 
         1885         if (term.line[y][x].mode & ATTR_WIDE) {
         1886                 if (x+1 < term.col) {
         1887                         term.line[y][x+1].u = ' ';
         1888                         term.line[y][x+1].mode &= ~ATTR_WDUMMY;
         1889                 }
         1890         } else if (term.line[y][x].mode & ATTR_WDUMMY) {
         1891                 term.line[y][x-1].u = ' ';
         1892                 term.line[y][x-1].mode &= ~ATTR_WIDE;
         1893         }
         1894 
         1895         term.dirty[y] = 1;
         1896         term.line[y][x] = *attr;
         1897         term.line[y][x].u = u;
         1898 }
         1899 
         1900 void
         1901 tclearregion(int x1, int y1, int x2, int y2)
         1902 {
         1903         int x, y, temp;
         1904         Glyph *gp;
         1905 
         1906         if (x1 > x2)
         1907                 temp = x1, x1 = x2, x2 = temp;
         1908         if (y1 > y2)
         1909                 temp = y1, y1 = y2, y2 = temp;
         1910 
         1911         LIMIT(x1, 0, term.col-1);
         1912         LIMIT(x2, 0, term.col-1);
         1913         LIMIT(y1, 0, term.row-1);
         1914         LIMIT(y2, 0, term.row-1);
         1915 
         1916         for (y = y1; y <= y2; y++) {
         1917                 term.dirty[y] = 1;
         1918                 for (x = x1; x <= x2; x++) {
         1919                         gp = &term.line[y][x];
         1920                         if (selected(x, y))
         1921                                 selclear(NULL);
         1922                         gp->fg = term.c.attr.fg;
         1923                         gp->bg = term.c.attr.bg;
         1924                         gp->mode = 0;
         1925                         gp->u = ' ';
         1926                 }
         1927         }
         1928 }
         1929 
         1930 void
         1931 tdeletechar(int n)
         1932 {
         1933         int dst, src, size;
         1934         Glyph *line;
         1935 
         1936         LIMIT(n, 0, term.col - term.c.x);
         1937 
         1938         dst = term.c.x;
         1939         src = term.c.x + n;
         1940         size = term.col - src;
         1941         line = term.line[term.c.y];
         1942 
         1943         memmove(&line[dst], &line[src], size * sizeof(Glyph));
         1944         tclearregion(term.col-n, term.c.y, term.col-1, term.c.y);
         1945 }
         1946 
         1947 void
         1948 tinsertblank(int n)
         1949 {
         1950         int dst, src, size;
         1951         Glyph *line;
         1952 
         1953         LIMIT(n, 0, term.col - term.c.x);
         1954 
         1955         dst = term.c.x + n;
         1956         src = term.c.x;
         1957         size = term.col - dst;
         1958         line = term.line[term.c.y];
         1959 
         1960         memmove(&line[dst], &line[src], size * sizeof(Glyph));
         1961         tclearregion(src, term.c.y, dst - 1, term.c.y);
         1962 }
         1963 
         1964 void
         1965 tinsertblankline(int n)
         1966 {
         1967         if (BETWEEN(term.c.y, term.top, term.bot))
         1968                 tscrolldown(term.c.y, n);
         1969 }
         1970 
         1971 void
         1972 tdeleteline(int n)
         1973 {
         1974         if (BETWEEN(term.c.y, term.top, term.bot))
         1975                 tscrollup(term.c.y, n);
         1976 }
         1977 
         1978 int32_t
         1979 tdefcolor(int *attr, int *npar, int l)
         1980 {
         1981         int32_t idx = -1;
         1982         uint r, g, b;
         1983 
         1984         switch (attr[*npar + 1]) {
         1985         case 2: /* direct color in RGB space */
         1986                 if (*npar + 4 >= l) {
         1987                         fprintf(stderr,
         1988                                 "erresc(38): Incorrect number of parameters (%d)\n",
         1989                                 *npar);
         1990                         break;
         1991                 }
         1992                 r = attr[*npar + 2];
         1993                 g = attr[*npar + 3];
         1994                 b = attr[*npar + 4];
         1995                 *npar += 4;
         1996                 if (!BETWEEN(r, 0, 255) || !BETWEEN(g, 0, 255) || !BETWEEN(b, 0, 255))
         1997                         fprintf(stderr, "erresc: bad rgb color (%u,%u,%u)\n",
         1998                                 r, g, b);
         1999                 else
         2000                         idx = TRUECOLOR(r, g, b);
         2001                 break;
         2002         case 5: /* indexed color */
         2003                 if (*npar + 2 >= l) {
         2004                         fprintf(stderr,
         2005                                 "erresc(38): Incorrect number of parameters (%d)\n",
         2006                                 *npar);
         2007                         break;
         2008                 }
         2009                 *npar += 2;
         2010                 if (!BETWEEN(attr[*npar], 0, 255))
         2011                         fprintf(stderr, "erresc: bad fgcolor %d\n", attr[*npar]);
         2012                 else
         2013                         idx = attr[*npar];
         2014                 break;
         2015         case 0: /* implemented defined (only foreground) */
         2016         case 1: /* transparent */
         2017         case 3: /* direct color in CMY space */
         2018         case 4: /* direct color in CMYK space */
         2019         default:
         2020                 fprintf(stderr,
         2021                         "erresc(38): gfx attr %d unknown\n", attr[*npar]);
         2022                 break;
         2023         }
         2024 
         2025         return idx;
         2026 }
         2027 
         2028 void
         2029 tsetattr(int *attr, int l)
         2030 {
         2031         int i;
         2032         int32_t idx;
         2033 
         2034         for (i = 0; i < l; i++) {
         2035                 switch (attr[i]) {
         2036                 case 0:
         2037                         term.c.attr.mode &= ~(
         2038                                 ATTR_BOLD       |
         2039                                 ATTR_FAINT      |
         2040                                 ATTR_ITALIC     |
         2041                                 ATTR_UNDERLINE  |
         2042                                 ATTR_BLINK      |
         2043                                 ATTR_REVERSE    |
         2044                                 ATTR_INVISIBLE  |
         2045                                 ATTR_STRUCK     );
         2046                         term.c.attr.fg = defaultfg;
         2047                         term.c.attr.bg = defaultbg;
         2048                         break;
         2049                 case 1:
         2050                         term.c.attr.mode |= ATTR_BOLD;
         2051                         break;
         2052                 case 2:
         2053                         term.c.attr.mode |= ATTR_FAINT;
         2054                         break;
         2055                 case 3:
         2056                         term.c.attr.mode |= ATTR_ITALIC;
         2057                         break;
         2058                 case 4:
         2059                         term.c.attr.mode |= ATTR_UNDERLINE;
         2060                         break;
         2061                 case 5: /* slow blink */
         2062                         /* FALLTHROUGH */
         2063                 case 6: /* rapid blink */
         2064                         term.c.attr.mode |= ATTR_BLINK;
         2065                         break;
         2066                 case 7:
         2067                         term.c.attr.mode |= ATTR_REVERSE;
         2068                         break;
         2069                 case 8:
         2070                         term.c.attr.mode |= ATTR_INVISIBLE;
         2071                         break;
         2072                 case 9:
         2073                         term.c.attr.mode |= ATTR_STRUCK;
         2074                         break;
         2075                 case 22:
         2076                         term.c.attr.mode &= ~(ATTR_BOLD | ATTR_FAINT);
         2077                         break;
         2078                 case 23:
         2079                         term.c.attr.mode &= ~ATTR_ITALIC;
         2080                         break;
         2081                 case 24:
         2082                         term.c.attr.mode &= ~ATTR_UNDERLINE;
         2083                         break;
         2084                 case 25:
         2085                         term.c.attr.mode &= ~ATTR_BLINK;
         2086                         break;
         2087                 case 27:
         2088                         term.c.attr.mode &= ~ATTR_REVERSE;
         2089                         break;
         2090                 case 28:
         2091                         term.c.attr.mode &= ~ATTR_INVISIBLE;
         2092                         break;
         2093                 case 29:
         2094                         term.c.attr.mode &= ~ATTR_STRUCK;
         2095                         break;
         2096                 case 38:
         2097                         if ((idx = tdefcolor(attr, &i, l)) >= 0)
         2098                                 term.c.attr.fg = idx;
         2099                         break;
         2100                 case 39:
         2101                         term.c.attr.fg = defaultfg;
         2102                         break;
         2103                 case 48:
         2104                         if ((idx = tdefcolor(attr, &i, l)) >= 0)
         2105                                 term.c.attr.bg = idx;
         2106                         break;
         2107                 case 49:
         2108                         term.c.attr.bg = defaultbg;
         2109                         break;
         2110                 default:
         2111                         if (BETWEEN(attr[i], 30, 37)) {
         2112                                 term.c.attr.fg = attr[i] - 30;
         2113                         } else if (BETWEEN(attr[i], 40, 47)) {
         2114                                 term.c.attr.bg = attr[i] - 40;
         2115                         } else if (BETWEEN(attr[i], 90, 97)) {
         2116                                 term.c.attr.fg = attr[i] - 90 + 8;
         2117                         } else if (BETWEEN(attr[i], 100, 107)) {
         2118                                 term.c.attr.bg = attr[i] - 100 + 8;
         2119                         } else {
         2120                                 fprintf(stderr,
         2121                                         "erresc(default): gfx attr %d unknown\n",
         2122                                         attr[i]), csidump();
         2123                         }
         2124                         break;
         2125                 }
         2126         }
         2127 }
         2128 
         2129 void
         2130 tsetscroll(int t, int b)
         2131 {
         2132         int temp;
         2133 
         2134         LIMIT(t, 0, term.row-1);
         2135         LIMIT(b, 0, term.row-1);
         2136         if (t > b) {
         2137                 temp = t;
         2138                 t = b;
         2139                 b = temp;
         2140         }
         2141         term.top = t;
         2142         term.bot = b;
         2143 }
         2144 
         2145 void
         2146 tsetmode(int priv, int set, int *args, int narg)
         2147 {
         2148         int *lim, mode;
         2149         int alt;
         2150 
         2151         for (lim = args + narg; args < lim; ++args) {
         2152                 if (priv) {
         2153                         switch (*args) {
         2154                         case 1: /* DECCKM -- Cursor key */
         2155                                 MODBIT(term.mode, set, MODE_APPCURSOR);
         2156                                 break;
         2157                         case 5: /* DECSCNM -- Reverse video */
         2158                                 mode = term.mode;
         2159                                 MODBIT(term.mode, set, MODE_REVERSE);
         2160                                 if (mode != term.mode)
         2161                                         redraw();
         2162                                 break;
         2163                         case 6: /* DECOM -- Origin */
         2164                                 MODBIT(term.c.state, set, CURSOR_ORIGIN);
         2165                                 tmoveato(0, 0);
         2166                                 break;
         2167                         case 7: /* DECAWM -- Auto wrap */
         2168                                 MODBIT(term.mode, set, MODE_WRAP);
         2169                                 break;
         2170                         case 0:  /* Error (IGNORED) */
         2171                         case 2:  /* DECANM -- ANSI/VT52 (IGNORED) */
         2172                         case 3:  /* DECCOLM -- Column  (IGNORED) */
         2173                         case 4:  /* DECSCLM -- Scroll (IGNORED) */
         2174                         case 8:  /* DECARM -- Auto repeat (IGNORED) */
         2175                         case 18: /* DECPFF -- Printer feed (IGNORED) */
         2176                         case 19: /* DECPEX -- Printer extent (IGNORED) */
         2177                         case 42: /* DECNRCM -- National characters (IGNORED) */
         2178                         case 12: /* att610 -- Start blinking cursor (IGNORED) */
         2179                                 break;
         2180                         case 25: /* DECTCEM -- Text Cursor Enable Mode */
         2181                                 MODBIT(term.mode, !set, MODE_HIDE);
         2182                                 break;
         2183                         case 9:    /* X10 mouse compatibility mode */
         2184                                 xsetpointermotion(0);
         2185                                 MODBIT(term.mode, 0, MODE_MOUSE);
         2186                                 MODBIT(term.mode, set, MODE_MOUSEX10);
         2187                                 break;
         2188                         case 1000: /* 1000: report button press */
         2189                                 xsetpointermotion(0);
         2190                                 MODBIT(term.mode, 0, MODE_MOUSE);
         2191                                 MODBIT(term.mode, set, MODE_MOUSEBTN);
         2192                                 break;
         2193                         case 1002: /* 1002: report motion on button press */
         2194                                 xsetpointermotion(0);
         2195                                 MODBIT(term.mode, 0, MODE_MOUSE);
         2196                                 MODBIT(term.mode, set, MODE_MOUSEMOTION);
         2197                                 break;
         2198                         case 1003: /* 1003: enable all mouse motions */
         2199                                 xsetpointermotion(set);
         2200                                 MODBIT(term.mode, 0, MODE_MOUSE);
         2201                                 MODBIT(term.mode, set, MODE_MOUSEMANY);
         2202                                 break;
         2203                         case 1004: /* 1004: send focus events to tty */
         2204                                 MODBIT(term.mode, set, MODE_FOCUS);
         2205                                 break;
         2206                         case 1006: /* 1006: extended reporting mode */
         2207                                 MODBIT(term.mode, set, MODE_MOUSESGR);
         2208                                 break;
         2209                         case 1034:
         2210                                 MODBIT(term.mode, set, MODE_8BIT);
         2211                                 break;
         2212                         case 1049: /* swap screen & set/restore cursor as xterm */
         2213                                 if (!allowaltscreen)
         2214                                         break;
         2215                                 tcursor((set) ? CURSOR_SAVE : CURSOR_LOAD);
         2216                                 /* FALLTHROUGH */
         2217                         case 47: /* swap screen */
         2218                         case 1047:
         2219                                 if (!allowaltscreen)
         2220                                         break;
         2221                                 alt = IS_SET(MODE_ALTSCREEN);
         2222                                 if (alt) {
         2223                                         tclearregion(0, 0, term.col-1,
         2224                                                         term.row-1);
         2225                                 }
         2226                                 if (set ^ alt) /* set is always 1 or 0 */
         2227                                         tswapscreen();
         2228                                 if (*args != 1049)
         2229                                         break;
         2230                                 /* FALLTHROUGH */
         2231                         case 1048:
         2232                                 tcursor((set) ? CURSOR_SAVE : CURSOR_LOAD);
         2233                                 break;
         2234                         case 2004: /* 2004: bracketed paste mode */
         2235                                 MODBIT(term.mode, set, MODE_BRCKTPASTE);
         2236                                 break;
         2237                         /* Not implemented mouse modes. See comments there. */
         2238                         case 1001: /* mouse highlight mode; can hang the
         2239                                       terminal by design when implemented. */
         2240                         case 1005: /* UTF-8 mouse mode; will confuse
         2241                                       applications not supporting UTF-8
         2242                                       and luit. */
         2243                         case 1015: /* urxvt mangled mouse mode; incompatible
         2244                                       and can be mistaken for other control
         2245                                       codes. */
         2246                         default:
         2247                                 fprintf(stderr,
         2248                                         "erresc: unknown private set/reset mode %d\n",
         2249                                         *args);
         2250                                 break;
         2251                         }
         2252                 } else {
         2253                         switch (*args) {
         2254                         case 0:  /* Error (IGNORED) */
         2255                                 break;
         2256                         case 2:  /* KAM -- keyboard action */
         2257                                 MODBIT(term.mode, set, MODE_KBDLOCK);
         2258                                 break;
         2259                         case 4:  /* IRM -- Insertion-replacement */
         2260                                 MODBIT(term.mode, set, MODE_INSERT);
         2261                                 break;
         2262                         case 12: /* SRM -- Send/Receive */
         2263                                 MODBIT(term.mode, !set, MODE_ECHO);
         2264                                 break;
         2265                         case 20: /* LNM -- Linefeed/new line */
         2266                                 MODBIT(term.mode, set, MODE_CRLF);
         2267                                 break;
         2268                         default:
         2269                                 fprintf(stderr,
         2270                                         "erresc: unknown set/reset mode %d\n",
         2271                                         *args);
         2272                                 break;
         2273                         }
         2274                 }
         2275         }
         2276 }
         2277 
         2278 void
         2279 csihandle(void)
         2280 {
         2281         char buf[40];
         2282         int len;
         2283 
         2284         switch (csiescseq.mode[0]) {
         2285         default:
         2286         unknown:
         2287                 fprintf(stderr, "erresc: unknown csi ");
         2288                 csidump();
         2289                 /* die(""); */
         2290                 break;
         2291         case '@': /* ICH -- Insert <n> blank char */
         2292                 DEFAULT(csiescseq.arg[0], 1);
         2293                 tinsertblank(csiescseq.arg[0]);
         2294                 break;
         2295         case 'A': /* CUU -- Cursor <n> Up */
         2296                 DEFAULT(csiescseq.arg[0], 1);
         2297                 tmoveto(term.c.x, term.c.y-csiescseq.arg[0]);
         2298                 break;
         2299         case 'B': /* CUD -- Cursor <n> Down */
         2300         case 'e': /* VPR --Cursor <n> Down */
         2301                 DEFAULT(csiescseq.arg[0], 1);
         2302                 tmoveto(term.c.x, term.c.y+csiescseq.arg[0]);
         2303                 break;
         2304         case 'i': /* MC -- Media Copy */
         2305                 switch (csiescseq.arg[0]) {
         2306                 case 0:
         2307                         tdump();
         2308                         break;
         2309                 case 1:
         2310                         tdumpline(term.c.y);
         2311                         break;
         2312                 case 2:
         2313                         tdumpsel();
         2314                         break;
         2315                 case 4:
         2316                         term.mode &= ~MODE_PRINT;
         2317                         break;
         2318                 case 5:
         2319                         term.mode |= MODE_PRINT;
         2320                         break;
         2321                 }
         2322                 break;
         2323         case 'c': /* DA -- Device Attributes */
         2324                 if (csiescseq.arg[0] == 0)
         2325                         ttywrite(vtiden, sizeof(vtiden) - 1);
         2326                 break;
         2327         case 'C': /* CUF -- Cursor <n> Forward */
         2328         case 'a': /* HPR -- Cursor <n> Forward */
         2329                 DEFAULT(csiescseq.arg[0], 1);
         2330                 tmoveto(term.c.x+csiescseq.arg[0], term.c.y);
         2331                 break;
         2332         case 'D': /* CUB -- Cursor <n> Backward */
         2333                 DEFAULT(csiescseq.arg[0], 1);
         2334                 tmoveto(term.c.x-csiescseq.arg[0], term.c.y);
         2335                 break;
         2336         case 'E': /* CNL -- Cursor <n> Down and first col */
         2337                 DEFAULT(csiescseq.arg[0], 1);
         2338                 tmoveto(0, term.c.y+csiescseq.arg[0]);
         2339                 break;
         2340         case 'F': /* CPL -- Cursor <n> Up and first col */
         2341                 DEFAULT(csiescseq.arg[0], 1);
         2342                 tmoveto(0, term.c.y-csiescseq.arg[0]);
         2343                 break;
         2344         case 'g': /* TBC -- Tabulation clear */
         2345                 switch (csiescseq.arg[0]) {
         2346                 case 0: /* clear current tab stop */
         2347                         term.tabs[term.c.x] = 0;
         2348                         break;
         2349                 case 3: /* clear all the tabs */
         2350                         memset(term.tabs, 0, term.col * sizeof(*term.tabs));
         2351                         break;
         2352                 default:
         2353                         goto unknown;
         2354                 }
         2355                 break;
         2356         case 'G': /* CHA -- Move to <col> */
         2357         case '`': /* HPA */
         2358                 DEFAULT(csiescseq.arg[0], 1);
         2359                 tmoveto(csiescseq.arg[0]-1, term.c.y);
         2360                 break;
         2361         case 'H': /* CUP -- Move to <row> <col> */
         2362         case 'f': /* HVP */
         2363                 DEFAULT(csiescseq.arg[0], 1);
         2364                 DEFAULT(csiescseq.arg[1], 1);
         2365                 tmoveato(csiescseq.arg[1]-1, csiescseq.arg[0]-1);
         2366                 break;
         2367         case 'I': /* CHT -- Cursor Forward Tabulation <n> tab stops */
         2368                 DEFAULT(csiescseq.arg[0], 1);
         2369                 tputtab(csiescseq.arg[0]);
         2370                 break;
         2371         case 'J': /* ED -- Clear screen */
         2372                 selclear(NULL);
         2373                 switch (csiescseq.arg[0]) {
         2374                 case 0: /* below */
         2375                         tclearregion(term.c.x, term.c.y, term.col-1, term.c.y);
         2376                         if (term.c.y < term.row-1) {
         2377                                 tclearregion(0, term.c.y+1, term.col-1,
         2378                                                 term.row-1);
         2379                         }
         2380                         break;
         2381                 case 1: /* above */
         2382                         if (term.c.y > 1)
         2383                                 tclearregion(0, 0, term.col-1, term.c.y-1);
         2384                         tclearregion(0, term.c.y, term.c.x, term.c.y);
         2385                         break;
         2386                 case 2: /* all */
         2387                         tclearregion(0, 0, term.col-1, term.row-1);
         2388                         break;
         2389                 default:
         2390                         goto unknown;
         2391                 }
         2392                 break;
         2393         case 'K': /* EL -- Clear line */
         2394                 switch (csiescseq.arg[0]) {
         2395                 case 0: /* right */
         2396                         tclearregion(term.c.x, term.c.y, term.col-1,
         2397                                         term.c.y);
         2398                         break;
         2399                 case 1: /* left */
         2400                         tclearregion(0, term.c.y, term.c.x, term.c.y);
         2401                         break;
         2402                 case 2: /* all */
         2403                         tclearregion(0, term.c.y, term.col-1, term.c.y);
         2404                         break;
         2405                 }
         2406                 break;
         2407         case 'S': /* SU -- Scroll <n> line up */
         2408                 DEFAULT(csiescseq.arg[0], 1);
         2409                 tscrollup(term.top, csiescseq.arg[0]);
         2410                 break;
         2411         case 'T': /* SD -- Scroll <n> line down */
         2412                 DEFAULT(csiescseq.arg[0], 1);
         2413                 tscrolldown(term.top, csiescseq.arg[0]);
         2414                 break;
         2415         case 'L': /* IL -- Insert <n> blank lines */
         2416                 DEFAULT(csiescseq.arg[0], 1);
         2417                 tinsertblankline(csiescseq.arg[0]);
         2418                 break;
         2419         case 'l': /* RM -- Reset Mode */
         2420                 tsetmode(csiescseq.priv, 0, csiescseq.arg, csiescseq.narg);
         2421                 break;
         2422         case 'M': /* DL -- Delete <n> lines */
         2423                 DEFAULT(csiescseq.arg[0], 1);
         2424                 tdeleteline(csiescseq.arg[0]);
         2425                 break;
         2426         case 'X': /* ECH -- Erase <n> char */
         2427                 DEFAULT(csiescseq.arg[0], 1);
         2428                 tclearregion(term.c.x, term.c.y,
         2429                                 term.c.x + csiescseq.arg[0] - 1, term.c.y);
         2430                 break;
         2431         case 'P': /* DCH -- Delete <n> char */
         2432                 DEFAULT(csiescseq.arg[0], 1);
         2433                 tdeletechar(csiescseq.arg[0]);
         2434                 break;
         2435         case 'Z': /* CBT -- Cursor Backward Tabulation <n> tab stops */
         2436                 DEFAULT(csiescseq.arg[0], 1);
         2437                 tputtab(-csiescseq.arg[0]);
         2438                 break;
         2439         case 'd': /* VPA -- Move to <row> */
         2440                 DEFAULT(csiescseq.arg[0], 1);
         2441                 tmoveato(term.c.x, csiescseq.arg[0]-1);
         2442                 break;
         2443         case 'h': /* SM -- Set terminal mode */
         2444                 tsetmode(csiescseq.priv, 1, csiescseq.arg, csiescseq.narg);
         2445                 break;
         2446         case 'm': /* SGR -- Terminal attribute (color) */
         2447                 tsetattr(csiescseq.arg, csiescseq.narg);
         2448                 break;
         2449         case 'n': /* DSR – Device Status Report (cursor position) */
         2450                 if (csiescseq.arg[0] == 6) {
         2451                         len = snprintf(buf, sizeof(buf),"\033[%i;%iR",
         2452                                         term.c.y+1, term.c.x+1);
         2453                         ttywrite(buf, len);
         2454                 }
         2455                 break;
         2456         case 'r': /* DECSTBM -- Set Scrolling Region */
         2457                 if (csiescseq.priv) {
         2458                         goto unknown;
         2459                 } else {
         2460                         DEFAULT(csiescseq.arg[0], 1);
         2461                         DEFAULT(csiescseq.arg[1], term.row);
         2462                         tsetscroll(csiescseq.arg[0]-1, csiescseq.arg[1]-1);
         2463                         tmoveato(0, 0);
         2464                 }
         2465                 break;
         2466         case 's': /* DECSC -- Save cursor position (ANSI.SYS) */
         2467                 tcursor(CURSOR_SAVE);
         2468                 break;
         2469         case 'u': /* DECRC -- Restore cursor position (ANSI.SYS) */
         2470                 tcursor(CURSOR_LOAD);
         2471                 break;
         2472         case ' ':
         2473                 switch (csiescseq.mode[1]) {
         2474                 case 'q': /* DECSCUSR -- Set Cursor Style */
         2475                         DEFAULT(csiescseq.arg[0], 1);
         2476                         if (!BETWEEN(csiescseq.arg[0], 0, 6)) {
         2477                                 goto unknown;
         2478                         }
         2479                         xw.cursor = csiescseq.arg[0];
         2480                         break;
         2481                 default:
         2482                         goto unknown;
         2483                 }
         2484                 break;
         2485         }
         2486 }
         2487 
         2488 void
         2489 csidump(void)
         2490 {
         2491         int i;
         2492         uint c;
         2493 
         2494         fprintf(stderr, "ESC[");
         2495         for (i = 0; i < csiescseq.len; i++) {
         2496                 c = csiescseq.buf[i] & 0xff;
         2497                 if (isprint(c)) {
         2498                         putc(c, stderr);
         2499                 } else if (c == '\n') {
         2500                         fprintf(stderr, "(\\n)");
         2501                 } else if (c == '\r') {
         2502                         fprintf(stderr, "(\\r)");
         2503                 } else if (c == 0x1b) {
         2504                         fprintf(stderr, "(\\e)");
         2505                 } else {
         2506                         fprintf(stderr, "(%02x)", c);
         2507                 }
         2508         }
         2509         putc('\n', stderr);
         2510 }
         2511 
         2512 void
         2513 csireset(void)
         2514 {
         2515         memset(&csiescseq, 0, sizeof(csiescseq));
         2516 }
         2517 
         2518 void
         2519 strhandle(void)
         2520 {
         2521         char *p = NULL;
         2522         int j, narg, par;
         2523 
         2524         term.esc &= ~(ESC_STR_END|ESC_STR);
         2525         strparse();
         2526         par = (narg = strescseq.narg) ? atoi(strescseq.args[0]) : 0;
         2527 
         2528         switch (strescseq.type) {
         2529         case ']': /* OSC -- Operating System Command */
         2530                 switch (par) {
         2531                 case 0:
         2532                 case 1:
         2533                 case 2:
         2534                         if (narg > 1)
         2535                                 xsettitle(strescseq.args[1]);
         2536                         return;
         2537                 case 4: /* color set */
         2538                         if (narg < 3)
         2539                                 break;
         2540                         p = strescseq.args[2];
         2541                         /* FALLTHROUGH */
         2542                 case 104: /* color reset, here p = NULL */
         2543                         j = (narg > 1) ? atoi(strescseq.args[1]) : -1;
         2544                         if (xsetcolorname(j, p)) {
         2545                                 fprintf(stderr, "erresc: invalid color %s\n", p);
         2546                         } else {
         2547                                 /*
         2548                                  * TODO if defaultbg color is changed, borders
         2549                                  * are dirty
         2550                                  */
         2551                                 redraw();
         2552                         }
         2553                         return;
         2554                 }
         2555                 break;
         2556         case 'k': /* old title set compatibility */
         2557                 xsettitle(strescseq.args[0]);
         2558                 return;
         2559         case 'P': /* DCS -- Device Control String */
         2560                 term.mode |= ESC_DCS;
         2561         case '_': /* APC -- Application Program Command */
         2562         case '^': /* PM -- Privacy Message */
         2563                 return;
         2564         }
         2565 
         2566         fprintf(stderr, "erresc: unknown str ");
         2567         strdump();
         2568 }
         2569 
         2570 void
         2571 strparse(void)
         2572 {
         2573         int c;
         2574         char *p = strescseq.buf;
         2575 
         2576         strescseq.narg = 0;
         2577         strescseq.buf[strescseq.len] = '\0';
         2578 
         2579         if (*p == '\0')
         2580                 return;
         2581 
         2582         while (strescseq.narg < STR_ARG_SIZ) {
         2583                 strescseq.args[strescseq.narg++] = p;
         2584                 while ((c = *p) != ';' && c != '\0')
         2585                         ++p;
         2586                 if (c == '\0')
         2587                         return;
         2588                 *p++ = '\0';
         2589         }
         2590 }
         2591 
         2592 void
         2593 strdump(void)
         2594 {
         2595         int i;
         2596         uint c;
         2597 
         2598         fprintf(stderr, "ESC%c", strescseq.type);
         2599         for (i = 0; i < strescseq.len; i++) {
         2600                 c = strescseq.buf[i] & 0xff;
         2601                 if (c == '\0') {
         2602                         putc('\n', stderr);
         2603                         return;
         2604                 } else if (isprint(c)) {
         2605                         putc(c, stderr);
         2606                 } else if (c == '\n') {
         2607                         fprintf(stderr, "(\\n)");
         2608                 } else if (c == '\r') {
         2609                         fprintf(stderr, "(\\r)");
         2610                 } else if (c == 0x1b) {
         2611                         fprintf(stderr, "(\\e)");
         2612                 } else {
         2613                         fprintf(stderr, "(%02x)", c);
         2614                 }
         2615         }
         2616         fprintf(stderr, "ESC\\\n");
         2617 }
         2618 
         2619 void
         2620 strreset(void)
         2621 {
         2622         memset(&strescseq, 0, sizeof(strescseq));
         2623 }
         2624 
         2625 void
         2626 sendbreak(const Arg *arg)
         2627 {
         2628         if (tcsendbreak(cmdfd, 0))
         2629                 perror("Error sending break");
         2630 }
         2631 
         2632 void
         2633 tprinter(char *s, size_t len)
         2634 {
         2635         if (iofd != -1 && xwrite(iofd, s, len) < 0) {
         2636                 fprintf(stderr, "Error writing in %s:%s\n",
         2637                         opt_io, strerror(errno));
         2638                 close(iofd);
         2639                 iofd = -1;
         2640         }
         2641 }
         2642 
         2643 void
         2644 iso14755(const Arg *arg)
         2645 {
         2646         char cmd[sizeof(ISO14755CMD) + NUMMAXLEN(xw.win)];
         2647         FILE *p;
         2648         char *us, *e, codepoint[9], uc[UTF_SIZ];
         2649         unsigned long utf32;
         2650 
         2651         snprintf(cmd, sizeof(cmd), ISO14755CMD, xw.win);
         2652         if (!(p = popen(cmd, "r")))
         2653                 return;
         2654 
         2655         us = fgets(codepoint, sizeof(codepoint), p);
         2656         pclose(p);
         2657 
         2658         if (!us || *us == '\0' || *us == '-' || strlen(us) > 7)
         2659                 return;
         2660         if ((utf32 = strtoul(us, &e, 16)) == ULONG_MAX ||
         2661             (*e != '\n' && *e != '\0'))
         2662                 return;
         2663 
         2664         ttysend(uc, utf8encode(utf32, uc));
         2665 }
         2666 
         2667 void
         2668 toggleprinter(const Arg *arg)
         2669 {
         2670         term.mode ^= MODE_PRINT;
         2671 }
         2672 
         2673 void
         2674 printscreen(const Arg *arg)
         2675 {
         2676         tdump();
         2677 }
         2678 
         2679 void
         2680 printsel(const Arg *arg)
         2681 {
         2682         tdumpsel();
         2683 }
         2684 
         2685 void
         2686 tdumpsel(void)
         2687 {
         2688         char *ptr;
         2689 
         2690         if ((ptr = getsel())) {
         2691                 tprinter(ptr, strlen(ptr));
         2692                 free(ptr);
         2693         }
         2694 }
         2695 
         2696 void
         2697 tdumpline(int n)
         2698 {
         2699         char buf[UTF_SIZ];
         2700         Glyph *bp, *end;
         2701 
         2702         bp = &term.line[n][0];
         2703         end = &bp[MIN(tlinelen(n), term.col) - 1];
         2704         if (bp != end || bp->u != ' ') {
         2705                 for ( ;bp <= end; ++bp)
         2706                         tprinter(buf, utf8encode(bp->u, buf));
         2707         }
         2708         tprinter("\n", 1);
         2709 }
         2710 
         2711 void
         2712 tdump(void)
         2713 {
         2714         int i;
         2715 
         2716         for (i = 0; i < term.row; ++i)
         2717                 tdumpline(i);
         2718 }
         2719 
         2720 void
         2721 tputtab(int n)
         2722 {
         2723         uint x = term.c.x;
         2724 
         2725         if (n > 0) {
         2726                 while (x < term.col && n--)
         2727                         for (++x; x < term.col && !term.tabs[x]; ++x)
         2728                                 /* nothing */ ;
         2729         } else if (n < 0) {
         2730                 while (x > 0 && n++)
         2731                         for (--x; x > 0 && !term.tabs[x]; --x)
         2732                                 /* nothing */ ;
         2733         }
         2734         term.c.x = LIMIT(x, 0, term.col-1);
         2735 }
         2736 
         2737 void
         2738 techo(Rune u)
         2739 {
         2740         if (ISCONTROL(u)) { /* control code */
         2741                 if (u & 0x80) {
         2742                         u &= 0x7f;
         2743                         tputc('^');
         2744                         tputc('[');
         2745                 } else if (u != '\n' && u != '\r' && u != '\t') {
         2746                         u ^= 0x40;
         2747                         tputc('^');
         2748                 }
         2749         }
         2750         tputc(u);
         2751 }
         2752 
         2753 void
         2754 tdefutf8(char ascii)
         2755 {
         2756         if (ascii == 'G')
         2757                 term.mode |= MODE_UTF8;
         2758         else if (ascii == '@')
         2759                 term.mode &= ~MODE_UTF8;
         2760 }
         2761 
         2762 void
         2763 tdeftran(char ascii)
         2764 {
         2765         static char cs[] = "0B";
         2766         static int vcs[] = {CS_GRAPHIC0, CS_USA};
         2767         char *p;
         2768 
         2769         if ((p = strchr(cs, ascii)) == NULL) {
         2770                 fprintf(stderr, "esc unhandled charset: ESC ( %c\n", ascii);
         2771         } else {
         2772                 term.trantbl[term.icharset] = vcs[p - cs];
         2773         }
         2774 }
         2775 
         2776 void
         2777 tdectest(char c)
         2778 {
         2779         int x, y;
         2780 
         2781         if (c == '8') { /* DEC screen alignment test. */
         2782                 for (x = 0; x < term.col; ++x) {
         2783                         for (y = 0; y < term.row; ++y)
         2784                                 tsetchar('E', &term.c.attr, x, y);
         2785                 }
         2786         }
         2787 }
         2788 
         2789 void
         2790 tstrsequence(uchar c)
         2791 {
         2792         strreset();
         2793 
         2794         switch (c) {
         2795         case 0x90:   /* DCS -- Device Control String */
         2796                 c = 'P';
         2797                 term.esc |= ESC_DCS;
         2798                 break;
         2799         case 0x9f:   /* APC -- Application Program Command */
         2800                 c = '_';
         2801                 break;
         2802         case 0x9e:   /* PM -- Privacy Message */
         2803                 c = '^';
         2804                 break;
         2805         case 0x9d:   /* OSC -- Operating System Command */
         2806                 c = ']';
         2807                 break;
         2808         }
         2809         strescseq.type = c;
         2810         term.esc |= ESC_STR;
         2811 }
         2812 
         2813 void
         2814 tcontrolcode(uchar ascii)
         2815 {
         2816         switch (ascii) {
         2817         case '\t':   /* HT */
         2818                 tputtab(1);
         2819                 return;
         2820         case '\b':   /* BS */
         2821                 tmoveto(term.c.x-1, term.c.y);
         2822                 return;
         2823         case '\r':   /* CR */
         2824                 tmoveto(0, term.c.y);
         2825                 return;
         2826         case '\f':   /* LF */
         2827         case '\v':   /* VT */
         2828         case '\n':   /* LF */
         2829                 /* go to first col if the mode is set */
         2830                 tnewline(IS_SET(MODE_CRLF));
         2831                 return;
         2832         case '\a':   /* BEL */
         2833                 if (term.esc & ESC_STR_END) {
         2834                         /* backwards compatibility to xterm */
         2835                         strhandle();
         2836                 } else {
         2837                         if (!(xw.state & WIN_FOCUSED))
         2838                                 xseturgency(1);
         2839                         if (bellvolume)
         2840                                 XkbBell(xw.dpy, xw.win, bellvolume, (Atom)NULL);
         2841                 }
         2842                 break;
         2843         case '\033': /* ESC */
         2844                 csireset();
         2845                 term.esc &= ~(ESC_CSI|ESC_ALTCHARSET|ESC_TEST);
         2846                 term.esc |= ESC_START;
         2847                 return;
         2848         case '\016': /* SO (LS1 -- Locking shift 1) */
         2849         case '\017': /* SI (LS0 -- Locking shift 0) */
         2850                 term.charset = 1 - (ascii - '\016');
         2851                 return;
         2852         case '\032': /* SUB */
         2853                 tsetchar('?', &term.c.attr, term.c.x, term.c.y);
         2854         case '\030': /* CAN */
         2855                 csireset();
         2856                 break;
         2857         case '\005': /* ENQ (IGNORED) */
         2858         case '\000': /* NUL (IGNORED) */
         2859         case '\021': /* XON (IGNORED) */
         2860         case '\023': /* XOFF (IGNORED) */
         2861         case 0177:   /* DEL (IGNORED) */
         2862                 return;
         2863         case 0x80:   /* TODO: PAD */
         2864         case 0x81:   /* TODO: HOP */
         2865         case 0x82:   /* TODO: BPH */
         2866         case 0x83:   /* TODO: NBH */
         2867         case 0x84:   /* TODO: IND */
         2868                 break;
         2869         case 0x85:   /* NEL -- Next line */
         2870                 tnewline(1); /* always go to first col */
         2871                 break;
         2872         case 0x86:   /* TODO: SSA */
         2873         case 0x87:   /* TODO: ESA */
         2874                 break;
         2875         case 0x88:   /* HTS -- Horizontal tab stop */
         2876                 term.tabs[term.c.x] = 1;
         2877                 break;
         2878         case 0x89:   /* TODO: HTJ */
         2879         case 0x8a:   /* TODO: VTS */
         2880         case 0x8b:   /* TODO: PLD */
         2881         case 0x8c:   /* TODO: PLU */
         2882         case 0x8d:   /* TODO: RI */
         2883         case 0x8e:   /* TODO: SS2 */
         2884         case 0x8f:   /* TODO: SS3 */
         2885         case 0x91:   /* TODO: PU1 */
         2886         case 0x92:   /* TODO: PU2 */
         2887         case 0x93:   /* TODO: STS */
         2888         case 0x94:   /* TODO: CCH */
         2889         case 0x95:   /* TODO: MW */
         2890         case 0x96:   /* TODO: SPA */
         2891         case 0x97:   /* TODO: EPA */
         2892         case 0x98:   /* TODO: SOS */
         2893         case 0x99:   /* TODO: SGCI */
         2894                 break;
         2895         case 0x9a:   /* DECID -- Identify Terminal */
         2896                 ttywrite(vtiden, sizeof(vtiden) - 1);
         2897                 break;
         2898         case 0x9b:   /* TODO: CSI */
         2899         case 0x9c:   /* TODO: ST */
         2900                 break;
         2901         case 0x90:   /* DCS -- Device Control String */
         2902         case 0x9d:   /* OSC -- Operating System Command */
         2903         case 0x9e:   /* PM -- Privacy Message */
         2904         case 0x9f:   /* APC -- Application Program Command */
         2905                 tstrsequence(ascii);
         2906                 return;
         2907         }
         2908         /* only CAN, SUB, \a and C1 chars interrupt a sequence */
         2909         term.esc &= ~(ESC_STR_END|ESC_STR);
         2910 }
         2911 
         2912 /*
         2913  * returns 1 when the sequence is finished and it hasn't to read
         2914  * more characters for this sequence, otherwise 0
         2915  */
         2916 int
         2917 eschandle(uchar ascii)
         2918 {
         2919         switch (ascii) {
         2920         case '[':
         2921                 term.esc |= ESC_CSI;
         2922                 return 0;
         2923         case '#':
         2924                 term.esc |= ESC_TEST;
         2925                 return 0;
         2926         case '%':
         2927                 term.esc |= ESC_UTF8;
         2928                 return 0;
         2929         case 'P': /* DCS -- Device Control String */
         2930         case '_': /* APC -- Application Program Command */
         2931         case '^': /* PM -- Privacy Message */
         2932         case ']': /* OSC -- Operating System Command */
         2933         case 'k': /* old title set compatibility */
         2934                 tstrsequence(ascii);
         2935                 return 0;
         2936         case 'n': /* LS2 -- Locking shift 2 */
         2937         case 'o': /* LS3 -- Locking shift 3 */
         2938                 term.charset = 2 + (ascii - 'n');
         2939                 break;
         2940         case '(': /* GZD4 -- set primary charset G0 */
         2941         case ')': /* G1D4 -- set secondary charset G1 */
         2942         case '*': /* G2D4 -- set tertiary charset G2 */
         2943         case '+': /* G3D4 -- set quaternary charset G3 */
         2944                 term.icharset = ascii - '(';
         2945                 term.esc |= ESC_ALTCHARSET;
         2946                 return 0;
         2947         case 'D': /* IND -- Linefeed */
         2948                 if (term.c.y == term.bot) {
         2949                         tscrollup(term.top, 1);
         2950                 } else {
         2951                         tmoveto(term.c.x, term.c.y+1);
         2952                 }
         2953                 break;
         2954         case 'E': /* NEL -- Next line */
         2955                 tnewline(1); /* always go to first col */
         2956                 break;
         2957         case 'H': /* HTS -- Horizontal tab stop */
         2958                 term.tabs[term.c.x] = 1;
         2959                 break;
         2960         case 'M': /* RI -- Reverse index */
         2961                 if (term.c.y == term.top) {
         2962                         tscrolldown(term.top, 1);
         2963                 } else {
         2964                         tmoveto(term.c.x, term.c.y-1);
         2965                 }
         2966                 break;
         2967         case 'Z': /* DECID -- Identify Terminal */
         2968                 ttywrite(vtiden, sizeof(vtiden) - 1);
         2969                 break;
         2970         case 'c': /* RIS -- Reset to inital state */
         2971                 treset();
         2972                 xresettitle();
         2973                 xloadcols();
         2974                 break;
         2975         case '=': /* DECPAM -- Application keypad */
         2976                 term.mode |= MODE_APPKEYPAD;
         2977                 break;
         2978         case '>': /* DECPNM -- Normal keypad */
         2979                 term.mode &= ~MODE_APPKEYPAD;
         2980                 break;
         2981         case '7': /* DECSC -- Save Cursor */
         2982                 tcursor(CURSOR_SAVE);
         2983                 break;
         2984         case '8': /* DECRC -- Restore Cursor */
         2985                 tcursor(CURSOR_LOAD);
         2986                 break;
         2987         case '\\': /* ST -- String Terminator */
         2988                 if (term.esc & ESC_STR_END)
         2989                         strhandle();
         2990                 break;
         2991         default:
         2992                 fprintf(stderr, "erresc: unknown sequence ESC 0x%02X '%c'\n",
         2993                         (uchar) ascii, isprint(ascii)? ascii:'.');
         2994                 break;
         2995         }
         2996         return 1;
         2997 }
         2998 
         2999 void
         3000 externalpipe(const Arg *arg)
         3001 {
         3002         int to[2]; /* 0 = read, 1 = write */
         3003         pid_t child;
         3004         int n;
         3005         void (*oldsigpipe)(int);
         3006         char buf[UTF_SIZ];
         3007         Glyph *bp, *end;
         3008 
         3009         if(pipe(to) == -1)
         3010                 return;
         3011 
         3012         /* sigchld() handles this */
         3013         switch(child = fork()){
         3014                 case -1:
         3015                         close(to[0]), close(to[1]);
         3016                         return;
         3017                 case 0:
         3018                         /* child */
         3019                         close(to[1]);
         3020                         dup2(to[0], STDIN_FILENO); /* 0<&to */
         3021                         close(to[0]);
         3022                         execvp(
         3023                                         "sh",
         3024                                         (char *const []){
         3025                                                 "/bin/sh",
         3026                                                 "-c",
         3027                                                 (char *)arg->v,
         3028                                                 0
         3029                                         });
         3030                         exit(127);
         3031         }
         3032 
         3033         /* parent */
         3034         close(to[0]);
         3035         /* ignore sigpipe for now, in case child exits early */
         3036         oldsigpipe = signal(SIGPIPE, SIG_IGN);
         3037 
         3038         for(n = 0; n < term.row; n++){
         3039                 bp = &term.line[n][0];
         3040                 end = &bp[MIN(tlinelen(n), term.col) - 1];
         3041                 if(bp != end || bp->u != ' ')
         3042                         for(; bp <= end; ++bp)
         3043                                 if(xwrite(to[1], buf, utf8encode(bp->u, buf)) < 0)
         3044                                         break;
         3045                 if(xwrite(to[1], "\n", 1) < 0)
         3046                         break;
         3047         }
         3048 
         3049         close(to[1]);
         3050 
         3051         /* restore */
         3052         signal(SIGPIPE, oldsigpipe);
         3053 }
         3054 
         3055 void
         3056 tputc(Rune u)
         3057 {
         3058         char c[UTF_SIZ];
         3059         int control;
         3060         int width, len;
         3061         Glyph *gp;
         3062 
         3063         control = ISCONTROL(u);
         3064         if (!IS_SET(MODE_UTF8) && !IS_SET(MODE_SIXEL)) {
         3065                 c[0] = u;
         3066                 width = len = 1;
         3067         } else {
         3068                 len = utf8encode(u, c);
         3069                 if (!control && (width = wcwidth(u)) == -1) {
         3070                         memcpy(c, "\357\277\275", 4); /* UTF_INVALID */
         3071                         width = 1;
         3072                 }
         3073         }
         3074 
         3075         if (IS_SET(MODE_PRINT))
         3076                 tprinter(c, len);
         3077 
         3078         /*
         3079          * STR sequence must be checked before anything else
         3080          * because it uses all following characters until it
         3081          * receives a ESC, a SUB, a ST or any other C1 control
         3082          * character.
         3083          */
         3084         if (term.esc & ESC_STR) {
         3085                 if (u == '\a' || u == 030 || u == 032 || u == 033 ||
         3086                    ISCONTROLC1(u)) {
         3087                         term.esc &= ~(ESC_START|ESC_STR|ESC_DCS);
         3088                         if (IS_SET(MODE_SIXEL)) {
         3089                                 /* TODO: render sixel */;
         3090                                 term.mode &= ~MODE_SIXEL;
         3091                                 return;
         3092                         }
         3093                         term.esc |= ESC_STR_END;
         3094                         goto check_control_code;
         3095                 }
         3096 
         3097 
         3098                 if (IS_SET(MODE_SIXEL)) {
         3099                         /* TODO: implement sixel mode */
         3100                         return;
         3101                 }
         3102                 if (term.esc&ESC_DCS && strescseq.len == 0 && u == 'q')
         3103                         term.mode |= MODE_SIXEL;
         3104 
         3105                 if (strescseq.len+len >= sizeof(strescseq.buf)-1) {
         3106                         /*
         3107                          * Here is a bug in terminals. If the user never sends
         3108                          * some code to stop the str or esc command, then st
         3109                          * will stop responding. But this is better than
         3110                          * silently failing with unknown characters. At least
         3111                          * then users will report back.
         3112                          *
         3113                          * In the case users ever get fixed, here is the code:
         3114                          */
         3115                         /*
         3116                          * term.esc = 0;
         3117                          * strhandle();
         3118                          */
         3119                         return;
         3120                 }
         3121 
         3122                 memmove(&strescseq.buf[strescseq.len], c, len);
         3123                 strescseq.len += len;
         3124                 return;
         3125         }
         3126 
         3127 check_control_code:
         3128         /*
         3129          * Actions of control codes must be performed as soon they arrive
         3130          * because they can be embedded inside a control sequence, and
         3131          * they must not cause conflicts with sequences.
         3132          */
         3133         if (control) {
         3134                 tcontrolcode(u);
         3135                 /*
         3136                  * control codes are not shown ever
         3137                  */
         3138                 return;
         3139         } else if (term.esc & ESC_START) {
         3140                 if (term.esc & ESC_CSI) {
         3141                         csiescseq.buf[csiescseq.len++] = u;
         3142                         if (BETWEEN(u, 0x40, 0x7E)
         3143                                         || csiescseq.len >= \
         3144                                         sizeof(csiescseq.buf)-1) {
         3145                                 term.esc = 0;
         3146                                 csiparse();
         3147                                 csihandle();
         3148                         }
         3149                         return;
         3150                 } else if (term.esc & ESC_UTF8) {
         3151                         tdefutf8(u);
         3152                 } else if (term.esc & ESC_ALTCHARSET) {
         3153                         tdeftran(u);
         3154                 } else if (term.esc & ESC_TEST) {
         3155                         tdectest(u);
         3156                 } else {
         3157                         if (!eschandle(u))
         3158                                 return;
         3159                         /* sequence already finished */
         3160                 }
         3161                 term.esc = 0;
         3162                 /*
         3163                  * All characters which form part of a sequence are not
         3164                  * printed
         3165                  */
         3166                 return;
         3167         }
         3168         if (sel.ob.x != -1 && BETWEEN(term.c.y, sel.ob.y, sel.oe.y))
         3169                 selclear(NULL);
         3170 
         3171         gp = &term.line[term.c.y][term.c.x];
         3172         if (IS_SET(MODE_WRAP) && (term.c.state & CURSOR_WRAPNEXT)) {
         3173                 gp->mode |= ATTR_WRAP;
         3174                 tnewline(1);
         3175                 gp = &term.line[term.c.y][term.c.x];
         3176         }
         3177 
         3178         if (IS_SET(MODE_INSERT) && term.c.x+width < term.col)
         3179                 memmove(gp+width, gp, (term.col - term.c.x - width) * sizeof(Glyph));
         3180 
         3181         if (term.c.x+width > term.col) {
         3182                 tnewline(1);
         3183                 gp = &term.line[term.c.y][term.c.x];
         3184         }
         3185 
         3186         tsetchar(u, &term.c.attr, term.c.x, term.c.y);
         3187 
         3188         if (width == 2) {
         3189                 gp->mode |= ATTR_WIDE;
         3190                 if (term.c.x+1 < term.col) {
         3191                         gp[1].u = '\0';
         3192                         gp[1].mode = ATTR_WDUMMY;
         3193                 }
         3194         }
         3195         if (term.c.x+width < term.col) {
         3196                 tmoveto(term.c.x+width, term.c.y);
         3197         } else {
         3198                 term.c.state |= CURSOR_WRAPNEXT;
         3199         }
         3200 }
         3201 
         3202 void
         3203 tresize(int col, int row)
         3204 {
         3205         int i;
         3206         int minrow = MIN(row, term.row);
         3207         int mincol = MIN(col, term.col);
         3208         int *bp;
         3209         TCursor c;
         3210 
         3211         if (col < 1 || row < 1) {
         3212                 fprintf(stderr,
         3213                         "tresize: error resizing to %dx%d\n", col, row);
         3214                 return;
         3215         }
         3216 
         3217         /*
         3218          * slide screen to keep cursor where we expect it -
         3219          * tscrollup would work here, but we can optimize to
         3220          * memmove because we're freeing the earlier lines
         3221          */
         3222         for (i = 0; i <= term.c.y - row; i++) {
         3223                 free(term.line[i]);
         3224                 free(term.alt[i]);
         3225         }
         3226         /* ensure that both src and dst are not NULL */
         3227         if (i > 0) {
         3228                 memmove(term.line, term.line + i, row * sizeof(Line));
         3229                 memmove(term.alt, term.alt + i, row * sizeof(Line));
         3230         }
         3231         for (i += row; i < term.row; i++) {
         3232                 free(term.line[i]);
         3233                 free(term.alt[i]);
         3234         }
         3235 
         3236         /* resize to new width */
         3237         term.specbuf = xrealloc(term.specbuf, col * sizeof(XftGlyphFontSpec));
         3238 
         3239         /* resize to new height */
         3240         term.line = xrealloc(term.line, row * sizeof(Line));
         3241         term.alt  = xrealloc(term.alt,  row * sizeof(Line));
         3242         term.dirty = xrealloc(term.dirty, row * sizeof(*term.dirty));
         3243         term.tabs = xrealloc(term.tabs, col * sizeof(*term.tabs));
         3244 
         3245         /* resize each row to new width, zero-pad if needed */
         3246         for (i = 0; i < minrow; i++) {
         3247                 term.line[i] = xrealloc(term.line[i], col * sizeof(Glyph));
         3248                 term.alt[i]  = xrealloc(term.alt[i],  col * sizeof(Glyph));
         3249         }
         3250 
         3251         /* allocate any new rows */
         3252         for (/* i == minrow */; i < row; i++) {
         3253                 term.line[i] = xmalloc(col * sizeof(Glyph));
         3254                 term.alt[i] = xmalloc(col * sizeof(Glyph));
         3255         }
         3256         if (col > term.col) {
         3257                 bp = term.tabs + term.col;
         3258 
         3259                 memset(bp, 0, sizeof(*term.tabs) * (col - term.col));
         3260                 while (--bp > term.tabs && !*bp)
         3261                         /* nothing */ ;
         3262                 for (bp += tabspaces; bp < term.tabs + col; bp += tabspaces)
         3263                         *bp = 1;
         3264         }
         3265         /* update terminal size */
         3266         term.col = col;
         3267         term.row = row;
         3268         /* reset scrolling region */
         3269         tsetscroll(0, row-1);
         3270         /* make use of the LIMIT in tmoveto */
         3271         tmoveto(term.c.x, term.c.y);
         3272         /* Clearing both screens (it makes dirty all lines) */
         3273         c = term.c;
         3274         for (i = 0; i < 2; i++) {
         3275                 if (mincol < col && 0 < minrow) {
         3276                         tclearregion(mincol, 0, col - 1, minrow - 1);
         3277                 }
         3278                 if (0 < col && minrow < row) {
         3279                         tclearregion(0, minrow, col - 1, row - 1);
         3280                 }
         3281                 tswapscreen();
         3282                 tcursor(CURSOR_LOAD);
         3283         }
         3284         term.c = c;
         3285 }
         3286 
         3287 void
         3288 xresize(int col, int row)
         3289 {
         3290         xw.tw = MAX(1, col * xw.cw);
         3291         xw.th = MAX(1, row * xw.ch);
         3292 
         3293         XFreePixmap(xw.dpy, xw.buf);
         3294         xw.buf = XCreatePixmap(xw.dpy, xw.win, xw.w, xw.h,
         3295                         DefaultDepth(xw.dpy, xw.scr));
         3296         XftDrawChange(xw.draw, xw.buf);
         3297         xclear(0, 0, xw.w, xw.h);
         3298 }
         3299 
         3300 ushort
         3301 sixd_to_16bit(int x)
         3302 {
         3303         return x == 0 ? 0 : 0x3737 + 0x2828 * x;
         3304 }
         3305 
         3306 int
         3307 xloadcolor(int i, const char *name, Color *ncolor)
         3308 {
         3309         XRenderColor color = { .alpha = 0xffff };
         3310 
         3311         if (!name) {
         3312                 if (BETWEEN(i, 16, 255)) { /* 256 color */
         3313                         if (i < 6*6*6+16) { /* same colors as xterm */
         3314                                 color.red   = sixd_to_16bit( ((i-16)/36)%6 );
         3315                                 color.green = sixd_to_16bit( ((i-16)/6) %6 );
         3316                                 color.blue  = sixd_to_16bit( ((i-16)/1) %6 );
         3317                         } else { /* greyscale */
         3318                                 color.red = 0x0808 + 0x0a0a * (i - (6*6*6+16));
         3319                                 color.green = color.blue = color.red;
         3320                         }
         3321                         return XftColorAllocValue(xw.dpy, xw.vis,
         3322                                                   xw.cmap, &color, ncolor);
         3323                 } else
         3324                         name = colorname[i];
         3325         }
         3326 
         3327         return XftColorAllocName(xw.dpy, xw.vis, xw.cmap, name, ncolor);
         3328 }
         3329 
         3330 void
         3331 xloadcols(void)
         3332 {
         3333         int i;
         3334         static int loaded;
         3335         Color *cp;
         3336 
         3337         if (loaded) {
         3338                 for (cp = dc.col; cp < &dc.col[LEN(dc.col)]; ++cp)
         3339                         XftColorFree(xw.dpy, xw.vis, xw.cmap, cp);
         3340         }
         3341 
         3342         for (i = 0; i < LEN(dc.col); i++)
         3343                 if (!xloadcolor(i, NULL, &dc.col[i])) {
         3344                         if (colorname[i])
         3345                                 die("Could not allocate color '%s'\n", colorname[i]);
         3346                         else
         3347                                 die("Could not allocate color %d\n", i);
         3348                 }
         3349         loaded = 1;
         3350 }
         3351 
         3352 int
         3353 xsetcolorname(int x, const char *name)
         3354 {
         3355         Color ncolor;
         3356 
         3357         if (!BETWEEN(x, 0, LEN(dc.col)))
         3358                 return 1;
         3359 
         3360 
         3361         if (!xloadcolor(x, name, &ncolor))
         3362                 return 1;
         3363 
         3364         XftColorFree(xw.dpy, xw.vis, xw.cmap, &dc.col[x]);
         3365         dc.col[x] = ncolor;
         3366 
         3367         return 0;
         3368 }
         3369 
         3370 /*
         3371  * Absolute coordinates.
         3372  */
         3373 void
         3374 xclear(int x1, int y1, int x2, int y2)
         3375 {
         3376         XftDrawRect(xw.draw,
         3377                         &dc.col[IS_SET(MODE_REVERSE)? defaultfg : defaultbg],
         3378                         x1, y1, x2-x1, y2-y1);
         3379 }
         3380 
         3381 void
         3382 xhints(void)
         3383 {
         3384         XClassHint class = {opt_name ? opt_name : termname,
         3385                             opt_class ? opt_class : termname};
         3386         XWMHints wm = {.flags = InputHint, .input = 1};
         3387         XSizeHints *sizeh = NULL;
         3388 
         3389         sizeh = XAllocSizeHints();
         3390 
         3391         sizeh->flags = PSize | PResizeInc | PBaseSize;
         3392         sizeh->height = xw.h;
         3393         sizeh->width = xw.w;
         3394         sizeh->height_inc = xw.ch;
         3395         sizeh->width_inc = xw.cw;
         3396         sizeh->base_height = 2 * borderpx;
         3397         sizeh->base_width = 2 * borderpx;
         3398         if (xw.isfixed) {
         3399                 sizeh->flags |= PMaxSize | PMinSize;
         3400                 sizeh->min_width = sizeh->max_width = xw.w;
         3401                 sizeh->min_height = sizeh->max_height = xw.h;
         3402         }
         3403         if (xw.gm & (XValue|YValue)) {
         3404                 sizeh->flags |= USPosition | PWinGravity;
         3405                 sizeh->x = xw.l;
         3406                 sizeh->y = xw.t;
         3407                 sizeh->win_gravity = xgeommasktogravity(xw.gm);
         3408         }
         3409 
         3410         XSetWMProperties(xw.dpy, xw.win, NULL, NULL, NULL, 0, sizeh, &wm,
         3411                         &class);
         3412         XFree(sizeh);
         3413 }
         3414 
         3415 int
         3416 xgeommasktogravity(int mask)
         3417 {
         3418         switch (mask & (XNegative|YNegative)) {
         3419         case 0:
         3420                 return NorthWestGravity;
         3421         case XNegative:
         3422                 return NorthEastGravity;
         3423         case YNegative:
         3424                 return SouthWestGravity;
         3425         }
         3426 
         3427         return SouthEastGravity;
         3428 }
         3429 
         3430 int
         3431 xloadfont(Font *f, FcPattern *pattern)
         3432 {
         3433         FcPattern *configured;
         3434         FcPattern *match;
         3435         FcResult result;
         3436         XGlyphInfo extents;
         3437         int wantattr, haveattr;
         3438 
         3439         /*
         3440          * Manually configure instead of calling XftMatchFont
         3441          * so that we can use the configured pattern for
         3442          * "missing glyph" lookups.
         3443          */
         3444         configured = FcPatternDuplicate(pattern);
         3445         if (!configured)
         3446                 return 1;
         3447 
         3448         FcConfigSubstitute(NULL, configured, FcMatchPattern);
         3449         XftDefaultSubstitute(xw.dpy, xw.scr, configured);
         3450 
         3451         match = FcFontMatch(NULL, configured, &result);
         3452         if (!match) {
         3453                 FcPatternDestroy(configured);
         3454                 return 1;
         3455         }
         3456 
         3457         if (!(f->match = XftFontOpenPattern(xw.dpy, match))) {
         3458                 FcPatternDestroy(configured);
         3459                 FcPatternDestroy(match);
         3460                 return 1;
         3461         }
         3462 
         3463         if ((XftPatternGetInteger(pattern, "slant", 0, &wantattr) ==
         3464             XftResultMatch)) {
         3465                 /*
         3466                  * Check if xft was unable to find a font with the appropriate
         3467                  * slant but gave us one anyway. Try to mitigate.
         3468                  */
         3469                 if ((XftPatternGetInteger(f->match->pattern, "slant", 0,
         3470                     &haveattr) != XftResultMatch) || haveattr < wantattr) {
         3471                         f->badslant = 1;
         3472                         fputs("st: font slant does not match\n", stderr);
         3473                 }
         3474         }
         3475 
         3476         if ((XftPatternGetInteger(pattern, "weight", 0, &wantattr) ==
         3477             XftResultMatch)) {
         3478                 if ((XftPatternGetInteger(f->match->pattern, "weight", 0,
         3479                     &haveattr) != XftResultMatch) || haveattr != wantattr) {
         3480                         f->badweight = 1;
         3481                         fputs("st: font weight does not match\n", stderr);
         3482                 }
         3483         }
         3484 
         3485         XftTextExtentsUtf8(xw.dpy, f->match,
         3486                 (const FcChar8 *) ascii_printable,
         3487                 strlen(ascii_printable), &extents);
         3488 
         3489         f->set = NULL;
         3490         f->pattern = configured;
         3491 
         3492         f->ascent = f->match->ascent;
         3493         f->descent = f->match->descent;
         3494         f->lbearing = 0;
         3495         f->rbearing = f->match->max_advance_width;
         3496 
         3497         f->height = f->ascent + f->descent;
         3498         f->width = DIVCEIL(extents.xOff, strlen(ascii_printable));
         3499 
         3500         return 0;
         3501 }
         3502 
         3503 void
         3504 xloadfonts(char *fontstr, double fontsize)
         3505 {
         3506         FcPattern *pattern;
         3507         double fontval;
         3508         float ceilf(float);
         3509 
         3510         if (fontstr[0] == '-') {
         3511                 pattern = XftXlfdParse(fontstr, False, False);
         3512         } else {
         3513                 pattern = FcNameParse((FcChar8 *)fontstr);
         3514         }
         3515 
         3516         if (!pattern)
         3517                 die("st: can't open font %s\n", fontstr);
         3518 
         3519         if (fontsize > 1) {
         3520                 FcPatternDel(pattern, FC_PIXEL_SIZE);
         3521                 FcPatternDel(pattern, FC_SIZE);
         3522                 FcPatternAddDouble(pattern, FC_PIXEL_SIZE, (double)fontsize);
         3523                 usedfontsize = fontsize;
         3524         } else {
         3525                 if (FcPatternGetDouble(pattern, FC_PIXEL_SIZE, 0, &fontval) ==
         3526                                 FcResultMatch) {
         3527                         usedfontsize = fontval;
         3528                 } else if (FcPatternGetDouble(pattern, FC_SIZE, 0, &fontval) ==
         3529                                 FcResultMatch) {
         3530                         usedfontsize = -1;
         3531                 } else {
         3532                         /*
         3533                          * Default font size is 12, if none given. This is to
         3534                          * have a known usedfontsize value.
         3535                          */
         3536                         FcPatternAddDouble(pattern, FC_PIXEL_SIZE, 12);
         3537                         usedfontsize = 12;
         3538                 }
         3539                 defaultfontsize = usedfontsize;
         3540         }
         3541 
         3542         if (xloadfont(&dc.font, pattern))
         3543                 die("st: can't open font %s\n", fontstr);
         3544 
         3545         if (usedfontsize < 0) {
         3546                 FcPatternGetDouble(dc.font.match->pattern,
         3547                                    FC_PIXEL_SIZE, 0, &fontval);
         3548                 usedfontsize = fontval;
         3549                 if (fontsize == 0)
         3550                         defaultfontsize = fontval;
         3551         }
         3552 
         3553         /* Setting character width and height. */
         3554         xw.cw = ceilf(dc.font.width * cwscale);
         3555         xw.ch = ceilf(dc.font.height * chscale);
         3556 
         3557         FcPatternDel(pattern, FC_SLANT);
         3558         FcPatternAddInteger(pattern, FC_SLANT, FC_SLANT_ITALIC);
         3559         if (xloadfont(&dc.ifont, pattern))
         3560                 die("st: can't open font %s\n", fontstr);
         3561 
         3562         FcPatternDel(pattern, FC_WEIGHT);
         3563         FcPatternAddInteger(pattern, FC_WEIGHT, FC_WEIGHT_BOLD);
         3564         if (xloadfont(&dc.ibfont, pattern))
         3565                 die("st: can't open font %s\n", fontstr);
         3566 
         3567         FcPatternDel(pattern, FC_SLANT);
         3568         FcPatternAddInteger(pattern, FC_SLANT, FC_SLANT_ROMAN);
         3569         if (xloadfont(&dc.bfont, pattern))
         3570                 die("st: can't open font %s\n", fontstr);
         3571 
         3572         FcPatternDestroy(pattern);
         3573 }
         3574 
         3575 void
         3576 xunloadfont(Font *f)
         3577 {
         3578         XftFontClose(xw.dpy, f->match);
         3579         FcPatternDestroy(f->pattern);
         3580         if (f->set)
         3581                 FcFontSetDestroy(f->set);
         3582 }
         3583 
         3584 void
         3585 xunloadfonts(void)
         3586 {
         3587         /* Free the loaded fonts in the font cache.  */
         3588         while (frclen > 0)
         3589                 XftFontClose(xw.dpy, frc[--frclen].font);
         3590 
         3591         xunloadfont(&dc.font);
         3592         xunloadfont(&dc.bfont);
         3593         xunloadfont(&dc.ifont);
         3594         xunloadfont(&dc.ibfont);
         3595 }
         3596 
         3597 void
         3598 xzoom(const Arg *arg)
         3599 {
         3600         Arg larg;
         3601 
         3602         larg.f = usedfontsize + arg->f;
         3603         xzoomabs(&larg);
         3604 }
         3605 
         3606 void
         3607 xzoomabs(const Arg *arg)
         3608 {
         3609         xunloadfonts();
         3610         xloadfonts(usedfont, arg->f);
         3611         cresize(0, 0);
         3612         ttyresize();
         3613         redraw();
         3614         xhints();
         3615 }
         3616 
         3617 void
         3618 xzoomreset(const Arg *arg)
         3619 {
         3620         Arg larg;
         3621 
         3622         if (defaultfontsize > 0) {
         3623                 larg.f = defaultfontsize;
         3624                 xzoomabs(&larg);
         3625         }
         3626 }
         3627 
         3628 void
         3629 xinit(void)
         3630 {
         3631         XGCValues gcvalues;
         3632         Cursor cursor;
         3633         Window parent;
         3634         pid_t thispid = getpid();
         3635         XColor xmousefg, xmousebg;
         3636 
         3637         if (!(xw.dpy = XOpenDisplay(NULL)))
         3638                 die("Can't open display\n");
         3639         xw.scr = XDefaultScreen(xw.dpy);
         3640         xw.vis = XDefaultVisual(xw.dpy, xw.scr);
         3641 
         3642         /* font */
         3643         if (!FcInit())
         3644                 die("Could not init fontconfig.\n");
         3645 
         3646         usedfont = (opt_font == NULL)? font : opt_font;
         3647         xloadfonts(usedfont, 0);
         3648 
         3649         /* colors */
         3650         xw.cmap = XDefaultColormap(xw.dpy, xw.scr);
         3651         xloadcols();
         3652 
         3653         /* adjust fixed window geometry */
         3654         xw.w = 2 * borderpx + term.col * xw.cw;
         3655         xw.h = 2 * borderpx + term.row * xw.ch;
         3656         if (xw.gm & XNegative)
         3657                 xw.l += DisplayWidth(xw.dpy, xw.scr) - xw.w - 2;
         3658         if (xw.gm & YNegative)
         3659                 xw.t += DisplayHeight(xw.dpy, xw.scr) - xw.h - 2;
         3660 
         3661         /* Events */
         3662         xw.attrs.background_pixel = dc.col[defaultbg].pixel;
         3663         xw.attrs.border_pixel = dc.col[defaultbg].pixel;
         3664         xw.attrs.bit_gravity = NorthWestGravity;
         3665         xw.attrs.event_mask = FocusChangeMask | KeyPressMask
         3666                 | ExposureMask | VisibilityChangeMask | StructureNotifyMask
         3667                 | ButtonMotionMask | ButtonPressMask | ButtonReleaseMask;
         3668         xw.attrs.colormap = xw.cmap;
         3669 
         3670         if (!(opt_embed && (parent = strtol(opt_embed, NULL, 0))))
         3671                 parent = XRootWindow(xw.dpy, xw.scr);
         3672         xw.win = XCreateWindow(xw.dpy, parent, xw.l, xw.t,
         3673                         xw.w, xw.h, 0, XDefaultDepth(xw.dpy, xw.scr), InputOutput,
         3674                         xw.vis, CWBackPixel | CWBorderPixel | CWBitGravity
         3675                         | CWEventMask | CWColormap, &xw.attrs);
         3676 
         3677         memset(&gcvalues, 0, sizeof(gcvalues));
         3678         gcvalues.graphics_exposures = False;
         3679         dc.gc = XCreateGC(xw.dpy, parent, GCGraphicsExposures,
         3680                         &gcvalues);
         3681         xw.buf = XCreatePixmap(xw.dpy, xw.win, xw.w, xw.h,
         3682                         DefaultDepth(xw.dpy, xw.scr));
         3683         XSetForeground(xw.dpy, dc.gc, dc.col[defaultbg].pixel);
         3684         XFillRectangle(xw.dpy, xw.buf, dc.gc, 0, 0, xw.w, xw.h);
         3685 
         3686         /* Xft rendering context */
         3687         xw.draw = XftDrawCreate(xw.dpy, xw.buf, xw.vis, xw.cmap);
         3688 
         3689         /* input methods */
         3690         if ((xw.xim = XOpenIM(xw.dpy, NULL, NULL, NULL)) == NULL) {
         3691                 XSetLocaleModifiers("@im=local");
         3692                 if ((xw.xim =  XOpenIM(xw.dpy, NULL, NULL, NULL)) == NULL) {
         3693                         XSetLocaleModifiers("@im=");
         3694                         if ((xw.xim = XOpenIM(xw.dpy,
         3695                                         NULL, NULL, NULL)) == NULL) {
         3696                                 die("XOpenIM failed. Could not open input"
         3697                                         " device.\n");
         3698                         }
         3699                 }
         3700         }
         3701         xw.xic = XCreateIC(xw.xim, XNInputStyle, XIMPreeditNothing
         3702                                            | XIMStatusNothing, XNClientWindow, xw.win,
         3703                                            XNFocusWindow, xw.win, NULL);
         3704         if (xw.xic == NULL)
         3705                 die("XCreateIC failed. Could not obtain input method.\n");
         3706 
         3707         /* white cursor, black outline */
         3708         cursor = XCreateFontCursor(xw.dpy, mouseshape);
         3709         XDefineCursor(xw.dpy, xw.win, cursor);
         3710 
         3711         if (XParseColor(xw.dpy, xw.cmap, colorname[mousefg], &xmousefg) == 0) {
         3712                 xmousefg.red   = 0xffff;
         3713                 xmousefg.green = 0xffff;
         3714                 xmousefg.blue  = 0xffff;
         3715         }
         3716 
         3717         if (XParseColor(xw.dpy, xw.cmap, colorname[mousebg], &xmousebg) == 0) {
         3718                 xmousebg.red   = 0x0000;
         3719                 xmousebg.green = 0x0000;
         3720                 xmousebg.blue  = 0x0000;
         3721         }
         3722 
         3723         XRecolorCursor(xw.dpy, cursor, &xmousefg, &xmousebg);
         3724 
         3725         xw.xembed = XInternAtom(xw.dpy, "_XEMBED", False);
         3726         xw.wmdeletewin = XInternAtom(xw.dpy, "WM_DELETE_WINDOW", False);
         3727         xw.netwmname = XInternAtom(xw.dpy, "_NET_WM_NAME", False);
         3728         XSetWMProtocols(xw.dpy, xw.win, &xw.wmdeletewin, 1);
         3729 
         3730         xw.netwmpid = XInternAtom(xw.dpy, "_NET_WM_PID", False);
         3731         XChangeProperty(xw.dpy, xw.win, xw.netwmpid, XA_CARDINAL, 32,
         3732                         PropModeReplace, (uchar *)&thispid, 1);
         3733 
         3734         xresettitle();
         3735         XMapWindow(xw.dpy, xw.win);
         3736         xhints();
         3737         XSync(xw.dpy, False);
         3738 }
         3739 
         3740 int
         3741 xmakeglyphfontspecs(XftGlyphFontSpec *specs, const Glyph *glyphs, int len, int x, int y)
         3742 {
         3743         float winx = borderpx + x * xw.cw, winy = borderpx + y * xw.ch, xp, yp;
         3744         ushort mode, prevmode = USHRT_MAX;
         3745         Font *font = &dc.font;
         3746         int frcflags = FRC_NORMAL;
         3747         float runewidth = xw.cw;
         3748         Rune rune;
         3749         FT_UInt glyphidx;
         3750         FcResult fcres;
         3751         FcPattern *fcpattern, *fontpattern;
         3752         FcFontSet *fcsets[] = { NULL };
         3753         FcCharSet *fccharset;
         3754         int i, f, numspecs = 0;
         3755 
         3756         for (i = 0, xp = winx, yp = winy + font->ascent; i < len; ++i) {
         3757                 /* Fetch rune and mode for current glyph. */
         3758                 rune = glyphs[i].u;
         3759                 mode = glyphs[i].mode;
         3760 
         3761                 /* Skip dummy wide-character spacing. */
         3762                 if (mode == ATTR_WDUMMY)
         3763                         continue;
         3764 
         3765                 /* Determine font for glyph if different from previous glyph. */
         3766                 if (prevmode != mode) {
         3767                         prevmode = mode;
         3768                         font = &dc.font;
         3769                         frcflags = FRC_NORMAL;
         3770                         runewidth = xw.cw * ((mode & ATTR_WIDE) ? 2.0f : 1.0f);
         3771                         if ((mode & ATTR_ITALIC) && (mode & ATTR_BOLD)) {
         3772                                 font = &dc.ibfont;
         3773                                 frcflags = FRC_ITALICBOLD;
         3774                         } else if (mode & ATTR_ITALIC) {
         3775                                 font = &dc.ifont;
         3776                                 frcflags = FRC_ITALIC;
         3777                         } else if (mode & ATTR_BOLD) {
         3778                                 font = &dc.bfont;
         3779                                 frcflags = FRC_BOLD;
         3780                         }
         3781                         yp = winy + font->ascent;
         3782                 }
         3783 
         3784                 /* Lookup character index with default font. */
         3785                 glyphidx = XftCharIndex(xw.dpy, font->match, rune);
         3786                 if (glyphidx) {
         3787                         specs[numspecs].font = font->match;
         3788                         specs[numspecs].glyph = glyphidx;
         3789                         specs[numspecs].x = (short)xp;
         3790                         specs[numspecs].y = (short)yp;
         3791                         xp += runewidth;
         3792                         numspecs++;
         3793                         continue;
         3794                 }
         3795 
         3796                 /* Fallback on font cache, search the font cache for match. */
         3797                 for (f = 0; f < frclen; f++) {
         3798                         glyphidx = XftCharIndex(xw.dpy, frc[f].font, rune);
         3799                         /* Everything correct. */
         3800                         if (glyphidx && frc[f].flags == frcflags)
         3801                                 break;
         3802                         /* We got a default font for a not found glyph. */
         3803                         if (!glyphidx && frc[f].flags == frcflags
         3804                                         && frc[f].unicodep == rune) {
         3805                                 break;
         3806                         }
         3807                 }
         3808 
         3809                 /* Nothing was found. Use fontconfig to find matching font. */
         3810                 if (f >= frclen) {
         3811                         if (!font->set)
         3812                                 font->set = FcFontSort(0, font->pattern,
         3813                                                        1, 0, &fcres);
         3814                         fcsets[0] = font->set;
         3815 
         3816                         /*
         3817                          * Nothing was found in the cache. Now use
         3818                          * some dozen of Fontconfig calls to get the
         3819                          * font for one single character.
         3820                          *
         3821                          * Xft and fontconfig are design failures.
         3822                          */
         3823                         fcpattern = FcPatternDuplicate(font->pattern);
         3824                         fccharset = FcCharSetCreate();
         3825 
         3826                         FcCharSetAddChar(fccharset, rune);
         3827                         FcPatternAddCharSet(fcpattern, FC_CHARSET,
         3828                                         fccharset);
         3829                         FcPatternAddBool(fcpattern, FC_SCALABLE, 1);
         3830 
         3831                         FcConfigSubstitute(0, fcpattern,
         3832                                         FcMatchPattern);
         3833                         FcDefaultSubstitute(fcpattern);
         3834 
         3835                         fontpattern = FcFontSetMatch(0, fcsets, 1,
         3836                                         fcpattern, &fcres);
         3837 
         3838                         /*
         3839                          * Overwrite or create the new cache entry.
         3840                          */
         3841                         if (frclen >= LEN(frc)) {
         3842                                 frclen = LEN(frc) - 1;
         3843                                 XftFontClose(xw.dpy, frc[frclen].font);
         3844                                 frc[frclen].unicodep = 0;
         3845                         }
         3846 
         3847                         frc[frclen].font = XftFontOpenPattern(xw.dpy,
         3848                                         fontpattern);
         3849                         frc[frclen].flags = frcflags;
         3850                         frc[frclen].unicodep = rune;
         3851 
         3852                         glyphidx = XftCharIndex(xw.dpy, frc[frclen].font, rune);
         3853 
         3854                         f = frclen;
         3855                         frclen++;
         3856 
         3857                         FcPatternDestroy(fcpattern);
         3858                         FcCharSetDestroy(fccharset);
         3859                 }
         3860 
         3861                 specs[numspecs].font = frc[f].font;
         3862                 specs[numspecs].glyph = glyphidx;
         3863                 specs[numspecs].x = (short)xp;
         3864                 specs[numspecs].y = (short)yp;
         3865                 xp += runewidth;
         3866                 numspecs++;
         3867         }
         3868 
         3869         return numspecs;
         3870 }
         3871 
         3872 void
         3873 xdrawglyphfontspecs(const XftGlyphFontSpec *specs, Glyph base, int len, int x, int y)
         3874 {
         3875         int charlen = len * ((base.mode & ATTR_WIDE) ? 2 : 1);
         3876         int winx = borderpx + x * xw.cw, winy = borderpx + y * xw.ch,
         3877             width = charlen * xw.cw;
         3878         Color *fg, *bg, *temp, revfg, revbg, truefg, truebg;
         3879         XRenderColor colfg, colbg;
         3880         XRectangle r;
         3881 
         3882         /* Fallback on color display for attributes not supported by the font */
         3883         if (base.mode & ATTR_ITALIC && base.mode & ATTR_BOLD) {
         3884                 if (dc.ibfont.badslant || dc.ibfont.badweight)
         3885                         base.fg = defaultattr;
         3886         } else if ((base.mode & ATTR_ITALIC && dc.ifont.badslant) ||
         3887             (base.mode & ATTR_BOLD && dc.bfont.badweight)) {
         3888                 base.fg = defaultattr;
         3889         }
         3890 
         3891         if (IS_TRUECOL(base.fg)) {
         3892                 colfg.alpha = 0xffff;
         3893                 colfg.red = TRUERED(base.fg);
         3894                 colfg.green = TRUEGREEN(base.fg);
         3895                 colfg.blue = TRUEBLUE(base.fg);
         3896                 XftColorAllocValue(xw.dpy, xw.vis, xw.cmap, &colfg, &truefg);
         3897                 fg = &truefg;
         3898         } else {
         3899                 fg = &dc.col[base.fg];
         3900         }
         3901 
         3902         if (IS_TRUECOL(base.bg)) {
         3903                 colbg.alpha = 0xffff;
         3904                 colbg.green = TRUEGREEN(base.bg);
         3905                 colbg.red = TRUERED(base.bg);
         3906                 colbg.blue = TRUEBLUE(base.bg);
         3907                 XftColorAllocValue(xw.dpy, xw.vis, xw.cmap, &colbg, &truebg);
         3908                 bg = &truebg;
         3909         } else {
         3910                 bg = &dc.col[base.bg];
         3911         }
         3912 
         3913         /* Change basic system colors [0-7] to bright system colors [8-15] */
         3914         if ((base.mode & ATTR_BOLD_FAINT) == ATTR_BOLD && BETWEEN(base.fg, 0, 7))
         3915                 fg = &dc.col[base.fg + 8];
         3916 
         3917         if (IS_SET(MODE_REVERSE)) {
         3918                 if (fg == &dc.col[defaultfg]) {
         3919                         fg = &dc.col[defaultbg];
         3920                 } else {
         3921                         colfg.red = ~fg->color.red;
         3922                         colfg.green = ~fg->color.green;
         3923                         colfg.blue = ~fg->color.blue;
         3924                         colfg.alpha = fg->color.alpha;
         3925                         XftColorAllocValue(xw.dpy, xw.vis, xw.cmap, &colfg,
         3926                                         &revfg);
         3927                         fg = &revfg;
         3928                 }
         3929 
         3930                 if (bg == &dc.col[defaultbg]) {
         3931                         bg = &dc.col[defaultfg];
         3932                 } else {
         3933                         colbg.red = ~bg->color.red;
         3934                         colbg.green = ~bg->color.green;
         3935                         colbg.blue = ~bg->color.blue;
         3936                         colbg.alpha = bg->color.alpha;
         3937                         XftColorAllocValue(xw.dpy, xw.vis, xw.cmap, &colbg,
         3938                                         &revbg);
         3939                         bg = &revbg;
         3940                 }
         3941         }
         3942 
         3943         if (base.mode & ATTR_REVERSE) {
         3944                 temp = fg;
         3945                 fg = bg;
         3946                 bg = temp;
         3947         }
         3948 
         3949         if ((base.mode & ATTR_BOLD_FAINT) == ATTR_FAINT) {
         3950                 colfg.red = fg->color.red / 2;
         3951                 colfg.green = fg->color.green / 2;
         3952                 colfg.blue = fg->color.blue / 2;
         3953                 XftColorAllocValue(xw.dpy, xw.vis, xw.cmap, &colfg, &revfg);
         3954                 fg = &revfg;
         3955         }
         3956 
         3957         if (base.mode & ATTR_BLINK && term.mode & MODE_BLINK)
         3958                 fg = bg;
         3959 
         3960         if (base.mode & ATTR_INVISIBLE)
         3961                 fg = bg;
         3962 
         3963         /* Intelligent cleaning up of the borders. */
         3964         if (x == 0) {
         3965                 xclear(0, (y == 0)? 0 : winy, borderpx,
         3966                         winy + xw.ch + ((y >= term.row-1)? xw.h : 0));
         3967         }
         3968         if (x + charlen >= term.col) {
         3969                 xclear(winx + width, (y == 0)? 0 : winy, xw.w,
         3970                         ((y >= term.row-1)? xw.h : (winy + xw.ch)));
         3971         }
         3972         if (y == 0)
         3973                 xclear(winx, 0, winx + width, borderpx);
         3974         if (y == term.row-1)
         3975                 xclear(winx, winy + xw.ch, winx + width, xw.h);
         3976 
         3977         /* Clean up the region we want to draw to. */
         3978         XftDrawRect(xw.draw, bg, winx, winy, width, xw.ch);
         3979 
         3980         /* Set the clip region because Xft is sometimes dirty. */
         3981         r.x = 0;
         3982         r.y = 0;
         3983         r.height = xw.ch;
         3984         r.width = width;
         3985         XftDrawSetClipRectangles(xw.draw, winx, winy, &r, 1);
         3986 
         3987         /* Render the glyphs. */
         3988         XftDrawGlyphFontSpec(xw.draw, fg, specs, len);
         3989 
         3990         /* Render underline and strikethrough. */
         3991         if (base.mode & ATTR_UNDERLINE) {
         3992                 XftDrawRect(xw.draw, fg, winx, winy + dc.font.ascent + 1,
         3993                                 width, 1);
         3994         }
         3995 
         3996         if (base.mode & ATTR_STRUCK) {
         3997                 XftDrawRect(xw.draw, fg, winx, winy + 2 * dc.font.ascent / 3,
         3998                                 width, 1);
         3999         }
         4000 
         4001         /* Reset clip to none. */
         4002         XftDrawSetClip(xw.draw, 0);
         4003 }
         4004 
         4005 void
         4006 xdrawglyph(Glyph g, int x, int y)
         4007 {
         4008         int numspecs;
         4009         XftGlyphFontSpec spec;
         4010 
         4011         numspecs = xmakeglyphfontspecs(&spec, &g, 1, x, y);
         4012         xdrawglyphfontspecs(&spec, g, numspecs, x, y);
         4013 }
         4014 
         4015 void
         4016 xdrawcursor(void)
         4017 {
         4018         static int oldx = 0, oldy = 0;
         4019         int curx;
         4020         Glyph g = {' ', ATTR_NULL, defaultbg, defaultcs}, og;
         4021         int ena_sel = sel.ob.x != -1 && sel.alt == IS_SET(MODE_ALTSCREEN);
         4022         Color drawcol;
         4023 
         4024         LIMIT(oldx, 0, term.col-1);
         4025         LIMIT(oldy, 0, term.row-1);
         4026 
         4027         curx = term.c.x;
         4028 
         4029         /* adjust position if in dummy */
         4030         if (term.line[oldy][oldx].mode & ATTR_WDUMMY)
         4031                 oldx--;
         4032         if (term.line[term.c.y][curx].mode & ATTR_WDUMMY)
         4033                 curx--;
         4034 
         4035         /* remove the old cursor */
         4036         og = term.line[oldy][oldx];
         4037         if (ena_sel && selected(oldx, oldy))
         4038                 og.mode ^= ATTR_REVERSE;
         4039         xdrawglyph(og, oldx, oldy);
         4040 
         4041         g.u = term.line[term.c.y][term.c.x].u;
         4042 
         4043         /*
         4044          * Select the right color for the right mode.
         4045          */
         4046         if (IS_SET(MODE_REVERSE)) {
         4047                 g.mode |= ATTR_REVERSE;
         4048                 g.bg = defaultfg;
         4049                 if (ena_sel && selected(term.c.x, term.c.y)) {
         4050                         drawcol = dc.col[defaultcs];
         4051                         g.fg = defaultrcs;
         4052                 } else {
         4053                         drawcol = dc.col[defaultrcs];
         4054                         g.fg = defaultcs;
         4055                 }
         4056         } else {
         4057                 if (ena_sel && selected(term.c.x, term.c.y)) {
         4058                         drawcol = dc.col[defaultrcs];
         4059                         g.fg = defaultfg;
         4060                         g.bg = defaultrcs;
         4061                 } else {
         4062                         drawcol = dc.col[defaultcs];
         4063                 }
         4064         }
         4065 
         4066         if (IS_SET(MODE_HIDE))
         4067                 return;
         4068 
         4069         /* draw the new one */
         4070         if (xw.state & WIN_FOCUSED) {
         4071                 switch (xw.cursor) {
         4072                 case 7: /* st extension: snowman */
         4073                         utf8decode("☃", &g.u, UTF_SIZ);
         4074                 case 0: /* Blinking Block */
         4075                 case 1: /* Blinking Block (Default) */
         4076                 case 2: /* Steady Block */
         4077                         g.mode |= term.line[term.c.y][curx].mode & ATTR_WIDE;
         4078                         xdrawglyph(g, term.c.x, term.c.y);
         4079                         break;
         4080                 case 3: /* Blinking Underline */
         4081                 case 4: /* Steady Underline */
         4082                         XftDrawRect(xw.draw, &drawcol,
         4083                                         borderpx + curx * xw.cw,
         4084                                         borderpx + (term.c.y + 1) * xw.ch - \
         4085                                                 cursorthickness,
         4086                                         xw.cw, cursorthickness);
         4087                         break;
         4088                 case 5: /* Blinking bar */
         4089                 case 6: /* Steady bar */
         4090                         XftDrawRect(xw.draw, &drawcol,
         4091                                         borderpx + curx * xw.cw,
         4092                                         borderpx + term.c.y * xw.ch,
         4093                                         cursorthickness, xw.ch);
         4094                         break;
         4095                 }
         4096         } else {
         4097                 XftDrawRect(xw.draw, &drawcol,
         4098                                 borderpx + curx * xw.cw,
         4099                                 borderpx + term.c.y * xw.ch,
         4100                                 xw.cw - 1, 1);
         4101                 XftDrawRect(xw.draw, &drawcol,
         4102                                 borderpx + curx * xw.cw,
         4103                                 borderpx + term.c.y * xw.ch,
         4104                                 1, xw.ch - 1);
         4105                 XftDrawRect(xw.draw, &drawcol,
         4106                                 borderpx + (curx + 1) * xw.cw - 1,
         4107                                 borderpx + term.c.y * xw.ch,
         4108                                 1, xw.ch - 1);
         4109                 XftDrawRect(xw.draw, &drawcol,
         4110                                 borderpx + curx * xw.cw,
         4111                                 borderpx + (term.c.y + 1) * xw.ch - 1,
         4112                                 xw.cw, 1);
         4113         }
         4114         oldx = curx, oldy = term.c.y;
         4115 }
         4116 
         4117 
         4118 void
         4119 xsettitle(char *p)
         4120 {
         4121         XTextProperty prop;
         4122 
         4123         Xutf8TextListToTextProperty(xw.dpy, &p, 1, XUTF8StringStyle,
         4124                         &prop);
         4125         XSetWMName(xw.dpy, xw.win, &prop);
         4126         XSetTextProperty(xw.dpy, xw.win, &prop, xw.netwmname);
         4127         XFree(prop.value);
         4128 }
         4129 
         4130 void
         4131 xresettitle(void)
         4132 {
         4133         xsettitle(opt_title ? opt_title : "st");
         4134 }
         4135 
         4136 void
         4137 redraw(void)
         4138 {
         4139         tfulldirt();
         4140         draw();
         4141 }
         4142 
         4143 void
         4144 draw(void)
         4145 {
         4146         drawregion(0, 0, term.col, term.row);
         4147         XCopyArea(xw.dpy, xw.buf, xw.win, dc.gc, 0, 0, xw.w,
         4148                         xw.h, 0, 0);
         4149         XSetForeground(xw.dpy, dc.gc,
         4150                         dc.col[IS_SET(MODE_REVERSE)?
         4151                                 defaultfg : defaultbg].pixel);
         4152 }
         4153 
         4154 void
         4155 drawregion(int x1, int y1, int x2, int y2)
         4156 {
         4157         int i, x, y, ox, numspecs;
         4158         Glyph base, new;
         4159         XftGlyphFontSpec *specs;
         4160         int ena_sel = sel.ob.x != -1 && sel.alt == IS_SET(MODE_ALTSCREEN);
         4161 
         4162         if (!(xw.state & WIN_VISIBLE))
         4163                 return;
         4164 
         4165         for (y = y1; y < y2; y++) {
         4166                 if (!term.dirty[y])
         4167                         continue;
         4168 
         4169                 term.dirty[y] = 0;
         4170 
         4171                 specs = term.specbuf;
         4172                 numspecs = xmakeglyphfontspecs(specs, &term.line[y][x1], x2 - x1, x1, y);
         4173 
         4174                 i = ox = 0;
         4175                 for (x = x1; x < x2 && i < numspecs; x++) {
         4176                         new = term.line[y][x];
         4177                         if (new.mode == ATTR_WDUMMY)
         4178                                 continue;
         4179                         if (ena_sel && selected(x, y))
         4180                                 new.mode ^= ATTR_REVERSE;
         4181                         if (i > 0 && ATTRCMP(base, new)) {
         4182                                 xdrawglyphfontspecs(specs, base, i, ox, y);
         4183                                 specs += i;
         4184                                 numspecs -= i;
         4185                                 i = 0;
         4186                         }
         4187                         if (i == 0) {
         4188                                 ox = x;
         4189                                 base = new;
         4190                         }
         4191                         i++;
         4192                 }
         4193                 if (i > 0)
         4194                         xdrawglyphfontspecs(specs, base, i, ox, y);
         4195         }
         4196         xdrawcursor();
         4197 }
         4198 
         4199 void
         4200 expose(XEvent *ev)
         4201 {
         4202         redraw();
         4203 }
         4204 
         4205 void
         4206 visibility(XEvent *ev)
         4207 {
         4208         XVisibilityEvent *e = &ev->xvisibility;
         4209 
         4210         MODBIT(xw.state, e->state != VisibilityFullyObscured, WIN_VISIBLE);
         4211 }
         4212 
         4213 void
         4214 unmap(XEvent *ev)
         4215 {
         4216         xw.state &= ~WIN_VISIBLE;
         4217 }
         4218 
         4219 void
         4220 xsetpointermotion(int set)
         4221 {
         4222         MODBIT(xw.attrs.event_mask, set, PointerMotionMask);
         4223         XChangeWindowAttributes(xw.dpy, xw.win, CWEventMask, &xw.attrs);
         4224 }
         4225 
         4226 void
         4227 xseturgency(int add)
         4228 {
         4229         XWMHints *h = XGetWMHints(xw.dpy, xw.win);
         4230 
         4231         MODBIT(h->flags, add, XUrgencyHint);
         4232         XSetWMHints(xw.dpy, xw.win, h);
         4233         XFree(h);
         4234 }
         4235 
         4236 void
         4237 focus(XEvent *ev)
         4238 {
         4239         XFocusChangeEvent *e = &ev->xfocus;
         4240 
         4241         if (e->mode == NotifyGrab)
         4242                 return;
         4243 
         4244         if (ev->type == FocusIn) {
         4245                 XSetICFocus(xw.xic);
         4246                 xw.state |= WIN_FOCUSED;
         4247                 xseturgency(0);
         4248                 if (IS_SET(MODE_FOCUS))
         4249                         ttywrite("\033[I", 3);
         4250         } else {
         4251                 XUnsetICFocus(xw.xic);
         4252                 xw.state &= ~WIN_FOCUSED;
         4253                 if (IS_SET(MODE_FOCUS))
         4254                         ttywrite("\033[O", 3);
         4255         }
         4256 }
         4257 
         4258 int
         4259 match(uint mask, uint state)
         4260 {
         4261         return mask == XK_ANY_MOD || mask == (state & ~ignoremod);
         4262 }
         4263 
         4264 void
         4265 numlock(const Arg *dummy)
         4266 {
         4267         term.numlock ^= 1;
         4268 }
         4269 
         4270 char*
         4271 kmap(KeySym k, uint state)
         4272 {
         4273         Key *kp;
         4274         int i;
         4275 
         4276         /* Check for mapped keys out of X11 function keys. */
         4277         for (i = 0; i < LEN(mappedkeys); i++) {
         4278                 if (mappedkeys[i] == k)
         4279                         break;
         4280         }
         4281         if (i == LEN(mappedkeys)) {
         4282                 if ((k & 0xFFFF) < 0xFD00)
         4283                         return NULL;
         4284         }
         4285 
         4286         for (kp = key; kp < key + LEN(key); kp++) {
         4287                 if (kp->k != k)
         4288                         continue;
         4289 
         4290                 if (!match(kp->mask, state))
         4291                         continue;
         4292 
         4293                 if (IS_SET(MODE_APPKEYPAD) ? kp->appkey < 0 : kp->appkey > 0)
         4294                         continue;
         4295                 if (term.numlock && kp->appkey == 2)
         4296                         continue;
         4297 
         4298                 if (IS_SET(MODE_APPCURSOR) ? kp->appcursor < 0 : kp->appcursor > 0)
         4299                         continue;
         4300 
         4301                 if (IS_SET(MODE_CRLF) ? kp->crlf < 0 : kp->crlf > 0)
         4302                         continue;
         4303 
         4304                 return kp->s;
         4305         }
         4306 
         4307         return NULL;
         4308 }
         4309 
         4310 void
         4311 kpress(XEvent *ev)
         4312 {
         4313         XKeyEvent *e = &ev->xkey;
         4314         KeySym ksym;
         4315         char buf[32], *customkey;
         4316         int len;
         4317         Rune c;
         4318         Status status;
         4319         Shortcut *bp;
         4320 
         4321         if (IS_SET(MODE_KBDLOCK))
         4322                 return;
         4323 
         4324         len = XmbLookupString(xw.xic, e, buf, sizeof buf, &ksym, &status);
         4325         /* 1. shortcuts */
         4326         for (bp = shortcuts; bp < shortcuts + LEN(shortcuts); bp++) {
         4327                 if (ksym == bp->keysym && match(bp->mod, e->state)) {
         4328                         bp->func(&(bp->arg));
         4329                         return;
         4330                 }
         4331         }
         4332 
         4333         /* 2. custom keys from config.h */
         4334         if ((customkey = kmap(ksym, e->state))) {
         4335                 ttysend(customkey, strlen(customkey));
         4336                 return;
         4337         }
         4338 
         4339         /* 3. composed string from input method */
         4340         if (len == 0)
         4341                 return;
         4342         if (len == 1 && e->state & Mod1Mask) {
         4343                 if (IS_SET(MODE_8BIT)) {
         4344                         if (*buf < 0177) {
         4345                                 c = *buf | 0x80;
         4346                                 len = utf8encode(c, buf);
         4347                         }
         4348                 } else {
         4349                         buf[1] = buf[0];
         4350                         buf[0] = '\033';
         4351                         len = 2;
         4352                 }
         4353         }
         4354         ttysend(buf, len);
         4355 }
         4356 
         4357 
         4358 void
         4359 cmessage(XEvent *e)
         4360 {
         4361         /*
         4362          * See xembed specs
         4363          *  http://standards.freedesktop.org/xembed-spec/xembed-spec-latest.html
         4364          */
         4365         if (e->xclient.message_type == xw.xembed && e->xclient.format == 32) {
         4366                 if (e->xclient.data.l[1] == XEMBED_FOCUS_IN) {
         4367                         xw.state |= WIN_FOCUSED;
         4368                         xseturgency(0);
         4369                 } else if (e->xclient.data.l[1] == XEMBED_FOCUS_OUT) {
         4370                         xw.state &= ~WIN_FOCUSED;
         4371                 }
         4372         } else if (e->xclient.data.l[0] == xw.wmdeletewin) {
         4373                 /* Send SIGHUP to shell */
         4374                 kill(pid, SIGHUP);
         4375                 exit(0);
         4376         }
         4377 }
         4378 
         4379 void
         4380 cresize(int width, int height)
         4381 {
         4382         int col, row;
         4383 
         4384         if (width != 0)
         4385                 xw.w = width;
         4386         if (height != 0)
         4387                 xw.h = height;
         4388 
         4389         col = (xw.w - 2 * borderpx) / xw.cw;
         4390         row = (xw.h - 2 * borderpx) / xw.ch;
         4391 
         4392         tresize(col, row);
         4393         xresize(col, row);
         4394 }
         4395 
         4396 void
         4397 resize(XEvent *e)
         4398 {
         4399         if (e->xconfigure.width == xw.w && e->xconfigure.height == xw.h)
         4400                 return;
         4401 
         4402         cresize(e->xconfigure.width, e->xconfigure.height);
         4403         ttyresize();
         4404 }
         4405 
         4406 void
         4407 run(void)
         4408 {
         4409         XEvent ev;
         4410         int w = xw.w, h = xw.h;
         4411         fd_set rfd;
         4412         int xfd = XConnectionNumber(xw.dpy), xev, blinkset = 0, dodraw = 0;
         4413         struct timespec drawtimeout, *tv = NULL, now, last, lastblink;
         4414         long deltatime;
         4415 
         4416         /* Waiting for window mapping */
         4417         do {
         4418                 XNextEvent(xw.dpy, &ev);
         4419                 /*
         4420                  * This XFilterEvent call is required because of XOpenIM. It
         4421                  * does filter out the key event and some client message for
         4422                  * the input method too.
         4423                  */
         4424                 if (XFilterEvent(&ev, None))
         4425                         continue;
         4426                 if (ev.type == ConfigureNotify) {
         4427                         w = ev.xconfigure.width;
         4428                         h = ev.xconfigure.height;
         4429                 }
         4430         } while (ev.type != MapNotify);
         4431 
         4432         cresize(w, h);
         4433         ttynew();
         4434         ttyresize();
         4435 
         4436         clock_gettime(CLOCK_MONOTONIC, &last);
         4437         lastblink = last;
         4438 
         4439         for (xev = actionfps;;) {
         4440                 FD_ZERO(&rfd);
         4441                 FD_SET(cmdfd, &rfd);
         4442                 FD_SET(xfd, &rfd);
         4443 
         4444                 if (pselect(MAX(xfd, cmdfd)+1, &rfd, NULL, NULL, tv, NULL) < 0) {
         4445                         if (errno == EINTR)
         4446                                 continue;
         4447                         die("select failed: %s\n", strerror(errno));
         4448                 }
         4449                 if (FD_ISSET(cmdfd, &rfd)) {
         4450                         ttyread();
         4451                         if (blinktimeout) {
         4452                                 blinkset = tattrset(ATTR_BLINK);
         4453                                 if (!blinkset)
         4454                                         MODBIT(term.mode, 0, MODE_BLINK);
         4455                         }
         4456                 }
         4457 
         4458                 if (FD_ISSET(xfd, &rfd))
         4459                         xev = actionfps;
         4460 
         4461                 clock_gettime(CLOCK_MONOTONIC, &now);
         4462                 drawtimeout.tv_sec = 0;
         4463                 drawtimeout.tv_nsec =  (1000 * 1E6)/ xfps;
         4464                 tv = &drawtimeout;
         4465 
         4466                 dodraw = 0;
         4467                 if (blinktimeout && TIMEDIFF(now, lastblink) > blinktimeout) {
         4468                         tsetdirtattr(ATTR_BLINK);
         4469                         term.mode ^= MODE_BLINK;
         4470                         lastblink = now;
         4471                         dodraw = 1;
         4472                 }
         4473                 deltatime = TIMEDIFF(now, last);
         4474                 if (deltatime > 1000 / (xev ? xfps : actionfps)) {
         4475                         dodraw = 1;
         4476                         last = now;
         4477                 }
         4478 
         4479                 if (dodraw) {
         4480                         while (XPending(xw.dpy)) {
         4481                                 XNextEvent(xw.dpy, &ev);
         4482                                 if (XFilterEvent(&ev, None))
         4483                                         continue;
         4484                                 if (handler[ev.type])
         4485                                         (handler[ev.type])(&ev);
         4486                         }
         4487 
         4488                         draw();
         4489                         XFlush(xw.dpy);
         4490 
         4491                         if (xev && !FD_ISSET(xfd, &rfd))
         4492                                 xev--;
         4493                         if (!FD_ISSET(cmdfd, &rfd) && !FD_ISSET(xfd, &rfd)) {
         4494                                 if (blinkset) {
         4495                                         if (TIMEDIFF(now, lastblink) \
         4496                                                         > blinktimeout) {
         4497                                                 drawtimeout.tv_nsec = 1000;
         4498                                         } else {
         4499                                                 drawtimeout.tv_nsec = (1E6 * \
         4500                                                         (blinktimeout - \
         4501                                                         TIMEDIFF(now,
         4502                                                                 lastblink)));
         4503                                         }
         4504                                         drawtimeout.tv_sec = \
         4505                                             drawtimeout.tv_nsec / 1E9;
         4506                                         drawtimeout.tv_nsec %= (long)1E9;
         4507                                 } else {
         4508                                         tv = NULL;
         4509                                 }
         4510                         }
         4511                 }
         4512         }
         4513 }
         4514 
         4515 void
         4516 usage(void)
         4517 {
         4518         die("usage: %s [-aiv] [-c class] [-f font] [-g geometry]"
         4519             " [-n name] [-o file]\n"
         4520             "          [-T title] [-t title] [-w windowid]"
         4521             " [[-e] command [args ...]]\n"
         4522             "       %s [-aiv] [-c class] [-f font] [-g geometry]"
         4523             " [-n name] [-o file]\n"
         4524             "          [-T title] [-t title] [-w windowid] -l line"
         4525             " [stty_args ...]\n", argv0, argv0);
         4526 }
         4527 
         4528 int
         4529 main(int argc, char *argv[])
         4530 {
         4531         xw.l = xw.t = 0;
         4532         xw.isfixed = False;
         4533         xw.cursor = cursorshape;
         4534 
         4535         ARGBEGIN {
         4536         case 'a':
         4537                 allowaltscreen = 0;
         4538                 break;
         4539         case 'c':
         4540                 opt_class = EARGF(usage());
         4541                 break;
         4542         case 'e':
         4543                 if (argc > 0)
         4544                         --argc, ++argv;
         4545                 goto run;
         4546         case 'f':
         4547                 opt_font = EARGF(usage());
         4548                 break;
         4549         case 'g':
         4550                 xw.gm = XParseGeometry(EARGF(usage()),
         4551                                 &xw.l, &xw.t, &cols, &rows);
         4552                 break;
         4553         case 'i':
         4554                 xw.isfixed = 1;
         4555                 break;
         4556         case 'o':
         4557                 opt_io = EARGF(usage());
         4558                 break;
         4559         case 'l':
         4560                 opt_line = EARGF(usage());
         4561                 break;
         4562         case 'n':
         4563                 opt_name = EARGF(usage());
         4564                 break;
         4565         case 't':
         4566         case 'T':
         4567                 opt_title = EARGF(usage());
         4568                 break;
         4569         case 'w':
         4570                 opt_embed = EARGF(usage());
         4571                 break;
         4572         case 'v':
         4573                 die("%s " VERSION " (c) 2010-2016 st engineers\n", argv0);
         4574                 break;
         4575         default:
         4576                 usage();
         4577         } ARGEND;
         4578 
         4579 run:
         4580         if (argc > 0) {
         4581                 /* eat all remaining arguments */
         4582                 opt_cmd = argv;
         4583                 if (!opt_title && !opt_line)
         4584                         opt_title = basename(xstrdup(argv[0]));
         4585         }
         4586         setlocale(LC_CTYPE, "");
         4587         XSetLocaleModifiers("");
         4588         tnew(MAX(cols, 1), MAX(rows, 1));
         4589         xinit();
         4590         selinit();
         4591         run();
         4592 
         4593         return 0;
         4594 }
         4595