Split X-specific code into x.c - st - Personal fork of st
 (HTM) git clone git://git.drkhsh.at/st.git
 (DIR) Log
 (DIR) Files
 (DIR) Refs
 (DIR) README
 (DIR) LICENSE
       ---
 (DIR) commit e2ee5ee6114eb74bb08cb9abe5a3020203e92688
 (DIR) parent c63a87cd936c1eeef14c4c21572e5b782d3df4bc
 (HTM) Author: Michael Forney <mforney@mforney.org>
       Date:   Fri, 20 Jan 2017 00:06:39 -0800
       
       Split X-specific code into x.c
       
       Diffstat:
         M Makefile                            |       5 ++++-
         M config.def.h                        |      64 ++++++++++++++++----------------
         M st.c                                |    2080 ++-----------------------------
         A st.h                                |     272 +++++++++++++++++++++++++++++++
         A win.h                               |      29 +++++++++++++++++++++++++++++
         A x.c                                 |    1766 ++++++++++++++++++++++++++++++
       
       6 files changed, 2195 insertions(+), 2021 deletions(-)
       ---
 (DIR) diff --git a/Makefile b/Makefile
       @@ -3,7 +3,7 @@
        
        include config.mk
        
       -SRC = st.c
       +SRC = st.c x.c
        OBJ = ${SRC:.c=.o}
        
        all: options st
       @@ -21,6 +21,9 @@ config.h:
                @echo CC $<
                @${CC} -c ${CFLAGS} $<
        
       +st.o: config.h st.h win.h
       +x.o: arg.h st.h win.h
       +
        ${OBJ}: config.h config.mk
        
        st: ${OBJ}
 (DIR) diff --git a/config.def.h b/config.def.h
       @@ -5,8 +5,8 @@
         *
         * font: see http://freedesktop.org/software/fontconfig/fontconfig-user.html
         */
       -static char font[] = "Liberation Mono:pixelsize=12:antialias=true:autohint=true";
       -static int borderpx = 2;
       +char font[] = "Liberation Mono:pixelsize=12:antialias=true:autohint=true";
       +int borderpx = 2;
        
        /*
         * What program is execed by st depends of these precedence rules:
       @@ -24,8 +24,8 @@ static char stty_args[] = "stty raw pass8 nl -echo -iexten -cstopb 38400";
        static char vtiden[] = "\033[?6c";
        
        /* Kerning / character bounding-box multipliers */
       -static float cwscale = 1.0;
       -static float chscale = 1.0;
       +float cwscale = 1.0;
       +float chscale = 1.0;
        
        /*
         * word delimiter string
       @@ -35,26 +35,26 @@ static float chscale = 1.0;
        static char worddelimiters[] = " ";
        
        /* selection timeouts (in milliseconds) */
       -static unsigned int doubleclicktimeout = 300;
       -static unsigned int tripleclicktimeout = 600;
       +unsigned int doubleclicktimeout = 300;
       +unsigned int tripleclicktimeout = 600;
        
        /* alt screens */
       -static int allowaltscreen = 1;
       +int allowaltscreen = 1;
        
        /* frames per second st should at maximum draw to the screen */
       -static unsigned int xfps = 120;
       -static unsigned int actionfps = 30;
       +unsigned int xfps = 120;
       +unsigned int actionfps = 30;
        
        /*
         * blinking timeout (set to 0 to disable blinking) for the terminal blinking
         * attribute.
         */
       -static unsigned int blinktimeout = 800;
       +unsigned int blinktimeout = 800;
        
        /*
         * thickness of underline and bar cursors
         */
       -static unsigned int cursorthickness = 2;
       +unsigned int cursorthickness = 2;
        
        /*
         * bell volume. It must be a value between -100 and 100. Use 0 for disabling
       @@ -63,7 +63,7 @@ static unsigned int cursorthickness = 2;
        static int bellvolume = 0;
        
        /* default TERM value */
       -static char termname[] = "st-256color";
       +char termname[] = "st-256color";
        
        /*
         * spaces per tab
       @@ -83,7 +83,7 @@ static char termname[] = "st-256color";
        static unsigned int tabspaces = 8;
        
        /* Terminal colors (16 first used in escape sequence) */
       -static const char *colorname[] = {
       +const char *colorname[] = {
                /* 8 normal colors */
                "black",
                "red3",
       @@ -116,10 +116,10 @@ static const char *colorname[] = {
         * Default colors (colorname index)
         * foreground, background, cursor, reverse cursor
         */
       -static unsigned int defaultfg = 7;
       -static unsigned int defaultbg = 0;
       -static unsigned int defaultcs = 256;
       -static unsigned int defaultrcs = 257;
       +unsigned int defaultfg = 7;
       +unsigned int defaultbg = 0;
       +unsigned int defaultcs = 256;
       +unsigned int defaultrcs = 257;
        
        /*
         * Default shape of cursor
       @@ -128,33 +128,33 @@ static unsigned int defaultrcs = 257;
         * 6: Bar ("|")
         * 7: Snowman ("☃")
         */
       -static unsigned int cursorshape = 2;
       +unsigned int cursorshape = 2;
        
        /*
         * Default columns and rows numbers
         */
        
       -static unsigned int cols = 80;
       -static unsigned int rows = 24;
       +unsigned int cols = 80;
       +unsigned int rows = 24;
        
        /*
         * Default colour and shape of the mouse cursor
         */
       -static unsigned int mouseshape = XC_xterm;
       -static unsigned int mousefg = 7;
       -static unsigned int mousebg = 0;
       +unsigned int mouseshape = XC_xterm;
       +unsigned int mousefg = 7;
       +unsigned int mousebg = 0;
        
        /*
         * Color used to display font attributes when fontconfig selected a font which
         * doesn't match the ones requested.
         */
       -static unsigned int defaultattr = 11;
       +unsigned int defaultattr = 11;
        
        /*
         * Internal mouse shortcuts.
         * Beware that overloading Button1 will disable the selection.
         */
       -static MouseShortcut mshortcuts[] = {
       +MouseShortcut mshortcuts[] = {
                /* button               mask            string */
                { Button4,              XK_ANY_MOD,     "\031" },
                { Button5,              XK_ANY_MOD,     "\005" },
       @@ -163,15 +163,15 @@ static MouseShortcut mshortcuts[] = {
        /* Internal keyboard shortcuts. */
        #define MODKEY Mod1Mask
        
       -static Shortcut shortcuts[] = {
       +Shortcut shortcuts[] = {
                /* mask                 keysym          function        argument */
                { XK_ANY_MOD,           XK_Break,       sendbreak,      {.i =  0} },
                { ControlMask,          XK_Print,       toggleprinter,  {.i =  0} },
                { ShiftMask,            XK_Print,       printscreen,    {.i =  0} },
                { XK_ANY_MOD,           XK_Print,       printsel,       {.i =  0} },
       -        { MODKEY|ShiftMask,     XK_Prior,       xzoom,          {.f = +1} },
       -        { MODKEY|ShiftMask,     XK_Next,        xzoom,          {.f = -1} },
       -        { MODKEY|ShiftMask,     XK_Home,        xzoomreset,     {.f =  0} },
       +        { MODKEY|ShiftMask,     XK_Prior,       zoom,           {.f = +1} },
       +        { MODKEY|ShiftMask,     XK_Next,        zoom,           {.f = -1} },
       +        { MODKEY|ShiftMask,     XK_Home,        zoomreset,      {.f =  0} },
                { ShiftMask,            XK_Insert,      selpaste,       {.i =  0} },
                { MODKEY|ShiftMask,     XK_Insert,      clippaste,      {.i =  0} },
                { MODKEY|ShiftMask,     XK_C,           clipcopy,       {.i =  0} },
       @@ -222,7 +222,7 @@ static uint ignoremod = Mod2Mask|XK_SWITCH_MOD;
         * Note that if you want to use ShiftMask with selmasks, set this to an other
         * modifier, set to 0 to not use it.
         */
       -static uint forceselmod = ShiftMask;
       +uint forceselmod = ShiftMask;
        
        /*
         * This is the huge key array which defines all compatibility to the Linux
       @@ -451,7 +451,7 @@ static Key key[] = {
         * ButtonRelease and MotionNotify.
         * If no match is found, regular selection is used.
         */
       -static uint selmasks[] = {
       +uint selmasks[] = {
                [SEL_RECTANGULAR] = Mod1Mask,
        };
        
       @@ -459,7 +459,7 @@ static uint selmasks[] = {
         * Printable characters in ASCII, used to estimate the advance width
         * of single wide characters.
         */
       -static char ascii_printable[] =
       +char ascii_printable[] =
                " !\"#$%&'()*+,-./0123456789:;<=>?"
                "@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_"
                "`abcdefghijklmnopqrstuvwxyz{|}~";
 (DIR) diff --git a/st.c b/st.c
       @@ -21,23 +21,21 @@
        #include <time.h>
        #include <unistd.h>
        #include <libgen.h>
       -#include <X11/Xatom.h>
       -#include <X11/Xlib.h>
       -#include <X11/Xutil.h>
       -#include <X11/cursorfont.h>
       -#include <X11/keysym.h>
       -#include <X11/Xft/Xft.h>
       -#include <X11/XKBlib.h>
        #include <fontconfig/fontconfig.h>
        #include <wchar.h>
        
       -#include "arg.h"
       +/* X11 */
       +#include <X11/cursorfont.h>
       +#include <X11/Xft/Xft.h>
        
        char *argv0;
        
        #define Glyph Glyph_
        #define Font Font_
        
       +#include "win.h"
       +#include "st.h"
       +
        #if   defined(__linux)
         #include <pty.h>
        #elif defined(__OpenBSD__) || defined(__NetBSD__) || defined(__APPLE__)
       @@ -46,67 +44,24 @@ char *argv0;
         #include <libutil.h>
        #endif
        
       -
       -/* XEMBED messages */
       -#define XEMBED_FOCUS_IN  4
       -#define XEMBED_FOCUS_OUT 5
       -
        /* Arbitrary sizes */
        #define UTF_INVALID   0xFFFD
       -#define UTF_SIZ       4
        #define ESC_BUF_SIZ   (128*UTF_SIZ)
        #define ESC_ARG_SIZ   16
        #define STR_BUF_SIZ   ESC_BUF_SIZ
        #define STR_ARG_SIZ   ESC_ARG_SIZ
       -#define XK_ANY_MOD    UINT_MAX
       -#define XK_NO_MOD     0
       -#define XK_SWITCH_MOD (1<<13)
        
        /* macros */
       -#define MIN(a, b)                ((a) < (b) ? (a) : (b))
       -#define MAX(a, b)                ((a) < (b) ? (b) : (a))
       -#define LEN(a)                        (sizeof(a) / sizeof(a)[0])
        #define NUMMAXLEN(x)                ((int)(sizeof(x) * 2.56 + 0.5) + 1)
        #define DEFAULT(a, b)                (a) = (a) ? (a) : (b)
       -#define BETWEEN(x, a, b)        ((a) <= (x) && (x) <= (b))
       -#define DIVCEIL(n, d)                (((n) + ((d) - 1)) / (d))
        #define ISCONTROLC0(c)                (BETWEEN(c, 0, 0x1f) || (c) == '\177')
        #define ISCONTROLC1(c)                (BETWEEN(c, 0x80, 0x9f))
        #define ISCONTROL(c)                (ISCONTROLC0(c) || ISCONTROLC1(c))
        #define ISDELIM(u)                (utf8strchr(worddelimiters, u) != NULL)
       -#define LIMIT(x, a, b)                (x) = (x) < (a) ? (a) : (x) > (b) ? (b) : (x)
       -#define ATTRCMP(a, b)                ((a).mode != (b).mode || (a).fg != (b).fg || \
       -                                (a).bg != (b).bg)
       -#define IS_SET(flag)                ((term.mode & (flag)) != 0)
       -#define TIMEDIFF(t1, t2)        ((t1.tv_sec-t2.tv_sec)*1000 + \
       -                                (t1.tv_nsec-t2.tv_nsec)/1E6)
       -#define MODBIT(x, set, bit)        ((set) ? ((x) |= (bit)) : ((x) &= ~(bit)))
       -
       -#define TRUECOLOR(r,g,b)        (1 << 24 | (r) << 16 | (g) << 8 | (b))
       -#define IS_TRUECOL(x)                (1 << 24 & (x))
       -#define TRUERED(x)                (((x) & 0xff0000) >> 8)
       -#define TRUEGREEN(x)                (((x) & 0xff00))
       -#define TRUEBLUE(x)                (((x) & 0xff) << 8)
        
        /* constants */
        #define ISO14755CMD                "dmenu -w %lu -p codepoint: </dev/null"
        
       -enum glyph_attribute {
       -        ATTR_NULL       = 0,
       -        ATTR_BOLD       = 1 << 0,
       -        ATTR_FAINT      = 1 << 1,
       -        ATTR_ITALIC     = 1 << 2,
       -        ATTR_UNDERLINE  = 1 << 3,
       -        ATTR_BLINK      = 1 << 4,
       -        ATTR_REVERSE    = 1 << 5,
       -        ATTR_INVISIBLE  = 1 << 6,
       -        ATTR_STRUCK     = 1 << 7,
       -        ATTR_WRAP       = 1 << 8,
       -        ATTR_WIDE       = 1 << 9,
       -        ATTR_WDUMMY     = 1 << 10,
       -        ATTR_BOLD_FAINT = ATTR_BOLD | ATTR_FAINT,
       -};
       -
        enum cursor_movement {
                CURSOR_SAVE,
                CURSOR_LOAD
       @@ -118,34 +73,6 @@ enum cursor_state {
                CURSOR_ORIGIN   = 2
        };
        
       -enum term_mode {
       -        MODE_WRAP        = 1 << 0,
       -        MODE_INSERT      = 1 << 1,
       -        MODE_APPKEYPAD   = 1 << 2,
       -        MODE_ALTSCREEN   = 1 << 3,
       -        MODE_CRLF        = 1 << 4,
       -        MODE_MOUSEBTN    = 1 << 5,
       -        MODE_MOUSEMOTION = 1 << 6,
       -        MODE_REVERSE     = 1 << 7,
       -        MODE_KBDLOCK     = 1 << 8,
       -        MODE_HIDE        = 1 << 9,
       -        MODE_ECHO        = 1 << 10,
       -        MODE_APPCURSOR   = 1 << 11,
       -        MODE_MOUSESGR    = 1 << 12,
       -        MODE_8BIT        = 1 << 13,
       -        MODE_BLINK       = 1 << 14,
       -        MODE_FBLINK      = 1 << 15,
       -        MODE_FOCUS       = 1 << 16,
       -        MODE_MOUSEX10    = 1 << 17,
       -        MODE_MOUSEMANY   = 1 << 18,
       -        MODE_BRCKTPASTE  = 1 << 19,
       -        MODE_PRINT       = 1 << 20,
       -        MODE_UTF8        = 1 << 21,
       -        MODE_SIXEL       = 1 << 22,
       -        MODE_MOUSE       = MODE_MOUSEBTN|MODE_MOUSEMOTION|MODE_MOUSEX10\
       -                          |MODE_MOUSEMANY,
       -};
       -
        enum charset {
                CS_GRAPHIC0,
                CS_GRAPHIC1,
       @@ -167,53 +94,6 @@ enum escape_state {
                ESC_DCS        =128,
        };
        
       -enum window_state {
       -        WIN_VISIBLE = 1,
       -        WIN_FOCUSED = 2
       -};
       -
       -enum selection_mode {
       -        SEL_IDLE = 0,
       -        SEL_EMPTY = 1,
       -        SEL_READY = 2
       -};
       -
       -enum selection_type {
       -        SEL_REGULAR = 1,
       -        SEL_RECTANGULAR = 2
       -};
       -
       -enum selection_snap {
       -        SNAP_WORD = 1,
       -        SNAP_LINE = 2
       -};
       -
       -typedef unsigned char uchar;
       -typedef unsigned int uint;
       -typedef unsigned long ulong;
       -typedef unsigned short ushort;
       -
       -typedef uint_least32_t Rune;
       -
       -typedef XftDraw *Draw;
       -typedef XftColor Color;
       -
       -typedef struct {
       -        Rune u;           /* character code */
       -        ushort mode;      /* attribute flags */
       -        uint32_t fg;      /* foreground  */
       -        uint32_t bg;      /* background  */
       -} Glyph;
       -
       -typedef Glyph *Line;
       -
       -typedef struct {
       -        Glyph attr; /* current char attributes */
       -        int x;
       -        int y;
       -        char state;
       -} TCursor;
       -
        /* CSI Escape sequence structs */
        /* ESC '[' [[ [<priv>] <arg> [;]] <mode> [<mode>]] */
        typedef struct {
       @@ -235,56 +115,6 @@ typedef struct {
                int narg;              /* nb of args */
        } STREscape;
        
       -/* Internal representation of the screen */
       -typedef struct {
       -        int row;      /* nb row */
       -        int col;      /* nb col */
       -        Line *line;   /* screen */
       -        Line *alt;    /* alternate screen */
       -        int *dirty;  /* dirtyness of lines */
       -        XftGlyphFontSpec *specbuf; /* font spec buffer used for rendering */
       -        TCursor c;    /* cursor */
       -        int top;      /* top    scroll limit */
       -        int bot;      /* bottom scroll limit */
       -        int mode;     /* terminal mode flags */
       -        int esc;      /* escape state flags */
       -        char trantbl[4]; /* charset table translation */
       -        int charset;  /* current charset */
       -        int icharset; /* selected charset for sequence */
       -        int numlock; /* lock numbers in keyboard */
       -        int *tabs;
       -} Term;
       -
       -/* Purely graphic info */
       -typedef struct {
       -        Display *dpy;
       -        Colormap cmap;
       -        Window win;
       -        Drawable buf;
       -        Atom xembed, wmdeletewin, netwmname, netwmpid;
       -        XIM xim;
       -        XIC xic;
       -        Draw draw;
       -        Visual *vis;
       -        XSetWindowAttributes attrs;
       -        int scr;
       -        int isfixed; /* is fixed geometry? */
       -        int l, t; /* left and top offset */
       -        int gm; /* geometry mask */
       -        int tw, th; /* tty width and height */
       -        int w, h; /* window width and height */
       -        int ch; /* char height */
       -        int cw; /* char width  */
       -        char state; /* focus, redraw, visible */
       -        int cursor; /* cursor style */
       -} XWindow;
       -
       -typedef struct {
       -        uint b;
       -        uint mask;
       -        char *s;
       -} MouseShortcut;
       -
        typedef struct {
                KeySym k;
                uint mask;
       @@ -295,89 +125,26 @@ typedef struct {
                signed char crlf;      /* crlf mode          */
        } Key;
        
       -typedef struct {
       -        int mode;
       -        int type;
       -        int snap;
       -        /*
       -         * Selection variables:
       -         * nb – normalized coordinates of the beginning of the selection
       -         * ne – normalized coordinates of the end of the selection
       -         * ob – original coordinates of the beginning of the selection
       -         * oe – original coordinates of the end of the selection
       -         */
       -        struct {
       -                int x, y;
       -        } nb, ne, ob, oe;
       -
       -        char *primary, *clipboard;
       -        Atom xtarget;
       -        int alt;
       -        struct timespec tclick1;
       -        struct timespec tclick2;
       -} Selection;
       -
       -typedef union {
       -        int i;
       -        uint ui;
       -        float f;
       -        const void *v;
       -} Arg;
       -
       -typedef struct {
       -        uint mod;
       -        KeySym keysym;
       -        void (*func)(const Arg *);
       -        const Arg arg;
       -} Shortcut;
       -
        /* function definitions used in config.h */
        static void clipcopy(const Arg *);
        static void clippaste(const Arg *);
        static void numlock(const Arg *);
        static void selpaste(const Arg *);
       -static void xzoom(const Arg *);
       -static void xzoomabs(const Arg *);
       -static void xzoomreset(const Arg *);
       +static void zoom(const Arg *);
       +static void zoomabs(const Arg *);
       +static void zoomreset(const Arg *);
        static void printsel(const Arg *);
        static void printscreen(const Arg *) ;
        static void iso14755(const Arg *);
        static void toggleprinter(const Arg *);
        static void sendbreak(const Arg *);
        
       -/* Config.h for applying patches and the configuration. */
       +/* config.h for applying patches and the configuration. */
        #include "config.h"
        
       -/* Font structure */
       -typedef struct {
       -        int height;
       -        int width;
       -        int ascent;
       -        int descent;
       -        int badslant;
       -        int badweight;
       -        short lbearing;
       -        short rbearing;
       -        XftFont *match;
       -        FcFontSet *set;
       -        FcPattern *pattern;
       -} Font;
       -
       -/* Drawing Context */
       -typedef struct {
       -        Color col[MAX(LEN(colorname), 256)];
       -        Font font, bfont, ifont, ibfont;
       -        GC gc;
       -} DC;
       -
       -static void die(const char *, ...);
       -static void draw(void);
       -static void redraw(void);
       -static void drawregion(int, int, int, int);
        static void execsh(void);
        static void stty(void);
        static void sigchld(int);
       -static void run(void);
        
        static void csidump(void);
        static void csihandle(void);
       @@ -389,7 +156,6 @@ static void strhandle(void);
        static void strparse(void);
        static void strreset(void);
        
       -static int tattrset(int);
        static void tprinter(char *, size_t);
        static void tdumpsel(void);
        static void tdumpline(int);
       @@ -403,7 +169,6 @@ static void tinsertblankline(int);
        static int tlinelen(int);
        static void tmoveto(int, int);
        static void tmoveato(int, int);
       -static void tnew(int, int);
        static void tnewline(int);
        static void tputtab(int);
        static void tputc(Rune);
       @@ -415,8 +180,6 @@ static void tsetattr(int *, int);
        static void tsetchar(Rune, Glyph *, int, int);
        static void tsetscroll(int, int);
        static void tswapscreen(void);
       -static void tsetdirt(int, int);
       -static void tsetdirtattr(int);
        static void tsetmode(int, int, int *, int);
        static void tfulldirt(void);
        static void techo(Rune);
       @@ -425,151 +188,53 @@ static void tdectest(char );
        static void tdefutf8(char);
        static int32_t tdefcolor(int *, int *, int);
        static void tdeftran(char);
       -static inline int match(uint, uint);
       -static void ttynew(void);
       -static size_t ttyread(void);
       -static void ttyresize(void);
       -static void ttysend(char *, size_t);
       -static void ttywrite(const char *, size_t);
        static void tstrsequence(uchar);
        
       -static inline ushort sixd_to_16bit(int);
       -static int xmakeglyphfontspecs(XftGlyphFontSpec *, const Glyph *, int, int, int);
       -static void xdrawglyphfontspecs(const XftGlyphFontSpec *, Glyph, int, int, int);
       -static void xdrawglyph(Glyph, int, int);
       -static void xhints(void);
       -static void xclear(int, int, int, int);
       -static void xdrawcursor(void);
       -static void xinit(void);
       -static void xloadcols(void);
       -static int xsetcolorname(int, const char *);
       -static int xgeommasktogravity(int);
       -static int xloadfont(Font *, FcPattern *);
       -static void xloadfonts(char *, double);
       -static void xsettitle(char *);
       -static void xresettitle(void);
       -static void xsetpointermotion(int);
       -static void xseturgency(int);
       -static void xsetsel(char *, Time);
       -static void xunloadfont(Font *);
       -static void xunloadfonts(void);
       -static void xresize(int, int);
       -
       -static void expose(XEvent *);
       -static void visibility(XEvent *);
       -static void unmap(XEvent *);
       -static char *kmap(KeySym, uint);
       -static void kpress(XEvent *);
       -static void cmessage(XEvent *);
       -static void cresize(int, int);
       -static void resize(XEvent *);
       -static void focus(XEvent *);
       -static void brelease(XEvent *);
       -static void bpress(XEvent *);
       -static void bmotion(XEvent *);
       -static void propnotify(XEvent *);
       -static void selnotify(XEvent *);
       -static void selclear(XEvent *);
       -static void selrequest(XEvent *);
       -
       -static void selinit(void);
       -static void selnormalize(void);
       -static inline int selected(int, int);
       -static char *getsel(void);
       -static void selcopy(Time);
        static void selscroll(int, int);
        static void selsnap(int *, int *, int);
       -static int x2col(int);
       -static int y2row(int);
       -static void getbuttoninfo(XEvent *);
       -static void mousereport(XEvent *);
        
       -static size_t utf8decode(char *, Rune *, size_t);
        static Rune utf8decodebyte(char, size_t *);
       -static size_t utf8encode(Rune, char *);
        static char utf8encodebyte(Rune, size_t);
        static char *utf8strchr(char *s, Rune u);
        static size_t utf8validate(Rune *, size_t);
        
        static ssize_t xwrite(int, const char *, size_t);
       -static void *xmalloc(size_t);
        static void *xrealloc(void *, size_t);
       -static char *xstrdup(char *);
       -
       -static void usage(void);
       -
       -static void (*handler[LASTEvent])(XEvent *) = {
       -        [KeyPress] = kpress,
       -        [ClientMessage] = cmessage,
       -        [ConfigureNotify] = resize,
       -        [VisibilityNotify] = visibility,
       -        [UnmapNotify] = unmap,
       -        [Expose] = expose,
       -        [FocusIn] = focus,
       -        [FocusOut] = focus,
       -        [MotionNotify] = bmotion,
       -        [ButtonPress] = bpress,
       -        [ButtonRelease] = brelease,
       -/*
       - * Uncomment if you want the selection to disappear when you select something
       - * different in another window.
       - */
       -/*        [SelectionClear] = selclear, */
       -        [SelectionNotify] = selnotify,
       -/*
       - * PropertyNotify is only turned on when there is some INCR transfer happening
       - * for the selection retrieval.
       - */
       -        [PropertyNotify] = propnotify,
       -        [SelectionRequest] = selrequest,
       -};
        
        /* Globals */
       -static DC dc;
       -static XWindow xw;
       -static Term term;
       +TermWindow win;
       +Term term;
       +Selection sel;
       +int cmdfd;
       +pid_t pid;
       +char **opt_cmd  = NULL;
       +char *opt_class = NULL;
       +char *opt_embed = NULL;
       +char *opt_font  = NULL;
       +char *opt_io    = NULL;
       +char *opt_line  = NULL;
       +char *opt_name  = NULL;
       +char *opt_title = NULL;
       +int oldbutton   = 3; /* button event on startup: 3 = release */
       +
        static CSIEscape csiescseq;
        static STREscape strescseq;
       -static int cmdfd;
       -static pid_t pid;
       -static Selection sel;
        static int iofd = 1;
       -static char **opt_cmd  = NULL;
       -static char *opt_class = NULL;
       -static char *opt_embed = NULL;
       -static char *opt_font  = NULL;
       -static char *opt_io    = NULL;
       -static char *opt_line  = NULL;
       -static char *opt_name  = NULL;
       -static char *opt_title = NULL;
       -static int oldbutton   = 3; /* button event on startup: 3 = release */
       -
       -static char *usedfont = NULL;
       -static double usedfontsize = 0;
       -static double defaultfontsize = 0;
       +
       +char *usedfont = NULL;
       +double usedfontsize = 0;
       +double defaultfontsize = 0;
        
        static uchar utfbyte[UTF_SIZ + 1] = {0x80,    0, 0xC0, 0xE0, 0xF0};
        static uchar utfmask[UTF_SIZ + 1] = {0xC0, 0x80, 0xE0, 0xF0, 0xF8};
        static Rune utfmin[UTF_SIZ + 1] = {       0,    0,  0x80,  0x800,  0x10000};
        static Rune utfmax[UTF_SIZ + 1] = {0x10FFFF, 0x7F, 0x7FF, 0xFFFF, 0x10FFFF};
        
       -/* Font Ring Cache */
       -enum {
       -        FRC_NORMAL,
       -        FRC_ITALIC,
       -        FRC_BOLD,
       -        FRC_ITALICBOLD
       -};
       -
       -typedef struct {
       -        XftFont *font;
       -        int flags;
       -        Rune unicodep;
       -} Fontcache;
       -
       -/* Fontcache is an array now. A new font will be appended to the array. */
       -static Fontcache frc[16];
       -static int frclen = 0;
       +/* config.h array lengths */
       +size_t colornamelen = LEN(colorname);
       +size_t mshortcutslen = LEN(mshortcuts);
       +size_t shortcutslen = LEN(shortcuts);
       +size_t selmaskslen = LEN(selmasks);
        
        ssize_t
        xwrite(int fd, const char *s, size_t len)
       @@ -714,16 +379,13 @@ selinit(void)
                sel.ob.x = -1;
                sel.primary = NULL;
                sel.clipboard = NULL;
       -        sel.xtarget = XInternAtom(xw.dpy, "UTF8_STRING", 0);
       -        if (sel.xtarget == None)
       -                sel.xtarget = XA_STRING;
        }
        
        int
        x2col(int x)
        {
                x -= borderpx;
       -        x /= xw.cw;
       +        x /= win.cw;
        
                return LIMIT(x, 0, term.col-1);
        }
       @@ -732,7 +394,7 @@ int
        y2row(int y)
        {
                y -= borderpx;
       -        y /= xw.ch;
       +        y /= win.ch;
        
                return LIMIT(y, 0, term.row-1);
        }
       @@ -867,141 +529,6 @@ selsnap(int *x, int *y, int direction)
                }
        }
        
       -void
       -getbuttoninfo(XEvent *e)
       -{
       -        int type;
       -        uint state = e->xbutton.state & ~(Button1Mask | forceselmod);
       -
       -        sel.alt = IS_SET(MODE_ALTSCREEN);
       -
       -        sel.oe.x = x2col(e->xbutton.x);
       -        sel.oe.y = y2row(e->xbutton.y);
       -        selnormalize();
       -
       -        sel.type = SEL_REGULAR;
       -        for (type = 1; type < LEN(selmasks); ++type) {
       -                if (match(selmasks[type], state)) {
       -                        sel.type = type;
       -                        break;
       -                }
       -        }
       -}
       -
       -void
       -mousereport(XEvent *e)
       -{
       -        int x = x2col(e->xbutton.x), y = y2row(e->xbutton.y),
       -            button = e->xbutton.button, state = e->xbutton.state,
       -            len;
       -        char buf[40];
       -        static int ox, oy;
       -
       -        /* from urxvt */
       -        if (e->xbutton.type == MotionNotify) {
       -                if (x == ox && y == oy)
       -                        return;
       -                if (!IS_SET(MODE_MOUSEMOTION) && !IS_SET(MODE_MOUSEMANY))
       -                        return;
       -                /* MOUSE_MOTION: no reporting if no button is pressed */
       -                if (IS_SET(MODE_MOUSEMOTION) && oldbutton == 3)
       -                        return;
       -
       -                button = oldbutton + 32;
       -                ox = x;
       -                oy = y;
       -        } else {
       -                if (!IS_SET(MODE_MOUSESGR) && e->xbutton.type == ButtonRelease) {
       -                        button = 3;
       -                } else {
       -                        button -= Button1;
       -                        if (button >= 3)
       -                                button += 64 - 3;
       -                }
       -                if (e->xbutton.type == ButtonPress) {
       -                        oldbutton = button;
       -                        ox = x;
       -                        oy = y;
       -                } else if (e->xbutton.type == ButtonRelease) {
       -                        oldbutton = 3;
       -                        /* MODE_MOUSEX10: no button release reporting */
       -                        if (IS_SET(MODE_MOUSEX10))
       -                                return;
       -                        if (button == 64 || button == 65)
       -                                return;
       -                }
       -        }
       -
       -        if (!IS_SET(MODE_MOUSEX10)) {
       -                button += ((state & ShiftMask  ) ? 4  : 0)
       -                        + ((state & Mod4Mask   ) ? 8  : 0)
       -                        + ((state & ControlMask) ? 16 : 0);
       -        }
       -
       -        if (IS_SET(MODE_MOUSESGR)) {
       -                len = snprintf(buf, sizeof(buf), "\033[<%d;%d;%d%c",
       -                                button, x+1, y+1,
       -                                e->xbutton.type == ButtonRelease ? 'm' : 'M');
       -        } else if (x < 223 && y < 223) {
       -                len = snprintf(buf, sizeof(buf), "\033[M%c%c%c",
       -                                32+button, 32+x+1, 32+y+1);
       -        } else {
       -                return;
       -        }
       -
       -        ttywrite(buf, len);
       -}
       -
       -void
       -bpress(XEvent *e)
       -{
       -        struct timespec now;
       -        MouseShortcut *ms;
       -
       -        if (IS_SET(MODE_MOUSE) && !(e->xbutton.state & forceselmod)) {
       -                mousereport(e);
       -                return;
       -        }
       -
       -        for (ms = mshortcuts; ms < mshortcuts + LEN(mshortcuts); ms++) {
       -                if (e->xbutton.button == ms->b
       -                                && match(ms->mask, e->xbutton.state)) {
       -                        ttysend(ms->s, strlen(ms->s));
       -                        return;
       -                }
       -        }
       -
       -        if (e->xbutton.button == Button1) {
       -                clock_gettime(CLOCK_MONOTONIC, &now);
       -
       -                /* Clear previous selection, logically and visually. */
       -                selclear(NULL);
       -                sel.mode = SEL_EMPTY;
       -                sel.type = SEL_REGULAR;
       -                sel.oe.x = sel.ob.x = x2col(e->xbutton.x);
       -                sel.oe.y = sel.ob.y = y2row(e->xbutton.y);
       -
       -                /*
       -                 * If the user clicks below predefined timeouts specific
       -                 * snapping behaviour is exposed.
       -                 */
       -                if (TIMEDIFF(now, sel.tclick2) <= tripleclicktimeout) {
       -                        sel.snap = SNAP_LINE;
       -                } else if (TIMEDIFF(now, sel.tclick1) <= doubleclicktimeout) {
       -                        sel.snap = SNAP_WORD;
       -                } else {
       -                        sel.snap = 0;
       -                }
       -                selnormalize();
       -
       -                if (sel.snap != 0)
       -                        sel.mode = SEL_READY;
       -                tsetdirt(sel.nb.y, sel.ne.y);
       -                sel.tclick2 = sel.tclick1;
       -                sel.tclick1 = now;
       -        }
       -}
       -
        char *
        getsel(void)
        {
       @@ -1057,148 +584,25 @@ getsel(void)
        }
        
        void
       -selcopy(Time t)
       -{
       -        xsetsel(getsel(), t);
       -}
       -
       -void
       -propnotify(XEvent *e)
       -{
       -        XPropertyEvent *xpev;
       -        Atom clipboard = XInternAtom(xw.dpy, "CLIPBOARD", 0);
       -
       -        xpev = &e->xproperty;
       -        if (xpev->state == PropertyNewValue &&
       -                        (xpev->atom == XA_PRIMARY ||
       -                         xpev->atom == clipboard)) {
       -                selnotify(e);
       -        }
       -}
       -
       -void
       -selnotify(XEvent *e)
       -{
       -        ulong nitems, ofs, rem;
       -        int format;
       -        uchar *data, *last, *repl;
       -        Atom type, incratom, property;
       -
       -        incratom = XInternAtom(xw.dpy, "INCR", 0);
       -
       -        ofs = 0;
       -        if (e->type == SelectionNotify) {
       -                property = e->xselection.property;
       -        } else if(e->type == PropertyNotify) {
       -                property = e->xproperty.atom;
       -        } else {
       -                return;
       -        }
       -        if (property == None)
       -                return;
       -
       -        do {
       -                if (XGetWindowProperty(xw.dpy, xw.win, property, ofs,
       -                                        BUFSIZ/4, False, AnyPropertyType,
       -                                        &type, &format, &nitems, &rem,
       -                                        &data)) {
       -                        fprintf(stderr, "Clipboard allocation failed\n");
       -                        return;
       -                }
       -
       -                if (e->type == PropertyNotify && nitems == 0 && rem == 0) {
       -                        /*
       -                         * If there is some PropertyNotify with no data, then
       -                         * this is the signal of the selection owner that all
       -                         * data has been transferred. We won't need to receive
       -                         * PropertyNotify events anymore.
       -                         */
       -                        MODBIT(xw.attrs.event_mask, 0, PropertyChangeMask);
       -                        XChangeWindowAttributes(xw.dpy, xw.win, CWEventMask,
       -                                        &xw.attrs);
       -                }
       -
       -                if (type == incratom) {
       -                        /*
       -                         * Activate the PropertyNotify events so we receive
       -                         * when the selection owner does send us the next
       -                         * chunk of data.
       -                         */
       -                        MODBIT(xw.attrs.event_mask, 1, PropertyChangeMask);
       -                        XChangeWindowAttributes(xw.dpy, xw.win, CWEventMask,
       -                                        &xw.attrs);
       -
       -                        /*
       -                         * Deleting the property is the transfer start signal.
       -                         */
       -                        XDeleteProperty(xw.dpy, xw.win, (int)property);
       -                        continue;
       -                }
       -
       -                /*
       -                 * As seen in getsel:
       -                 * Line endings are inconsistent in the terminal and GUI world
       -                 * copy and pasting. When receiving some selection data,
       -                 * replace all '\n' with '\r'.
       -                 * FIXME: Fix the computer world.
       -                 */
       -                repl = data;
       -                last = data + nitems * format / 8;
       -                while ((repl = memchr(repl, '\n', last - repl))) {
       -                        *repl++ = '\r';
       -                }
       -
       -                if (IS_SET(MODE_BRCKTPASTE) && ofs == 0)
       -                        ttywrite("\033[200~", 6);
       -                ttysend((char *)data, nitems * format / 8);
       -                if (IS_SET(MODE_BRCKTPASTE) && rem == 0)
       -                        ttywrite("\033[201~", 6);
       -                XFree(data);
       -                /* number of 32-bit chunks returned */
       -                ofs += nitems * format / 32;
       -        } while (rem > 0);
       -
       -        /*
       -         * Deleting the property again tells the selection owner to send the
       -         * next data chunk in the property.
       -         */
       -        XDeleteProperty(xw.dpy, xw.win, (int)property);
       -}
       -
       -void
        selpaste(const Arg *dummy)
        {
       -        XConvertSelection(xw.dpy, XA_PRIMARY, sel.xtarget, XA_PRIMARY,
       -                        xw.win, CurrentTime);
       +        xselpaste();
        }
        
        void
        clipcopy(const Arg *dummy)
        {
       -        Atom clipboard;
       -
       -        if (sel.clipboard != NULL)
       -                free(sel.clipboard);
       -
       -        if (sel.primary != NULL) {
       -                sel.clipboard = xstrdup(sel.primary);
       -                clipboard = XInternAtom(xw.dpy, "CLIPBOARD", 0);
       -                XSetSelectionOwner(xw.dpy, clipboard, xw.win, CurrentTime);
       -        }
       +        xclipcopy();
        }
        
        void
        clippaste(const Arg *dummy)
        {
       -        Atom clipboard;
       -
       -        clipboard = XInternAtom(xw.dpy, "CLIPBOARD", 0);
       -        XConvertSelection(xw.dpy, clipboard, sel.xtarget, clipboard,
       -                        xw.win, CurrentTime);
       +        xclippaste();
        }
        
        void
       -selclear(XEvent *e)
       +selclear(void)
        {
                if (sel.ob.x == -1)
                        return;
       @@ -1208,120 +612,6 @@ selclear(XEvent *e)
        }
        
        void
       -selrequest(XEvent *e)
       -{
       -        XSelectionRequestEvent *xsre;
       -        XSelectionEvent xev;
       -        Atom xa_targets, string, clipboard;
       -        char *seltext;
       -
       -        xsre = (XSelectionRequestEvent *) e;
       -        xev.type = SelectionNotify;
       -        xev.requestor = xsre->requestor;
       -        xev.selection = xsre->selection;
       -        xev.target = xsre->target;
       -        xev.time = xsre->time;
       -        if (xsre->property == None)
       -                xsre->property = xsre->target;
       -
       -        /* reject */
       -        xev.property = None;
       -
       -        xa_targets = XInternAtom(xw.dpy, "TARGETS", 0);
       -        if (xsre->target == xa_targets) {
       -                /* respond with the supported type */
       -                string = sel.xtarget;
       -                XChangeProperty(xsre->display, xsre->requestor, xsre->property,
       -                                XA_ATOM, 32, PropModeReplace,
       -                                (uchar *) &string, 1);
       -                xev.property = xsre->property;
       -        } else if (xsre->target == sel.xtarget || xsre->target == XA_STRING) {
       -                /*
       -                 * xith XA_STRING non ascii characters may be incorrect in the
       -                 * requestor. It is not our problem, use utf8.
       -                 */
       -                clipboard = XInternAtom(xw.dpy, "CLIPBOARD", 0);
       -                if (xsre->selection == XA_PRIMARY) {
       -                        seltext = sel.primary;
       -                } else if (xsre->selection == clipboard) {
       -                        seltext = sel.clipboard;
       -                } else {
       -                        fprintf(stderr,
       -                                "Unhandled clipboard selection 0x%lx\n",
       -                                xsre->selection);
       -                        return;
       -                }
       -                if (seltext != NULL) {
       -                        XChangeProperty(xsre->display, xsre->requestor,
       -                                        xsre->property, xsre->target,
       -                                        8, PropModeReplace,
       -                                        (uchar *)seltext, strlen(seltext));
       -                        xev.property = xsre->property;
       -                }
       -        }
       -
       -        /* all done, send a notification to the listener */
       -        if (!XSendEvent(xsre->display, xsre->requestor, 1, 0, (XEvent *) &xev))
       -                fprintf(stderr, "Error sending SelectionNotify event\n");
       -}
       -
       -void
       -xsetsel(char *str, Time t)
       -{
       -        free(sel.primary);
       -        sel.primary = str;
       -
       -        XSetSelectionOwner(xw.dpy, XA_PRIMARY, xw.win, t);
       -        if (XGetSelectionOwner(xw.dpy, XA_PRIMARY) != xw.win)
       -                selclear(0);
       -}
       -
       -void
       -brelease(XEvent *e)
       -{
       -        if (IS_SET(MODE_MOUSE) && !(e->xbutton.state & forceselmod)) {
       -                mousereport(e);
       -                return;
       -        }
       -
       -        if (e->xbutton.button == Button2) {
       -                selpaste(NULL);
       -        } else if (e->xbutton.button == Button1) {
       -                if (sel.mode == SEL_READY) {
       -                        getbuttoninfo(e);
       -                        selcopy(e->xbutton.time);
       -                } else
       -                        selclear(NULL);
       -                sel.mode = SEL_IDLE;
       -                tsetdirt(sel.nb.y, sel.ne.y);
       -        }
       -}
       -
       -void
       -bmotion(XEvent *e)
       -{
       -        int oldey, oldex, oldsby, oldsey;
       -
       -        if (IS_SET(MODE_MOUSE) && !(e->xbutton.state & forceselmod)) {
       -                mousereport(e);
       -                return;
       -        }
       -
       -        if (!sel.mode)
       -                return;
       -
       -        sel.mode = SEL_READY;
       -        oldey = sel.oe.y;
       -        oldex = sel.oe.x;
       -        oldsby = sel.nb.y;
       -        oldsey = sel.ne.y;
       -        getbuttoninfo(e);
       -
       -        if (oldey != sel.oe.y || oldex != sel.oe.x)
       -                tsetdirt(MIN(sel.nb.y, oldsby), MAX(sel.ne.y, oldsey));
       -}
       -
       -void
        die(const char *errstr, ...)
        {
                va_list ap;
       @@ -1337,7 +627,6 @@ execsh(void)
        {
                char **args, *sh, *prog;
                const struct passwd *pw;
       -        char buf[sizeof(long) * 8 + 1];
        
                errno = 0;
                if ((pw = getpwuid(getuid())) == NULL) {
       @@ -1358,8 +647,6 @@ execsh(void)
                        prog = sh;
                args = (opt_cmd) ? opt_cmd : (char *[]) {prog, NULL};
        
       -        snprintf(buf, sizeof(buf), "%lu", xw.win);
       -
                unsetenv("COLUMNS");
                unsetenv("LINES");
                unsetenv("TERMCAP");
       @@ -1368,7 +655,7 @@ execsh(void)
                setenv("SHELL", sh, 1);
                setenv("HOME", pw->pw_dir, 1);
                setenv("TERM", termname, 1);
       -        setenv("WINDOWID", buf, 1);
       +        xsetenv();
        
                signal(SIGCHLD, SIG_DFL);
                signal(SIGHUP, SIG_DFL);
       @@ -1606,8 +893,8 @@ ttyresize(void)
        
                w.ws_row = term.row;
                w.ws_col = term.col;
       -        w.ws_xpixel = xw.tw;
       -        w.ws_ypixel = xw.th;
       +        w.ws_xpixel = win.tw;
       +        w.ws_ypixel = win.th;
                if (ioctl(cmdfd, TIOCSWINSZ, &w) < 0)
                        fprintf(stderr, "Couldn't set window size: %s\n", strerror(errno));
        }
       @@ -1771,7 +1058,7 @@ selscroll(int orig, int n)
        
                if (BETWEEN(sel.ob.y, orig, term.bot) || BETWEEN(sel.oe.y, orig, term.bot)) {
                        if ((sel.ob.y += n) > term.bot || (sel.oe.y += n) < term.top) {
       -                        selclear(NULL);
       +                        selclear();
                                return;
                        }
                        if (sel.type == SEL_RECTANGULAR) {
       @@ -1917,7 +1204,7 @@ tclearregion(int x1, int y1, int x2, int y2)
                        for (x = x1; x <= x2; x++) {
                                gp = &term.line[y][x];
                                if (selected(x, y))
       -                                selclear(NULL);
       +                                selclear();
                                gp->fg = term.c.attr.fg;
                                gp->bg = term.c.attr.bg;
                                gp->mode = 0;
       @@ -2368,7 +1655,7 @@ csihandle(void)
                        tputtab(csiescseq.arg[0]);
                        break;
                case 'J': /* ED -- Clear screen */
       -                selclear(NULL);
       +                selclear();
                        switch (csiescseq.arg[0]) {
                        case 0: /* below */
                                tclearregion(term.c.x, term.c.y, term.col-1, term.c.y);
       @@ -2475,7 +1762,7 @@ csihandle(void)
                                if (!BETWEEN(csiescseq.arg[0], 0, 6)) {
                                        goto unknown;
                                }
       -                        xw.cursor = csiescseq.arg[0];
       +                        win.cursor = csiescseq.arg[0];
                                break;
                        default:
                                goto unknown;
       @@ -2642,12 +1929,13 @@ tprinter(char *s, size_t len)
        void
        iso14755(const Arg *arg)
        {
       -        char cmd[sizeof(ISO14755CMD) + NUMMAXLEN(xw.win)];
       +        unsigned long id = xwinid();
       +        char cmd[sizeof(ISO14755CMD) + NUMMAXLEN(id)];
                FILE *p;
                char *us, *e, codepoint[9], uc[UTF_SIZ];
                unsigned long utf32;
        
       -        snprintf(cmd, sizeof(cmd), ISO14755CMD, xw.win);
       +        snprintf(cmd, sizeof(cmd), ISO14755CMD, id);
                if (!(p = popen(cmd, "r")))
                        return;
        
       @@ -2833,10 +2121,10 @@ tcontrolcode(uchar ascii)
                                /* backwards compatibility to xterm */
                                strhandle();
                        } else {
       -                        if (!(xw.state & WIN_FOCUSED))
       +                        if (!(win.state & WIN_FOCUSED))
                                        xseturgency(1);
                                if (bellvolume)
       -                                XkbBell(xw.dpy, xw.win, bellvolume, (Atom)NULL);
       +                                xbell(bellvolume);
                        }
                        break;
                case '\033': /* ESC */
       @@ -2968,7 +2256,7 @@ eschandle(uchar ascii)
                        break;
                case 'c': /* RIS -- Reset to inital state */
                        treset();
       -                xresettitle();
       +                resettitle();
                        xloadcols();
                        break;
                case '=': /* DECPAM -- Application keypad */
       @@ -3109,7 +2397,7 @@ check_control_code:
                        return;
                }
                if (sel.ob.x != -1 && BETWEEN(term.c.y, sel.ob.y, sel.oe.y))
       -                selclear(NULL);
       +                selclear();
        
                gp = &term.line[term.c.y][term.c.x];
                if (IS_SET(MODE_WRAP) && (term.c.state & CURSOR_WRAPNEXT)) {
       @@ -3177,7 +2465,7 @@ tresize(int col, int row)
                }
        
                /* resize to new width */
       -        term.specbuf = xrealloc(term.specbuf, col * sizeof(XftGlyphFontSpec));
       +        term.specbuf = xrealloc(term.specbuf, col * sizeof(GlyphFontSpec));
        
                /* resize to new height */
                term.line = xrealloc(term.line, row * sizeof(Line));
       @@ -3228,993 +2516,66 @@ tresize(int col, int row)
        }
        
        void
       -xresize(int col, int row)
       +zoom(const Arg *arg)
        {
       -        xw.tw = MAX(1, col * xw.cw);
       -        xw.th = MAX(1, row * xw.ch);
       -
       -        XFreePixmap(xw.dpy, xw.buf);
       -        xw.buf = XCreatePixmap(xw.dpy, xw.win, xw.w, xw.h,
       -                        DefaultDepth(xw.dpy, xw.scr));
       -        XftDrawChange(xw.draw, xw.buf);
       -        xclear(0, 0, xw.w, xw.h);
       -}
       +        Arg larg;
        
       -ushort
       -sixd_to_16bit(int x)
       -{
       -        return x == 0 ? 0 : 0x3737 + 0x2828 * x;
       +        larg.f = usedfontsize + arg->f;
       +        zoomabs(&larg);
        }
        
       -int
       -xloadcolor(int i, const char *name, Color *ncolor)
       +void
       +zoomabs(const Arg *arg)
        {
       -        XRenderColor color = { .alpha = 0xffff };
       -
       -        if (!name) {
       -                if (BETWEEN(i, 16, 255)) { /* 256 color */
       -                        if (i < 6*6*6+16) { /* same colors as xterm */
       -                                color.red   = sixd_to_16bit( ((i-16)/36)%6 );
       -                                color.green = sixd_to_16bit( ((i-16)/6) %6 );
       -                                color.blue  = sixd_to_16bit( ((i-16)/1) %6 );
       -                        } else { /* greyscale */
       -                                color.red = 0x0808 + 0x0a0a * (i - (6*6*6+16));
       -                                color.green = color.blue = color.red;
       -                        }
       -                        return XftColorAllocValue(xw.dpy, xw.vis,
       -                                                  xw.cmap, &color, ncolor);
       -                } else
       -                        name = colorname[i];
       -        }
       -
       -        return XftColorAllocName(xw.dpy, xw.vis, xw.cmap, name, ncolor);
       +        xunloadfonts();
       +        xloadfonts(usedfont, arg->f);
       +        cresize(0, 0);
       +        ttyresize();
       +        redraw();
       +        xhints();
        }
        
        void
       -xloadcols(void)
       +zoomreset(const Arg *arg)
        {
       -        int i;
       -        static int loaded;
       -        Color *cp;
       +        Arg larg;
        
       -        if (loaded) {
       -                for (cp = dc.col; cp < &dc.col[LEN(dc.col)]; ++cp)
       -                        XftColorFree(xw.dpy, xw.vis, xw.cmap, cp);
       +        if (defaultfontsize > 0) {
       +                larg.f = defaultfontsize;
       +                zoomabs(&larg);
                }
       -
       -        for (i = 0; i < LEN(dc.col); i++)
       -                if (!xloadcolor(i, NULL, &dc.col[i])) {
       -                        if (colorname[i])
       -                                die("Could not allocate color '%s'\n", colorname[i]);
       -                        else
       -                                die("Could not allocate color %d\n", i);
       -                }
       -        loaded = 1;
       -}
       -
       -int
       -xsetcolorname(int x, const char *name)
       -{
       -        Color ncolor;
       -
       -        if (!BETWEEN(x, 0, LEN(dc.col)))
       -                return 1;
       -
       -
       -        if (!xloadcolor(x, name, &ncolor))
       -                return 1;
       -
       -        XftColorFree(xw.dpy, xw.vis, xw.cmap, &dc.col[x]);
       -        dc.col[x] = ncolor;
       -
       -        return 0;
        }
        
       -/*
       - * Absolute coordinates.
       - */
        void
       -xclear(int x1, int y1, int x2, int y2)
       +resettitle(void)
        {
       -        XftDrawRect(xw.draw,
       -                        &dc.col[IS_SET(MODE_REVERSE)? defaultfg : defaultbg],
       -                        x1, y1, x2-x1, y2-y1);
       +        xsettitle(opt_title ? opt_title : "st");
        }
        
        void
       -xhints(void)
       +redraw(void)
        {
       -        XClassHint class = {opt_name ? opt_name : termname,
       -                            opt_class ? opt_class : termname};
       -        XWMHints wm = {.flags = InputHint, .input = 1};
       -        XSizeHints *sizeh = NULL;
       -
       -        sizeh = XAllocSizeHints();
       -
       -        sizeh->flags = PSize | PResizeInc | PBaseSize;
       -        sizeh->height = xw.h;
       -        sizeh->width = xw.w;
       -        sizeh->height_inc = xw.ch;
       -        sizeh->width_inc = xw.cw;
       -        sizeh->base_height = 2 * borderpx;
       -        sizeh->base_width = 2 * borderpx;
       -        if (xw.isfixed) {
       -                sizeh->flags |= PMaxSize | PMinSize;
       -                sizeh->min_width = sizeh->max_width = xw.w;
       -                sizeh->min_height = sizeh->max_height = xw.h;
       -        }
       -        if (xw.gm & (XValue|YValue)) {
       -                sizeh->flags |= USPosition | PWinGravity;
       -                sizeh->x = xw.l;
       -                sizeh->y = xw.t;
       -                sizeh->win_gravity = xgeommasktogravity(xw.gm);
       -        }
       -
       -        XSetWMProperties(xw.dpy, xw.win, NULL, NULL, NULL, 0, sizeh, &wm,
       -                        &class);
       -        XFree(sizeh);
       +        tfulldirt();
       +        draw();
        }
        
        int
       -xgeommasktogravity(int mask)
       +match(uint mask, uint state)
        {
       -        switch (mask & (XNegative|YNegative)) {
       -        case 0:
       -                return NorthWestGravity;
       -        case XNegative:
       -                return NorthEastGravity;
       -        case YNegative:
       -                return SouthWestGravity;
       -        }
       -
       -        return SouthEastGravity;
       +        return mask == XK_ANY_MOD || mask == (state & ~ignoremod);
        }
        
       -int
       -xloadfont(Font *f, FcPattern *pattern)
       +void
       +numlock(const Arg *dummy)
        {
       -        FcPattern *configured;
       -        FcPattern *match;
       -        FcResult result;
       -        XGlyphInfo extents;
       -        int wantattr, haveattr;
       +        term.numlock ^= 1;
       +}
        
       -        /*
       -         * Manually configure instead of calling XftMatchFont
       -         * so that we can use the configured pattern for
       -         * "missing glyph" lookups.
       -         */
       -        configured = FcPatternDuplicate(pattern);
       -        if (!configured)
       -                return 1;
       -
       -        FcConfigSubstitute(NULL, configured, FcMatchPattern);
       -        XftDefaultSubstitute(xw.dpy, xw.scr, configured);
       -
       -        match = FcFontMatch(NULL, configured, &result);
       -        if (!match) {
       -                FcPatternDestroy(configured);
       -                return 1;
       -        }
       -
       -        if (!(f->match = XftFontOpenPattern(xw.dpy, match))) {
       -                FcPatternDestroy(configured);
       -                FcPatternDestroy(match);
       -                return 1;
       -        }
       -
       -        if ((XftPatternGetInteger(pattern, "slant", 0, &wantattr) ==
       -            XftResultMatch)) {
       -                /*
       -                 * Check if xft was unable to find a font with the appropriate
       -                 * slant but gave us one anyway. Try to mitigate.
       -                 */
       -                if ((XftPatternGetInteger(f->match->pattern, "slant", 0,
       -                    &haveattr) != XftResultMatch) || haveattr < wantattr) {
       -                        f->badslant = 1;
       -                        fputs("st: font slant does not match\n", stderr);
       -                }
       -        }
       -
       -        if ((XftPatternGetInteger(pattern, "weight", 0, &wantattr) ==
       -            XftResultMatch)) {
       -                if ((XftPatternGetInteger(f->match->pattern, "weight", 0,
       -                    &haveattr) != XftResultMatch) || haveattr != wantattr) {
       -                        f->badweight = 1;
       -                        fputs("st: font weight does not match\n", stderr);
       -                }
       -        }
       -
       -        XftTextExtentsUtf8(xw.dpy, f->match,
       -                (const FcChar8 *) ascii_printable,
       -                strlen(ascii_printable), &extents);
       -
       -        f->set = NULL;
       -        f->pattern = configured;
       -
       -        f->ascent = f->match->ascent;
       -        f->descent = f->match->descent;
       -        f->lbearing = 0;
       -        f->rbearing = f->match->max_advance_width;
       -
       -        f->height = f->ascent + f->descent;
       -        f->width = DIVCEIL(extents.xOff, strlen(ascii_printable));
       -
       -        return 0;
       -}
       -
       -void
       -xloadfonts(char *fontstr, double fontsize)
       -{
       -        FcPattern *pattern;
       -        double fontval;
       -        float ceilf(float);
       -
       -        if (fontstr[0] == '-') {
       -                pattern = XftXlfdParse(fontstr, False, False);
       -        } else {
       -                pattern = FcNameParse((FcChar8 *)fontstr);
       -        }
       -
       -        if (!pattern)
       -                die("st: can't open font %s\n", fontstr);
       -
       -        if (fontsize > 1) {
       -                FcPatternDel(pattern, FC_PIXEL_SIZE);
       -                FcPatternDel(pattern, FC_SIZE);
       -                FcPatternAddDouble(pattern, FC_PIXEL_SIZE, (double)fontsize);
       -                usedfontsize = fontsize;
       -        } else {
       -                if (FcPatternGetDouble(pattern, FC_PIXEL_SIZE, 0, &fontval) ==
       -                                FcResultMatch) {
       -                        usedfontsize = fontval;
       -                } else if (FcPatternGetDouble(pattern, FC_SIZE, 0, &fontval) ==
       -                                FcResultMatch) {
       -                        usedfontsize = -1;
       -                } else {
       -                        /*
       -                         * Default font size is 12, if none given. This is to
       -                         * have a known usedfontsize value.
       -                         */
       -                        FcPatternAddDouble(pattern, FC_PIXEL_SIZE, 12);
       -                        usedfontsize = 12;
       -                }
       -                defaultfontsize = usedfontsize;
       -        }
       -
       -        if (xloadfont(&dc.font, pattern))
       -                die("st: can't open font %s\n", fontstr);
       -
       -        if (usedfontsize < 0) {
       -                FcPatternGetDouble(dc.font.match->pattern,
       -                                   FC_PIXEL_SIZE, 0, &fontval);
       -                usedfontsize = fontval;
       -                if (fontsize == 0)
       -                        defaultfontsize = fontval;
       -        }
       -
       -        /* Setting character width and height. */
       -        xw.cw = ceilf(dc.font.width * cwscale);
       -        xw.ch = ceilf(dc.font.height * chscale);
       -
       -        FcPatternDel(pattern, FC_SLANT);
       -        FcPatternAddInteger(pattern, FC_SLANT, FC_SLANT_ITALIC);
       -        if (xloadfont(&dc.ifont, pattern))
       -                die("st: can't open font %s\n", fontstr);
       -
       -        FcPatternDel(pattern, FC_WEIGHT);
       -        FcPatternAddInteger(pattern, FC_WEIGHT, FC_WEIGHT_BOLD);
       -        if (xloadfont(&dc.ibfont, pattern))
       -                die("st: can't open font %s\n", fontstr);
       -
       -        FcPatternDel(pattern, FC_SLANT);
       -        FcPatternAddInteger(pattern, FC_SLANT, FC_SLANT_ROMAN);
       -        if (xloadfont(&dc.bfont, pattern))
       -                die("st: can't open font %s\n", fontstr);
       -
       -        FcPatternDestroy(pattern);
       -}
       -
       -void
       -xunloadfont(Font *f)
       -{
       -        XftFontClose(xw.dpy, f->match);
       -        FcPatternDestroy(f->pattern);
       -        if (f->set)
       -                FcFontSetDestroy(f->set);
       -}
       -
       -void
       -xunloadfonts(void)
       -{
       -        /* Free the loaded fonts in the font cache.  */
       -        while (frclen > 0)
       -                XftFontClose(xw.dpy, frc[--frclen].font);
       -
       -        xunloadfont(&dc.font);
       -        xunloadfont(&dc.bfont);
       -        xunloadfont(&dc.ifont);
       -        xunloadfont(&dc.ibfont);
       -}
       -
       -void
       -xzoom(const Arg *arg)
       -{
       -        Arg larg;
       -
       -        larg.f = usedfontsize + arg->f;
       -        xzoomabs(&larg);
       -}
       -
       -void
       -xzoomabs(const Arg *arg)
       -{
       -        xunloadfonts();
       -        xloadfonts(usedfont, arg->f);
       -        cresize(0, 0);
       -        ttyresize();
       -        redraw();
       -        xhints();
       -}
       -
       -void
       -xzoomreset(const Arg *arg)
       -{
       -        Arg larg;
       -
       -        if (defaultfontsize > 0) {
       -                larg.f = defaultfontsize;
       -                xzoomabs(&larg);
       -        }
       -}
       -
       -void
       -xinit(void)
       -{
       -        XGCValues gcvalues;
       -        Cursor cursor;
       -        Window parent;
       -        pid_t thispid = getpid();
       -        XColor xmousefg, xmousebg;
       -
       -        if (!(xw.dpy = XOpenDisplay(NULL)))
       -                die("Can't open display\n");
       -        xw.scr = XDefaultScreen(xw.dpy);
       -        xw.vis = XDefaultVisual(xw.dpy, xw.scr);
       -
       -        /* font */
       -        if (!FcInit())
       -                die("Could not init fontconfig.\n");
       -
       -        usedfont = (opt_font == NULL)? font : opt_font;
       -        xloadfonts(usedfont, 0);
       -
       -        /* colors */
       -        xw.cmap = XDefaultColormap(xw.dpy, xw.scr);
       -        xloadcols();
       -
       -        /* adjust fixed window geometry */
       -        xw.w = 2 * borderpx + term.col * xw.cw;
       -        xw.h = 2 * borderpx + term.row * xw.ch;
       -        if (xw.gm & XNegative)
       -                xw.l += DisplayWidth(xw.dpy, xw.scr) - xw.w - 2;
       -        if (xw.gm & YNegative)
       -                xw.t += DisplayHeight(xw.dpy, xw.scr) - xw.h - 2;
       -
       -        /* Events */
       -        xw.attrs.background_pixel = dc.col[defaultbg].pixel;
       -        xw.attrs.border_pixel = dc.col[defaultbg].pixel;
       -        xw.attrs.bit_gravity = NorthWestGravity;
       -        xw.attrs.event_mask = FocusChangeMask | KeyPressMask
       -                | ExposureMask | VisibilityChangeMask | StructureNotifyMask
       -                | ButtonMotionMask | ButtonPressMask | ButtonReleaseMask;
       -        xw.attrs.colormap = xw.cmap;
       -
       -        if (!(opt_embed && (parent = strtol(opt_embed, NULL, 0))))
       -                parent = XRootWindow(xw.dpy, xw.scr);
       -        xw.win = XCreateWindow(xw.dpy, parent, xw.l, xw.t,
       -                        xw.w, xw.h, 0, XDefaultDepth(xw.dpy, xw.scr), InputOutput,
       -                        xw.vis, CWBackPixel | CWBorderPixel | CWBitGravity
       -                        | CWEventMask | CWColormap, &xw.attrs);
       -
       -        memset(&gcvalues, 0, sizeof(gcvalues));
       -        gcvalues.graphics_exposures = False;
       -        dc.gc = XCreateGC(xw.dpy, parent, GCGraphicsExposures,
       -                        &gcvalues);
       -        xw.buf = XCreatePixmap(xw.dpy, xw.win, xw.w, xw.h,
       -                        DefaultDepth(xw.dpy, xw.scr));
       -        XSetForeground(xw.dpy, dc.gc, dc.col[defaultbg].pixel);
       -        XFillRectangle(xw.dpy, xw.buf, dc.gc, 0, 0, xw.w, xw.h);
       -
       -        /* Xft rendering context */
       -        xw.draw = XftDrawCreate(xw.dpy, xw.buf, xw.vis, xw.cmap);
       -
       -        /* input methods */
       -        if ((xw.xim = XOpenIM(xw.dpy, NULL, NULL, NULL)) == NULL) {
       -                XSetLocaleModifiers("@im=local");
       -                if ((xw.xim =  XOpenIM(xw.dpy, NULL, NULL, NULL)) == NULL) {
       -                        XSetLocaleModifiers("@im=");
       -                        if ((xw.xim = XOpenIM(xw.dpy,
       -                                        NULL, NULL, NULL)) == NULL) {
       -                                die("XOpenIM failed. Could not open input"
       -                                        " device.\n");
       -                        }
       -                }
       -        }
       -        xw.xic = XCreateIC(xw.xim, XNInputStyle, XIMPreeditNothing
       -                                           | XIMStatusNothing, XNClientWindow, xw.win,
       -                                           XNFocusWindow, xw.win, NULL);
       -        if (xw.xic == NULL)
       -                die("XCreateIC failed. Could not obtain input method.\n");
       -
       -        /* white cursor, black outline */
       -        cursor = XCreateFontCursor(xw.dpy, mouseshape);
       -        XDefineCursor(xw.dpy, xw.win, cursor);
       -
       -        if (XParseColor(xw.dpy, xw.cmap, colorname[mousefg], &xmousefg) == 0) {
       -                xmousefg.red   = 0xffff;
       -                xmousefg.green = 0xffff;
       -                xmousefg.blue  = 0xffff;
       -        }
       -
       -        if (XParseColor(xw.dpy, xw.cmap, colorname[mousebg], &xmousebg) == 0) {
       -                xmousebg.red   = 0x0000;
       -                xmousebg.green = 0x0000;
       -                xmousebg.blue  = 0x0000;
       -        }
       -
       -        XRecolorCursor(xw.dpy, cursor, &xmousefg, &xmousebg);
       -
       -        xw.xembed = XInternAtom(xw.dpy, "_XEMBED", False);
       -        xw.wmdeletewin = XInternAtom(xw.dpy, "WM_DELETE_WINDOW", False);
       -        xw.netwmname = XInternAtom(xw.dpy, "_NET_WM_NAME", False);
       -        XSetWMProtocols(xw.dpy, xw.win, &xw.wmdeletewin, 1);
       -
       -        xw.netwmpid = XInternAtom(xw.dpy, "_NET_WM_PID", False);
       -        XChangeProperty(xw.dpy, xw.win, xw.netwmpid, XA_CARDINAL, 32,
       -                        PropModeReplace, (uchar *)&thispid, 1);
       -
       -        xresettitle();
       -        XMapWindow(xw.dpy, xw.win);
       -        xhints();
       -        XSync(xw.dpy, False);
       -}
       -
       -int
       -xmakeglyphfontspecs(XftGlyphFontSpec *specs, const Glyph *glyphs, int len, int x, int y)
       -{
       -        float winx = borderpx + x * xw.cw, winy = borderpx + y * xw.ch, xp, yp;
       -        ushort mode, prevmode = USHRT_MAX;
       -        Font *font = &dc.font;
       -        int frcflags = FRC_NORMAL;
       -        float runewidth = xw.cw;
       -        Rune rune;
       -        FT_UInt glyphidx;
       -        FcResult fcres;
       -        FcPattern *fcpattern, *fontpattern;
       -        FcFontSet *fcsets[] = { NULL };
       -        FcCharSet *fccharset;
       -        int i, f, numspecs = 0;
       -
       -        for (i = 0, xp = winx, yp = winy + font->ascent; i < len; ++i) {
       -                /* Fetch rune and mode for current glyph. */
       -                rune = glyphs[i].u;
       -                mode = glyphs[i].mode;
       -
       -                /* Skip dummy wide-character spacing. */
       -                if (mode == ATTR_WDUMMY)
       -                        continue;
       -
       -                /* Determine font for glyph if different from previous glyph. */
       -                if (prevmode != mode) {
       -                        prevmode = mode;
       -                        font = &dc.font;
       -                        frcflags = FRC_NORMAL;
       -                        runewidth = xw.cw * ((mode & ATTR_WIDE) ? 2.0f : 1.0f);
       -                        if ((mode & ATTR_ITALIC) && (mode & ATTR_BOLD)) {
       -                                font = &dc.ibfont;
       -                                frcflags = FRC_ITALICBOLD;
       -                        } else if (mode & ATTR_ITALIC) {
       -                                font = &dc.ifont;
       -                                frcflags = FRC_ITALIC;
       -                        } else if (mode & ATTR_BOLD) {
       -                                font = &dc.bfont;
       -                                frcflags = FRC_BOLD;
       -                        }
       -                        yp = winy + font->ascent;
       -                }
       -
       -                /* Lookup character index with default font. */
       -                glyphidx = XftCharIndex(xw.dpy, font->match, rune);
       -                if (glyphidx) {
       -                        specs[numspecs].font = font->match;
       -                        specs[numspecs].glyph = glyphidx;
       -                        specs[numspecs].x = (short)xp;
       -                        specs[numspecs].y = (short)yp;
       -                        xp += runewidth;
       -                        numspecs++;
       -                        continue;
       -                }
       -
       -                /* Fallback on font cache, search the font cache for match. */
       -                for (f = 0; f < frclen; f++) {
       -                        glyphidx = XftCharIndex(xw.dpy, frc[f].font, rune);
       -                        /* Everything correct. */
       -                        if (glyphidx && frc[f].flags == frcflags)
       -                                break;
       -                        /* We got a default font for a not found glyph. */
       -                        if (!glyphidx && frc[f].flags == frcflags
       -                                        && frc[f].unicodep == rune) {
       -                                break;
       -                        }
       -                }
       -
       -                /* Nothing was found. Use fontconfig to find matching font. */
       -                if (f >= frclen) {
       -                        if (!font->set)
       -                                font->set = FcFontSort(0, font->pattern,
       -                                                       1, 0, &fcres);
       -                        fcsets[0] = font->set;
       -
       -                        /*
       -                         * Nothing was found in the cache. Now use
       -                         * some dozen of Fontconfig calls to get the
       -                         * font for one single character.
       -                         *
       -                         * Xft and fontconfig are design failures.
       -                         */
       -                        fcpattern = FcPatternDuplicate(font->pattern);
       -                        fccharset = FcCharSetCreate();
       -
       -                        FcCharSetAddChar(fccharset, rune);
       -                        FcPatternAddCharSet(fcpattern, FC_CHARSET,
       -                                        fccharset);
       -                        FcPatternAddBool(fcpattern, FC_SCALABLE, 1);
       -
       -                        FcConfigSubstitute(0, fcpattern,
       -                                        FcMatchPattern);
       -                        FcDefaultSubstitute(fcpattern);
       -
       -                        fontpattern = FcFontSetMatch(0, fcsets, 1,
       -                                        fcpattern, &fcres);
       -
       -                        /*
       -                         * Overwrite or create the new cache entry.
       -                         */
       -                        if (frclen >= LEN(frc)) {
       -                                frclen = LEN(frc) - 1;
       -                                XftFontClose(xw.dpy, frc[frclen].font);
       -                                frc[frclen].unicodep = 0;
       -                        }
       -
       -                        frc[frclen].font = XftFontOpenPattern(xw.dpy,
       -                                        fontpattern);
       -                        frc[frclen].flags = frcflags;
       -                        frc[frclen].unicodep = rune;
       -
       -                        glyphidx = XftCharIndex(xw.dpy, frc[frclen].font, rune);
       -
       -                        f = frclen;
       -                        frclen++;
       -
       -                        FcPatternDestroy(fcpattern);
       -                        FcCharSetDestroy(fccharset);
       -                }
       -
       -                specs[numspecs].font = frc[f].font;
       -                specs[numspecs].glyph = glyphidx;
       -                specs[numspecs].x = (short)xp;
       -                specs[numspecs].y = (short)yp;
       -                xp += runewidth;
       -                numspecs++;
       -        }
       -
       -        return numspecs;
       -}
       -
       -void
       -xdrawglyphfontspecs(const XftGlyphFontSpec *specs, Glyph base, int len, int x, int y)
       -{
       -        int charlen = len * ((base.mode & ATTR_WIDE) ? 2 : 1);
       -        int winx = borderpx + x * xw.cw, winy = borderpx + y * xw.ch,
       -            width = charlen * xw.cw;
       -        Color *fg, *bg, *temp, revfg, revbg, truefg, truebg;
       -        XRenderColor colfg, colbg;
       -        XRectangle r;
       -
       -        /* Fallback on color display for attributes not supported by the font */
       -        if (base.mode & ATTR_ITALIC && base.mode & ATTR_BOLD) {
       -                if (dc.ibfont.badslant || dc.ibfont.badweight)
       -                        base.fg = defaultattr;
       -        } else if ((base.mode & ATTR_ITALIC && dc.ifont.badslant) ||
       -            (base.mode & ATTR_BOLD && dc.bfont.badweight)) {
       -                base.fg = defaultattr;
       -        }
       -
       -        if (IS_TRUECOL(base.fg)) {
       -                colfg.alpha = 0xffff;
       -                colfg.red = TRUERED(base.fg);
       -                colfg.green = TRUEGREEN(base.fg);
       -                colfg.blue = TRUEBLUE(base.fg);
       -                XftColorAllocValue(xw.dpy, xw.vis, xw.cmap, &colfg, &truefg);
       -                fg = &truefg;
       -        } else {
       -                fg = &dc.col[base.fg];
       -        }
       -
       -        if (IS_TRUECOL(base.bg)) {
       -                colbg.alpha = 0xffff;
       -                colbg.green = TRUEGREEN(base.bg);
       -                colbg.red = TRUERED(base.bg);
       -                colbg.blue = TRUEBLUE(base.bg);
       -                XftColorAllocValue(xw.dpy, xw.vis, xw.cmap, &colbg, &truebg);
       -                bg = &truebg;
       -        } else {
       -                bg = &dc.col[base.bg];
       -        }
       -
       -        /* Change basic system colors [0-7] to bright system colors [8-15] */
       -        if ((base.mode & ATTR_BOLD_FAINT) == ATTR_BOLD && BETWEEN(base.fg, 0, 7))
       -                fg = &dc.col[base.fg + 8];
       -
       -        if (IS_SET(MODE_REVERSE)) {
       -                if (fg == &dc.col[defaultfg]) {
       -                        fg = &dc.col[defaultbg];
       -                } else {
       -                        colfg.red = ~fg->color.red;
       -                        colfg.green = ~fg->color.green;
       -                        colfg.blue = ~fg->color.blue;
       -                        colfg.alpha = fg->color.alpha;
       -                        XftColorAllocValue(xw.dpy, xw.vis, xw.cmap, &colfg,
       -                                        &revfg);
       -                        fg = &revfg;
       -                }
       -
       -                if (bg == &dc.col[defaultbg]) {
       -                        bg = &dc.col[defaultfg];
       -                } else {
       -                        colbg.red = ~bg->color.red;
       -                        colbg.green = ~bg->color.green;
       -                        colbg.blue = ~bg->color.blue;
       -                        colbg.alpha = bg->color.alpha;
       -                        XftColorAllocValue(xw.dpy, xw.vis, xw.cmap, &colbg,
       -                                        &revbg);
       -                        bg = &revbg;
       -                }
       -        }
       -
       -        if (base.mode & ATTR_REVERSE) {
       -                temp = fg;
       -                fg = bg;
       -                bg = temp;
       -        }
       -
       -        if ((base.mode & ATTR_BOLD_FAINT) == ATTR_FAINT) {
       -                colfg.red = fg->color.red / 2;
       -                colfg.green = fg->color.green / 2;
       -                colfg.blue = fg->color.blue / 2;
       -                XftColorAllocValue(xw.dpy, xw.vis, xw.cmap, &colfg, &revfg);
       -                fg = &revfg;
       -        }
       -
       -        if (base.mode & ATTR_BLINK && term.mode & MODE_BLINK)
       -                fg = bg;
       -
       -        if (base.mode & ATTR_INVISIBLE)
       -                fg = bg;
       -
       -        /* Intelligent cleaning up of the borders. */
       -        if (x == 0) {
       -                xclear(0, (y == 0)? 0 : winy, borderpx,
       -                        winy + xw.ch + ((y >= term.row-1)? xw.h : 0));
       -        }
       -        if (x + charlen >= term.col) {
       -                xclear(winx + width, (y == 0)? 0 : winy, xw.w,
       -                        ((y >= term.row-1)? xw.h : (winy + xw.ch)));
       -        }
       -        if (y == 0)
       -                xclear(winx, 0, winx + width, borderpx);
       -        if (y == term.row-1)
       -                xclear(winx, winy + xw.ch, winx + width, xw.h);
       -
       -        /* Clean up the region we want to draw to. */
       -        XftDrawRect(xw.draw, bg, winx, winy, width, xw.ch);
       -
       -        /* Set the clip region because Xft is sometimes dirty. */
       -        r.x = 0;
       -        r.y = 0;
       -        r.height = xw.ch;
       -        r.width = width;
       -        XftDrawSetClipRectangles(xw.draw, winx, winy, &r, 1);
       -
       -        /* Render the glyphs. */
       -        XftDrawGlyphFontSpec(xw.draw, fg, specs, len);
       -
       -        /* Render underline and strikethrough. */
       -        if (base.mode & ATTR_UNDERLINE) {
       -                XftDrawRect(xw.draw, fg, winx, winy + dc.font.ascent + 1,
       -                                width, 1);
       -        }
       -
       -        if (base.mode & ATTR_STRUCK) {
       -                XftDrawRect(xw.draw, fg, winx, winy + 2 * dc.font.ascent / 3,
       -                                width, 1);
       -        }
       -
       -        /* Reset clip to none. */
       -        XftDrawSetClip(xw.draw, 0);
       -}
       -
       -void
       -xdrawglyph(Glyph g, int x, int y)
       -{
       -        int numspecs;
       -        XftGlyphFontSpec spec;
       -
       -        numspecs = xmakeglyphfontspecs(&spec, &g, 1, x, y);
       -        xdrawglyphfontspecs(&spec, g, numspecs, x, y);
       -}
       -
       -void
       -xdrawcursor(void)
       -{
       -        static int oldx = 0, oldy = 0;
       -        int curx;
       -        Glyph g = {' ', ATTR_NULL, defaultbg, defaultcs}, og;
       -        int ena_sel = sel.ob.x != -1 && sel.alt == IS_SET(MODE_ALTSCREEN);
       -        Color drawcol;
       -
       -        LIMIT(oldx, 0, term.col-1);
       -        LIMIT(oldy, 0, term.row-1);
       -
       -        curx = term.c.x;
       -
       -        /* adjust position if in dummy */
       -        if (term.line[oldy][oldx].mode & ATTR_WDUMMY)
       -                oldx--;
       -        if (term.line[term.c.y][curx].mode & ATTR_WDUMMY)
       -                curx--;
       -
       -        /* remove the old cursor */
       -        og = term.line[oldy][oldx];
       -        if (ena_sel && selected(oldx, oldy))
       -                og.mode ^= ATTR_REVERSE;
       -        xdrawglyph(og, oldx, oldy);
       -
       -        g.u = term.line[term.c.y][term.c.x].u;
       -
       -        /*
       -         * Select the right color for the right mode.
       -         */
       -        if (IS_SET(MODE_REVERSE)) {
       -                g.mode |= ATTR_REVERSE;
       -                g.bg = defaultfg;
       -                if (ena_sel && selected(term.c.x, term.c.y)) {
       -                        drawcol = dc.col[defaultcs];
       -                        g.fg = defaultrcs;
       -                } else {
       -                        drawcol = dc.col[defaultrcs];
       -                        g.fg = defaultcs;
       -                }
       -        } else {
       -                if (ena_sel && selected(term.c.x, term.c.y)) {
       -                        drawcol = dc.col[defaultrcs];
       -                        g.fg = defaultfg;
       -                        g.bg = defaultrcs;
       -                } else {
       -                        drawcol = dc.col[defaultcs];
       -                }
       -        }
       -
       -        if (IS_SET(MODE_HIDE))
       -                return;
       -
       -        /* draw the new one */
       -        if (xw.state & WIN_FOCUSED) {
       -                switch (xw.cursor) {
       -                case 7: /* st extension: snowman */
       -                        utf8decode("☃", &g.u, UTF_SIZ);
       -                case 0: /* Blinking Block */
       -                case 1: /* Blinking Block (Default) */
       -                case 2: /* Steady Block */
       -                        g.mode |= term.line[term.c.y][curx].mode & ATTR_WIDE;
       -                        xdrawglyph(g, term.c.x, term.c.y);
       -                        break;
       -                case 3: /* Blinking Underline */
       -                case 4: /* Steady Underline */
       -                        XftDrawRect(xw.draw, &drawcol,
       -                                        borderpx + curx * xw.cw,
       -                                        borderpx + (term.c.y + 1) * xw.ch - \
       -                                                cursorthickness,
       -                                        xw.cw, cursorthickness);
       -                        break;
       -                case 5: /* Blinking bar */
       -                case 6: /* Steady bar */
       -                        XftDrawRect(xw.draw, &drawcol,
       -                                        borderpx + curx * xw.cw,
       -                                        borderpx + term.c.y * xw.ch,
       -                                        cursorthickness, xw.ch);
       -                        break;
       -                }
       -        } else {
       -                XftDrawRect(xw.draw, &drawcol,
       -                                borderpx + curx * xw.cw,
       -                                borderpx + term.c.y * xw.ch,
       -                                xw.cw - 1, 1);
       -                XftDrawRect(xw.draw, &drawcol,
       -                                borderpx + curx * xw.cw,
       -                                borderpx + term.c.y * xw.ch,
       -                                1, xw.ch - 1);
       -                XftDrawRect(xw.draw, &drawcol,
       -                                borderpx + (curx + 1) * xw.cw - 1,
       -                                borderpx + term.c.y * xw.ch,
       -                                1, xw.ch - 1);
       -                XftDrawRect(xw.draw, &drawcol,
       -                                borderpx + curx * xw.cw,
       -                                borderpx + (term.c.y + 1) * xw.ch - 1,
       -                                xw.cw, 1);
       -        }
       -        oldx = curx, oldy = term.c.y;
       -}
       -
       -
       -void
       -xsettitle(char *p)
       -{
       -        XTextProperty prop;
       -
       -        Xutf8TextListToTextProperty(xw.dpy, &p, 1, XUTF8StringStyle,
       -                        &prop);
       -        XSetWMName(xw.dpy, xw.win, &prop);
       -        XSetTextProperty(xw.dpy, xw.win, &prop, xw.netwmname);
       -        XFree(prop.value);
       -}
       -
       -void
       -xresettitle(void)
       -{
       -        xsettitle(opt_title ? opt_title : "st");
       -}
       -
       -void
       -redraw(void)
       -{
       -        tfulldirt();
       -        draw();
       -}
       -
       -void
       -draw(void)
       -{
       -        drawregion(0, 0, term.col, term.row);
       -        XCopyArea(xw.dpy, xw.buf, xw.win, dc.gc, 0, 0, xw.w,
       -                        xw.h, 0, 0);
       -        XSetForeground(xw.dpy, dc.gc,
       -                        dc.col[IS_SET(MODE_REVERSE)?
       -                                defaultfg : defaultbg].pixel);
       -}
       -
       -void
       -drawregion(int x1, int y1, int x2, int y2)
       -{
       -        int i, x, y, ox, numspecs;
       -        Glyph base, new;
       -        XftGlyphFontSpec *specs;
       -        int ena_sel = sel.ob.x != -1 && sel.alt == IS_SET(MODE_ALTSCREEN);
       -
       -        if (!(xw.state & WIN_VISIBLE))
       -                return;
       -
       -        for (y = y1; y < y2; y++) {
       -                if (!term.dirty[y])
       -                        continue;
       -
       -                term.dirty[y] = 0;
       -
       -                specs = term.specbuf;
       -                numspecs = xmakeglyphfontspecs(specs, &term.line[y][x1], x2 - x1, x1, y);
       -
       -                i = ox = 0;
       -                for (x = x1; x < x2 && i < numspecs; x++) {
       -                        new = term.line[y][x];
       -                        if (new.mode == ATTR_WDUMMY)
       -                                continue;
       -                        if (ena_sel && selected(x, y))
       -                                new.mode ^= ATTR_REVERSE;
       -                        if (i > 0 && ATTRCMP(base, new)) {
       -                                xdrawglyphfontspecs(specs, base, i, ox, y);
       -                                specs += i;
       -                                numspecs -= i;
       -                                i = 0;
       -                        }
       -                        if (i == 0) {
       -                                ox = x;
       -                                base = new;
       -                        }
       -                        i++;
       -                }
       -                if (i > 0)
       -                        xdrawglyphfontspecs(specs, base, i, ox, y);
       -        }
       -        xdrawcursor();
       -}
       -
       -void
       -expose(XEvent *ev)
       -{
       -        redraw();
       -}
       -
       -void
       -visibility(XEvent *ev)
       -{
       -        XVisibilityEvent *e = &ev->xvisibility;
       -
       -        MODBIT(xw.state, e->state != VisibilityFullyObscured, WIN_VISIBLE);
       -}
       -
       -void
       -unmap(XEvent *ev)
       -{
       -        xw.state &= ~WIN_VISIBLE;
       -}
       -
       -void
       -xsetpointermotion(int set)
       -{
       -        MODBIT(xw.attrs.event_mask, set, PointerMotionMask);
       -        XChangeWindowAttributes(xw.dpy, xw.win, CWEventMask, &xw.attrs);
       -}
       -
       -void
       -xseturgency(int add)
       -{
       -        XWMHints *h = XGetWMHints(xw.dpy, xw.win);
       -
       -        MODBIT(h->flags, add, XUrgencyHint);
       -        XSetWMHints(xw.dpy, xw.win, h);
       -        XFree(h);
       -}
       -
       -void
       -focus(XEvent *ev)
       -{
       -        XFocusChangeEvent *e = &ev->xfocus;
       -
       -        if (e->mode == NotifyGrab)
       -                return;
       -
       -        if (ev->type == FocusIn) {
       -                XSetICFocus(xw.xic);
       -                xw.state |= WIN_FOCUSED;
       -                xseturgency(0);
       -                if (IS_SET(MODE_FOCUS))
       -                        ttywrite("\033[I", 3);
       -        } else {
       -                XUnsetICFocus(xw.xic);
       -                xw.state &= ~WIN_FOCUSED;
       -                if (IS_SET(MODE_FOCUS))
       -                        ttywrite("\033[O", 3);
       -        }
       -}
       -
       -int
       -match(uint mask, uint state)
       -{
       -        return mask == XK_ANY_MOD || mask == (state & ~ignoremod);
       -}
       -
       -void
       -numlock(const Arg *dummy)
       -{
       -        term.numlock ^= 1;
       -}
       -
       -char*
       -kmap(KeySym k, uint state)
       -{
       -        Key *kp;
       -        int i;
       +char*
       +kmap(KeySym k, uint state)
       +{
       +        Key *kp;
       +        int i;
        
                /* Check for mapped keys out of X11 function keys. */
                for (i = 0; i < LEN(mappedkeys); i++) {
       @@ -4251,211 +2612,23 @@ kmap(KeySym k, uint state)
        }
        
        void
       -kpress(XEvent *ev)
       -{
       -        XKeyEvent *e = &ev->xkey;
       -        KeySym ksym;
       -        char buf[32], *customkey;
       -        int len;
       -        Rune c;
       -        Status status;
       -        Shortcut *bp;
       -
       -        if (IS_SET(MODE_KBDLOCK))
       -                return;
       -
       -        len = XmbLookupString(xw.xic, e, buf, sizeof buf, &ksym, &status);
       -        /* 1. shortcuts */
       -        for (bp = shortcuts; bp < shortcuts + LEN(shortcuts); bp++) {
       -                if (ksym == bp->keysym && match(bp->mod, e->state)) {
       -                        bp->func(&(bp->arg));
       -                        return;
       -                }
       -        }
       -
       -        /* 2. custom keys from config.h */
       -        if ((customkey = kmap(ksym, e->state))) {
       -                ttysend(customkey, strlen(customkey));
       -                return;
       -        }
       -
       -        /* 3. composed string from input method */
       -        if (len == 0)
       -                return;
       -        if (len == 1 && e->state & Mod1Mask) {
       -                if (IS_SET(MODE_8BIT)) {
       -                        if (*buf < 0177) {
       -                                c = *buf | 0x80;
       -                                len = utf8encode(c, buf);
       -                        }
       -                } else {
       -                        buf[1] = buf[0];
       -                        buf[0] = '\033';
       -                        len = 2;
       -                }
       -        }
       -        ttysend(buf, len);
       -}
       -
       -
       -void
       -cmessage(XEvent *e)
       -{
       -        /*
       -         * See xembed specs
       -         *  http://standards.freedesktop.org/xembed-spec/xembed-spec-latest.html
       -         */
       -        if (e->xclient.message_type == xw.xembed && e->xclient.format == 32) {
       -                if (e->xclient.data.l[1] == XEMBED_FOCUS_IN) {
       -                        xw.state |= WIN_FOCUSED;
       -                        xseturgency(0);
       -                } else if (e->xclient.data.l[1] == XEMBED_FOCUS_OUT) {
       -                        xw.state &= ~WIN_FOCUSED;
       -                }
       -        } else if (e->xclient.data.l[0] == xw.wmdeletewin) {
       -                /* Send SIGHUP to shell */
       -                kill(pid, SIGHUP);
       -                exit(0);
       -        }
       -}
       -
       -void
        cresize(int width, int height)
        {
                int col, row;
        
                if (width != 0)
       -                xw.w = width;
       +                win.w = width;
                if (height != 0)
       -                xw.h = height;
       +                win.h = height;
        
       -        col = (xw.w - 2 * borderpx) / xw.cw;
       -        row = (xw.h - 2 * borderpx) / xw.ch;
       +        col = (win.w - 2 * borderpx) / win.cw;
       +        row = (win.h - 2 * borderpx) / win.ch;
        
                tresize(col, row);
                xresize(col, row);
        }
        
        void
       -resize(XEvent *e)
       -{
       -        if (e->xconfigure.width == xw.w && e->xconfigure.height == xw.h)
       -                return;
       -
       -        cresize(e->xconfigure.width, e->xconfigure.height);
       -        ttyresize();
       -}
       -
       -void
       -run(void)
       -{
       -        XEvent ev;
       -        int w = xw.w, h = xw.h;
       -        fd_set rfd;
       -        int xfd = XConnectionNumber(xw.dpy), xev, blinkset = 0, dodraw = 0;
       -        struct timespec drawtimeout, *tv = NULL, now, last, lastblink;
       -        long deltatime;
       -
       -        /* Waiting for window mapping */
       -        do {
       -                XNextEvent(xw.dpy, &ev);
       -                /*
       -                 * This XFilterEvent call is required because of XOpenIM. It
       -                 * does filter out the key event and some client message for
       -                 * the input method too.
       -                 */
       -                if (XFilterEvent(&ev, None))
       -                        continue;
       -                if (ev.type == ConfigureNotify) {
       -                        w = ev.xconfigure.width;
       -                        h = ev.xconfigure.height;
       -                }
       -        } while (ev.type != MapNotify);
       -
       -        cresize(w, h);
       -        ttynew();
       -        ttyresize();
       -
       -        clock_gettime(CLOCK_MONOTONIC, &last);
       -        lastblink = last;
       -
       -        for (xev = actionfps;;) {
       -                FD_ZERO(&rfd);
       -                FD_SET(cmdfd, &rfd);
       -                FD_SET(xfd, &rfd);
       -
       -                if (pselect(MAX(xfd, cmdfd)+1, &rfd, NULL, NULL, tv, NULL) < 0) {
       -                        if (errno == EINTR)
       -                                continue;
       -                        die("select failed: %s\n", strerror(errno));
       -                }
       -                if (FD_ISSET(cmdfd, &rfd)) {
       -                        ttyread();
       -                        if (blinktimeout) {
       -                                blinkset = tattrset(ATTR_BLINK);
       -                                if (!blinkset)
       -                                        MODBIT(term.mode, 0, MODE_BLINK);
       -                        }
       -                }
       -
       -                if (FD_ISSET(xfd, &rfd))
       -                        xev = actionfps;
       -
       -                clock_gettime(CLOCK_MONOTONIC, &now);
       -                drawtimeout.tv_sec = 0;
       -                drawtimeout.tv_nsec =  (1000 * 1E6)/ xfps;
       -                tv = &drawtimeout;
       -
       -                dodraw = 0;
       -                if (blinktimeout && TIMEDIFF(now, lastblink) > blinktimeout) {
       -                        tsetdirtattr(ATTR_BLINK);
       -                        term.mode ^= MODE_BLINK;
       -                        lastblink = now;
       -                        dodraw = 1;
       -                }
       -                deltatime = TIMEDIFF(now, last);
       -                if (deltatime > 1000 / (xev ? xfps : actionfps)) {
       -                        dodraw = 1;
       -                        last = now;
       -                }
       -
       -                if (dodraw) {
       -                        while (XPending(xw.dpy)) {
       -                                XNextEvent(xw.dpy, &ev);
       -                                if (XFilterEvent(&ev, None))
       -                                        continue;
       -                                if (handler[ev.type])
       -                                        (handler[ev.type])(&ev);
       -                        }
       -
       -                        draw();
       -                        XFlush(xw.dpy);
       -
       -                        if (xev && !FD_ISSET(xfd, &rfd))
       -                                xev--;
       -                        if (!FD_ISSET(cmdfd, &rfd) && !FD_ISSET(xfd, &rfd)) {
       -                                if (blinkset) {
       -                                        if (TIMEDIFF(now, lastblink) \
       -                                                        > blinktimeout) {
       -                                                drawtimeout.tv_nsec = 1000;
       -                                        } else {
       -                                                drawtimeout.tv_nsec = (1E6 * \
       -                                                        (blinktimeout - \
       -                                                        TIMEDIFF(now,
       -                                                                lastblink)));
       -                                        }
       -                                        drawtimeout.tv_sec = \
       -                                            drawtimeout.tv_nsec / 1E9;
       -                                        drawtimeout.tv_nsec %= (long)1E9;
       -                                } else {
       -                                        tv = NULL;
       -                                }
       -                        }
       -                }
       -        }
       -}
       -
       -void
        usage(void)
        {
                die("usage: %s [-aiv] [-c class] [-f font] [-g geometry]"
       @@ -4467,72 +2640,3 @@ usage(void)
                    "          [-T title] [-t title] [-w windowid] -l line"
                    " [stty_args ...]\n", argv0, argv0);
        }
       -
       -int
       -main(int argc, char *argv[])
       -{
       -        xw.l = xw.t = 0;
       -        xw.isfixed = False;
       -        xw.cursor = cursorshape;
       -
       -        ARGBEGIN {
       -        case 'a':
       -                allowaltscreen = 0;
       -                break;
       -        case 'c':
       -                opt_class = EARGF(usage());
       -                break;
       -        case 'e':
       -                if (argc > 0)
       -                        --argc, ++argv;
       -                goto run;
       -        case 'f':
       -                opt_font = EARGF(usage());
       -                break;
       -        case 'g':
       -                xw.gm = XParseGeometry(EARGF(usage()),
       -                                &xw.l, &xw.t, &cols, &rows);
       -                break;
       -        case 'i':
       -                xw.isfixed = 1;
       -                break;
       -        case 'o':
       -                opt_io = EARGF(usage());
       -                break;
       -        case 'l':
       -                opt_line = EARGF(usage());
       -                break;
       -        case 'n':
       -                opt_name = EARGF(usage());
       -                break;
       -        case 't':
       -        case 'T':
       -                opt_title = EARGF(usage());
       -                break;
       -        case 'w':
       -                opt_embed = EARGF(usage());
       -                break;
       -        case 'v':
       -                die("%s " VERSION " (c) 2010-2016 st engineers\n", argv0);
       -                break;
       -        default:
       -                usage();
       -        } ARGEND;
       -
       -run:
       -        if (argc > 0) {
       -                /* eat all remaining arguments */
       -                opt_cmd = argv;
       -                if (!opt_title && !opt_line)
       -                        opt_title = basename(xstrdup(argv[0]));
       -        }
       -        setlocale(LC_CTYPE, "");
       -        XSetLocaleModifiers("");
       -        tnew(MAX(cols, 1), MAX(rows, 1));
       -        xinit();
       -        selinit();
       -        run();
       -
       -        return 0;
       -}
       -
 (DIR) diff --git a/st.h b/st.h
       @@ -0,0 +1,272 @@
       +/* See LICENSE for license details. */
       +
       +/* Arbitrary sizes */
       +#define UTF_SIZ       4
       +
       +/* macros */
       +#define MIN(a, b)                ((a) < (b) ? (a) : (b))
       +#define MAX(a, b)                ((a) < (b) ? (b) : (a))
       +#define LEN(a)                        (sizeof(a) / sizeof(a)[0])
       +#define BETWEEN(x, a, b)        ((a) <= (x) && (x) <= (b))
       +#define DIVCEIL(n, d)                (((n) + ((d) - 1)) / (d))
       +#define LIMIT(x, a, b)                (x) = (x) < (a) ? (a) : (x) > (b) ? (b) : (x)
       +#define ATTRCMP(a, b)                ((a).mode != (b).mode || (a).fg != (b).fg || \
       +                                (a).bg != (b).bg)
       +#define IS_SET(flag)                ((term.mode & (flag)) != 0)
       +#define TIMEDIFF(t1, t2)        ((t1.tv_sec-t2.tv_sec)*1000 + \
       +                                (t1.tv_nsec-t2.tv_nsec)/1E6)
       +#define MODBIT(x, set, bit)        ((set) ? ((x) |= (bit)) : ((x) &= ~(bit)))
       +
       +#define TRUECOLOR(r,g,b)        (1 << 24 | (r) << 16 | (g) << 8 | (b))
       +#define IS_TRUECOL(x)                (1 << 24 & (x))
       +
       +enum glyph_attribute {
       +        ATTR_NULL       = 0,
       +        ATTR_BOLD       = 1 << 0,
       +        ATTR_FAINT      = 1 << 1,
       +        ATTR_ITALIC     = 1 << 2,
       +        ATTR_UNDERLINE  = 1 << 3,
       +        ATTR_BLINK      = 1 << 4,
       +        ATTR_REVERSE    = 1 << 5,
       +        ATTR_INVISIBLE  = 1 << 6,
       +        ATTR_STRUCK     = 1 << 7,
       +        ATTR_WRAP       = 1 << 8,
       +        ATTR_WIDE       = 1 << 9,
       +        ATTR_WDUMMY     = 1 << 10,
       +        ATTR_BOLD_FAINT = ATTR_BOLD | ATTR_FAINT,
       +};
       +
       +enum term_mode {
       +        MODE_WRAP        = 1 << 0,
       +        MODE_INSERT      = 1 << 1,
       +        MODE_APPKEYPAD   = 1 << 2,
       +        MODE_ALTSCREEN   = 1 << 3,
       +        MODE_CRLF        = 1 << 4,
       +        MODE_MOUSEBTN    = 1 << 5,
       +        MODE_MOUSEMOTION = 1 << 6,
       +        MODE_REVERSE     = 1 << 7,
       +        MODE_KBDLOCK     = 1 << 8,
       +        MODE_HIDE        = 1 << 9,
       +        MODE_ECHO        = 1 << 10,
       +        MODE_APPCURSOR   = 1 << 11,
       +        MODE_MOUSESGR    = 1 << 12,
       +        MODE_8BIT        = 1 << 13,
       +        MODE_BLINK       = 1 << 14,
       +        MODE_FBLINK      = 1 << 15,
       +        MODE_FOCUS       = 1 << 16,
       +        MODE_MOUSEX10    = 1 << 17,
       +        MODE_MOUSEMANY   = 1 << 18,
       +        MODE_BRCKTPASTE  = 1 << 19,
       +        MODE_PRINT       = 1 << 20,
       +        MODE_UTF8        = 1 << 21,
       +        MODE_SIXEL       = 1 << 22,
       +        MODE_MOUSE       = MODE_MOUSEBTN|MODE_MOUSEMOTION|MODE_MOUSEX10\
       +                          |MODE_MOUSEMANY,
       +};
       +
       +enum selection_mode {
       +        SEL_IDLE = 0,
       +        SEL_EMPTY = 1,
       +        SEL_READY = 2
       +};
       +
       +enum selection_type {
       +        SEL_REGULAR = 1,
       +        SEL_RECTANGULAR = 2
       +};
       +
       +enum selection_snap {
       +        SNAP_WORD = 1,
       +        SNAP_LINE = 2
       +};
       +
       +enum window_state {
       +        WIN_VISIBLE = 1,
       +        WIN_FOCUSED = 2
       +};
       +
       +typedef unsigned char uchar;
       +typedef unsigned int uint;
       +typedef unsigned long ulong;
       +typedef unsigned short ushort;
       +
       +typedef uint_least32_t Rune;
       +
       +typedef struct {
       +        Rune u;           /* character code */
       +        ushort mode;      /* attribute flags */
       +        uint32_t fg;      /* foreground  */
       +        uint32_t bg;      /* background  */
       +} Glyph;
       +
       +typedef Glyph *Line;
       +
       +typedef struct {
       +        Glyph attr; /* current char attributes */
       +        int x;
       +        int y;
       +        char state;
       +} TCursor;
       +
       +/* Internal representation of the screen */
       +typedef struct {
       +        int row;      /* nb row */
       +        int col;      /* nb col */
       +        Line *line;   /* screen */
       +        Line *alt;    /* alternate screen */
       +        int *dirty;  /* dirtyness of lines */
       +        GlyphFontSpec *specbuf; /* font spec buffer used for rendering */
       +        TCursor c;    /* cursor */
       +        int top;      /* top    scroll limit */
       +        int bot;      /* bottom scroll limit */
       +        int mode;     /* terminal mode flags */
       +        int esc;      /* escape state flags */
       +        char trantbl[4]; /* charset table translation */
       +        int charset;  /* current charset */
       +        int icharset; /* selected charset for sequence */
       +        int numlock; /* lock numbers in keyboard */
       +        int *tabs;
       +} Term;
       +
       +/* Purely graphic info */
       +typedef struct {
       +        int tw, th; /* tty width and height */
       +        int w, h; /* window width and height */
       +        int ch; /* char height */
       +        int cw; /* char width  */
       +        char state; /* focus, redraw, visible */
       +        int cursor; /* cursor style */
       +} TermWindow;
       +
       +typedef struct {
       +        uint b;
       +        uint mask;
       +        char *s;
       +} MouseShortcut;
       +
       +typedef struct {
       +        int mode;
       +        int type;
       +        int snap;
       +        /*
       +         * Selection variables:
       +         * nb – normalized coordinates of the beginning of the selection
       +         * ne – normalized coordinates of the end of the selection
       +         * ob – original coordinates of the beginning of the selection
       +         * oe – original coordinates of the end of the selection
       +         */
       +        struct {
       +                int x, y;
       +        } nb, ne, ob, oe;
       +
       +        char *primary, *clipboard;
       +        int alt;
       +        struct timespec tclick1;
       +        struct timespec tclick2;
       +
       +        //Atom xtarget;
       +} Selection;
       +
       +typedef union {
       +        int i;
       +        uint ui;
       +        float f;
       +        const void *v;
       +} Arg;
       +
       +typedef struct {
       +        uint mod;
       +        KeySym keysym;
       +        void (*func)(const Arg *);
       +        const Arg arg;
       +} Shortcut;
       +
       +void die(const char *, ...);
       +void redraw(void);
       +
       +int tattrset(int);
       +void tnew(int, int);
       +void tsetdirt(int, int);
       +void tsetdirtattr(int);
       +int match(uint, uint);
       +void ttynew(void);
       +size_t ttyread(void);
       +void ttyresize(void);
       +void ttysend(char *, size_t);
       +void ttywrite(const char *, size_t);
       +
       +void resettitle(void);
       +
       +char *kmap(KeySym, uint);
       +void cresize(int, int);
       +void selclear(void);
       +
       +void selinit(void);
       +void selnormalize(void);
       +int selected(int, int);
       +char *getsel(void);
       +int x2col(int);
       +int y2row(int);
       +
       +size_t utf8decode(char *, Rune *, size_t);
       +size_t utf8encode(Rune, char *);
       +
       +void *xmalloc(size_t);
       +char *xstrdup(char *);
       +
       +void usage(void);
       +
       +/* Globals */
       +extern TermWindow win;
       +extern Term term;
       +extern Selection sel;
       +extern int cmdfd;
       +extern pid_t pid;
       +extern char **opt_cmd;
       +extern char *opt_class;
       +extern char *opt_embed;
       +extern char *opt_font;
       +extern char *opt_io;
       +extern char *opt_line;
       +extern char *opt_name;
       +extern char *opt_title;
       +extern int oldbutton;
       +
       +extern char *usedfont;
       +extern double usedfontsize;
       +extern double defaultfontsize;
       +
       +/* config.h globals */
       +extern char font[];
       +extern int borderpx;
       +extern float cwscale;
       +extern float chscale;
       +extern unsigned int doubleclicktimeout;
       +extern unsigned int tripleclicktimeout;
       +extern int allowaltscreen;
       +extern unsigned int xfps;
       +extern unsigned int actionfps;
       +extern unsigned int cursorthickness;
       +extern unsigned int blinktimeout;
       +extern char termname[];
       +extern const char *colorname[];
       +extern size_t colornamelen;
       +extern unsigned int defaultfg;
       +extern unsigned int defaultbg;
       +extern unsigned int defaultcs;
       +extern unsigned int defaultrcs;
       +extern unsigned int cursorshape;
       +extern unsigned int cols;
       +extern unsigned int rows;
       +extern unsigned int mouseshape;
       +extern unsigned int mousefg;
       +extern unsigned int mousebg;
       +extern unsigned int defaultattr;
       +extern MouseShortcut mshortcuts[];
       +extern size_t mshortcutslen;
       +extern Shortcut shortcuts[];
       +extern size_t shortcutslen;
       +extern uint forceselmod;
       +extern uint selmasks[];
       +extern size_t selmaskslen;
       +extern char ascii_printable[];
 (DIR) diff --git a/win.h b/win.h
       @@ -0,0 +1,29 @@
       +/* See LICENSE for license details. */
       +
       +/* X modifiers */
       +#define XK_ANY_MOD    UINT_MAX
       +#define XK_NO_MOD     0
       +#define XK_SWITCH_MOD (1<<13)
       +
       +typedef XftGlyphFontSpec GlyphFontSpec;
       +
       +void draw(void);
       +void drawregion(int, int, int, int);
       +void run(void);
       +
       +void xbell(int);
       +void xclipcopy(void);
       +void xclippaste(void);
       +void xhints(void);
       +void xinit(void);
       +void xloadcols(void);
       +int xsetcolorname(int, const char *);
       +void xloadfonts(char *, double);
       +void xsetenv(void);
       +void xsettitle(char *);
       +void xsetpointermotion(int);
       +void xseturgency(int);
       +void xunloadfonts(void);
       +void xresize(int, int);
       +void xselpaste(void);
       +unsigned long xwinid(void);
 (DIR) diff --git a/x.c b/x.c
       @@ -0,0 +1,1766 @@
       +/* See LICENSE for license details. */
       +#include <errno.h>
       +#include <locale.h>
       +#include <signal.h>
       +#include <stdint.h>
       +#include <sys/select.h>
       +#include <time.h>
       +#include <unistd.h>
       +#include <libgen.h>
       +#include <X11/Xatom.h>
       +#include <X11/Xlib.h>
       +#include <X11/Xutil.h>
       +#include <X11/cursorfont.h>
       +#include <X11/keysym.h>
       +#include <X11/Xft/Xft.h>
       +#include <X11/XKBlib.h>
       +
       +#include "arg.h"
       +
       +#define Glyph Glyph_
       +#define Font Font_
       +
       +#include "win.h"
       +#include "st.h"
       +
       +/* XEMBED messages */
       +#define XEMBED_FOCUS_IN  4
       +#define XEMBED_FOCUS_OUT 5
       +
       +/* macros */
       +#define TRUERED(x)                (((x) & 0xff0000) >> 8)
       +#define TRUEGREEN(x)                (((x) & 0xff00))
       +#define TRUEBLUE(x)                (((x) & 0xff) << 8)
       +
       +typedef XftDraw *Draw;
       +typedef XftColor Color;
       +
       +/* Purely graphic info */
       +typedef struct {
       +        Display *dpy;
       +        Colormap cmap;
       +        Window win;
       +        Drawable buf;
       +        Atom xembed, wmdeletewin, netwmname, netwmpid;
       +        XIM xim;
       +        XIC xic;
       +        Draw draw;
       +        Visual *vis;
       +        XSetWindowAttributes attrs;
       +        int scr;
       +        int isfixed; /* is fixed geometry? */
       +        int l, t; /* left and top offset */
       +        int gm; /* geometry mask */
       +} XWindow;
       +
       +typedef struct {
       +        Atom xtarget;
       +} XSelection;
       +
       +/* Font structure */
       +typedef struct {
       +        int height;
       +        int width;
       +        int ascent;
       +        int descent;
       +        int badslant;
       +        int badweight;
       +        short lbearing;
       +        short rbearing;
       +        XftFont *match;
       +        FcFontSet *set;
       +        FcPattern *pattern;
       +} Font;
       +
       +/* Drawing Context */
       +typedef struct {
       +        Color *col;
       +        size_t collen;
       +        Font font, bfont, ifont, ibfont;
       +        GC gc;
       +} DC;
       +
       +static inline ushort sixd_to_16bit(int);
       +static int xmakeglyphfontspecs(XftGlyphFontSpec *, const Glyph *, int, int, int);
       +static void xdrawglyphfontspecs(const XftGlyphFontSpec *, Glyph, int, int, int);
       +static void xdrawglyph(Glyph, int, int);
       +static void xclear(int, int, int, int);
       +static void xdrawcursor(void);
       +static int xgeommasktogravity(int);
       +static int xloadfont(Font *, FcPattern *);
       +static void xsetsel(char *, Time);
       +static void xunloadfont(Font *);
       +
       +static void expose(XEvent *);
       +static void visibility(XEvent *);
       +static void unmap(XEvent *);
       +static void kpress(XEvent *);
       +static void cmessage(XEvent *);
       +static void resize(XEvent *);
       +static void focus(XEvent *);
       +static void brelease(XEvent *);
       +static void bpress(XEvent *);
       +static void bmotion(XEvent *);
       +static void propnotify(XEvent *);
       +static void selnotify(XEvent *);
       +static void selclear_(XEvent *);
       +static void selrequest(XEvent *);
       +
       +static void selcopy(Time);
       +static void getbuttoninfo(XEvent *);
       +static void mousereport(XEvent *);
       +
       +static void (*handler[LASTEvent])(XEvent *) = {
       +        [KeyPress] = kpress,
       +        [ClientMessage] = cmessage,
       +        [ConfigureNotify] = resize,
       +        [VisibilityNotify] = visibility,
       +        [UnmapNotify] = unmap,
       +        [Expose] = expose,
       +        [FocusIn] = focus,
       +        [FocusOut] = focus,
       +        [MotionNotify] = bmotion,
       +        [ButtonPress] = bpress,
       +        [ButtonRelease] = brelease,
       +/*
       + * Uncomment if you want the selection to disappear when you select something
       + * different in another window.
       + */
       +/*        [SelectionClear] = selclear_, */
       +        [SelectionNotify] = selnotify,
       +/*
       + * PropertyNotify is only turned on when there is some INCR transfer happening
       + * for the selection retrieval.
       + */
       +        [PropertyNotify] = propnotify,
       +        [SelectionRequest] = selrequest,
       +};
       +
       +/* Globals */
       +static DC dc;
       +static XWindow xw;
       +static XSelection xsel;
       +
       +/* Font Ring Cache */
       +enum {
       +        FRC_NORMAL,
       +        FRC_ITALIC,
       +        FRC_BOLD,
       +        FRC_ITALICBOLD
       +};
       +
       +typedef struct {
       +        XftFont *font;
       +        int flags;
       +        Rune unicodep;
       +} Fontcache;
       +
       +/* Fontcache is an array now. A new font will be appended to the array. */
       +static Fontcache frc[16];
       +static int frclen = 0;
       +
       +void
       +getbuttoninfo(XEvent *e)
       +{
       +        int type;
       +        uint state = e->xbutton.state & ~(Button1Mask | forceselmod);
       +
       +        sel.alt = IS_SET(MODE_ALTSCREEN);
       +
       +        sel.oe.x = x2col(e->xbutton.x);
       +        sel.oe.y = y2row(e->xbutton.y);
       +        selnormalize();
       +
       +        sel.type = SEL_REGULAR;
       +        for (type = 1; type < selmaskslen; ++type) {
       +                if (match(selmasks[type], state)) {
       +                        sel.type = type;
       +                        break;
       +                }
       +        }
       +}
       +
       +void
       +mousereport(XEvent *e)
       +{
       +        int x = x2col(e->xbutton.x), y = y2row(e->xbutton.y),
       +            button = e->xbutton.button, state = e->xbutton.state,
       +            len;
       +        char buf[40];
       +        static int ox, oy;
       +
       +        /* from urxvt */
       +        if (e->xbutton.type == MotionNotify) {
       +                if (x == ox && y == oy)
       +                        return;
       +                if (!IS_SET(MODE_MOUSEMOTION) && !IS_SET(MODE_MOUSEMANY))
       +                        return;
       +                /* MOUSE_MOTION: no reporting if no button is pressed */
       +                if (IS_SET(MODE_MOUSEMOTION) && oldbutton == 3)
       +                        return;
       +
       +                button = oldbutton + 32;
       +                ox = x;
       +                oy = y;
       +        } else {
       +                if (!IS_SET(MODE_MOUSESGR) && e->xbutton.type == ButtonRelease) {
       +                        button = 3;
       +                } else {
       +                        button -= Button1;
       +                        if (button >= 3)
       +                                button += 64 - 3;
       +                }
       +                if (e->xbutton.type == ButtonPress) {
       +                        oldbutton = button;
       +                        ox = x;
       +                        oy = y;
       +                } else if (e->xbutton.type == ButtonRelease) {
       +                        oldbutton = 3;
       +                        /* MODE_MOUSEX10: no button release reporting */
       +                        if (IS_SET(MODE_MOUSEX10))
       +                                return;
       +                        if (button == 64 || button == 65)
       +                                return;
       +                }
       +        }
       +
       +        if (!IS_SET(MODE_MOUSEX10)) {
       +                button += ((state & ShiftMask  ) ? 4  : 0)
       +                        + ((state & Mod4Mask   ) ? 8  : 0)
       +                        + ((state & ControlMask) ? 16 : 0);
       +        }
       +
       +        if (IS_SET(MODE_MOUSESGR)) {
       +                len = snprintf(buf, sizeof(buf), "\033[<%d;%d;%d%c",
       +                                button, x+1, y+1,
       +                                e->xbutton.type == ButtonRelease ? 'm' : 'M');
       +        } else if (x < 223 && y < 223) {
       +                len = snprintf(buf, sizeof(buf), "\033[M%c%c%c",
       +                                32+button, 32+x+1, 32+y+1);
       +        } else {
       +                return;
       +        }
       +
       +        ttywrite(buf, len);
       +}
       +
       +void
       +bpress(XEvent *e)
       +{
       +        struct timespec now;
       +        MouseShortcut *ms;
       +
       +        if (IS_SET(MODE_MOUSE) && !(e->xbutton.state & forceselmod)) {
       +                mousereport(e);
       +                return;
       +        }
       +
       +        for (ms = mshortcuts; ms < mshortcuts + mshortcutslen; ms++) {
       +                if (e->xbutton.button == ms->b
       +                                && match(ms->mask, e->xbutton.state)) {
       +                        ttysend(ms->s, strlen(ms->s));
       +                        return;
       +                }
       +        }
       +
       +        if (e->xbutton.button == Button1) {
       +                clock_gettime(CLOCK_MONOTONIC, &now);
       +
       +                /* Clear previous selection, logically and visually. */
       +                selclear_(NULL);
       +                sel.mode = SEL_EMPTY;
       +                sel.type = SEL_REGULAR;
       +                sel.oe.x = sel.ob.x = x2col(e->xbutton.x);
       +                sel.oe.y = sel.ob.y = y2row(e->xbutton.y);
       +
       +                /*
       +                 * If the user clicks below predefined timeouts specific
       +                 * snapping behaviour is exposed.
       +                 */
       +                if (TIMEDIFF(now, sel.tclick2) <= tripleclicktimeout) {
       +                        sel.snap = SNAP_LINE;
       +                } else if (TIMEDIFF(now, sel.tclick1) <= doubleclicktimeout) {
       +                        sel.snap = SNAP_WORD;
       +                } else {
       +                        sel.snap = 0;
       +                }
       +                selnormalize();
       +
       +                if (sel.snap != 0)
       +                        sel.mode = SEL_READY;
       +                tsetdirt(sel.nb.y, sel.ne.y);
       +                sel.tclick2 = sel.tclick1;
       +                sel.tclick1 = now;
       +        }
       +}
       +
       +void
       +selcopy(Time t)
       +{
       +        xsetsel(getsel(), t);
       +}
       +
       +void
       +propnotify(XEvent *e)
       +{
       +        XPropertyEvent *xpev;
       +        Atom clipboard = XInternAtom(xw.dpy, "CLIPBOARD", 0);
       +
       +        xpev = &e->xproperty;
       +        if (xpev->state == PropertyNewValue &&
       +                        (xpev->atom == XA_PRIMARY ||
       +                         xpev->atom == clipboard)) {
       +                selnotify(e);
       +        }
       +}
       +
       +void
       +selnotify(XEvent *e)
       +{
       +        ulong nitems, ofs, rem;
       +        int format;
       +        uchar *data, *last, *repl;
       +        Atom type, incratom, property;
       +
       +        incratom = XInternAtom(xw.dpy, "INCR", 0);
       +
       +        ofs = 0;
       +        if (e->type == SelectionNotify) {
       +                property = e->xselection.property;
       +        } else if(e->type == PropertyNotify) {
       +                property = e->xproperty.atom;
       +        } else {
       +                return;
       +        }
       +        if (property == None)
       +                return;
       +
       +        do {
       +                if (XGetWindowProperty(xw.dpy, xw.win, property, ofs,
       +                                        BUFSIZ/4, False, AnyPropertyType,
       +                                        &type, &format, &nitems, &rem,
       +                                        &data)) {
       +                        fprintf(stderr, "Clipboard allocation failed\n");
       +                        return;
       +                }
       +
       +                if (e->type == PropertyNotify && nitems == 0 && rem == 0) {
       +                        /*
       +                         * If there is some PropertyNotify with no data, then
       +                         * this is the signal of the selection owner that all
       +                         * data has been transferred. We won't need to receive
       +                         * PropertyNotify events anymore.
       +                         */
       +                        MODBIT(xw.attrs.event_mask, 0, PropertyChangeMask);
       +                        XChangeWindowAttributes(xw.dpy, xw.win, CWEventMask,
       +                                        &xw.attrs);
       +                }
       +
       +                if (type == incratom) {
       +                        /*
       +                         * Activate the PropertyNotify events so we receive
       +                         * when the selection owner does send us the next
       +                         * chunk of data.
       +                         */
       +                        MODBIT(xw.attrs.event_mask, 1, PropertyChangeMask);
       +                        XChangeWindowAttributes(xw.dpy, xw.win, CWEventMask,
       +                                        &xw.attrs);
       +
       +                        /*
       +                         * Deleting the property is the transfer start signal.
       +                         */
       +                        XDeleteProperty(xw.dpy, xw.win, (int)property);
       +                        continue;
       +                }
       +
       +                /*
       +                 * As seen in getsel:
       +                 * Line endings are inconsistent in the terminal and GUI world
       +                 * copy and pasting. When receiving some selection data,
       +                 * replace all '\n' with '\r'.
       +                 * FIXME: Fix the computer world.
       +                 */
       +                repl = data;
       +                last = data + nitems * format / 8;
       +                while ((repl = memchr(repl, '\n', last - repl))) {
       +                        *repl++ = '\r';
       +                }
       +
       +                if (IS_SET(MODE_BRCKTPASTE) && ofs == 0)
       +                        ttywrite("\033[200~", 6);
       +                ttysend((char *)data, nitems * format / 8);
       +                if (IS_SET(MODE_BRCKTPASTE) && rem == 0)
       +                        ttywrite("\033[201~", 6);
       +                XFree(data);
       +                /* number of 32-bit chunks returned */
       +                ofs += nitems * format / 32;
       +        } while (rem > 0);
       +
       +        /*
       +         * Deleting the property again tells the selection owner to send the
       +         * next data chunk in the property.
       +         */
       +        XDeleteProperty(xw.dpy, xw.win, (int)property);
       +}
       +
       +void
       +xselpaste(void)
       +{
       +        XConvertSelection(xw.dpy, XA_PRIMARY, xsel.xtarget, XA_PRIMARY,
       +                        xw.win, CurrentTime);
       +}
       +
       +void
       +xclipcopy(void)
       +{
       +        Atom clipboard;
       +
       +        if (sel.clipboard != NULL)
       +                free(sel.clipboard);
       +
       +        if (sel.primary != NULL) {
       +                sel.clipboard = xstrdup(sel.primary);
       +                clipboard = XInternAtom(xw.dpy, "CLIPBOARD", 0);
       +                XSetSelectionOwner(xw.dpy, clipboard, xw.win, CurrentTime);
       +        }
       +}
       +
       +void
       +xclippaste(void)
       +{
       +        Atom clipboard;
       +
       +        clipboard = XInternAtom(xw.dpy, "CLIPBOARD", 0);
       +        XConvertSelection(xw.dpy, clipboard, xsel.xtarget, clipboard,
       +                        xw.win, CurrentTime);
       +}
       +
       +void
       +selclear_(XEvent *e)
       +{
       +        selclear();
       +}
       +
       +void
       +selrequest(XEvent *e)
       +{
       +        XSelectionRequestEvent *xsre;
       +        XSelectionEvent xev;
       +        Atom xa_targets, string, clipboard;
       +        char *seltext;
       +
       +        xsre = (XSelectionRequestEvent *) e;
       +        xev.type = SelectionNotify;
       +        xev.requestor = xsre->requestor;
       +        xev.selection = xsre->selection;
       +        xev.target = xsre->target;
       +        xev.time = xsre->time;
       +        if (xsre->property == None)
       +                xsre->property = xsre->target;
       +
       +        /* reject */
       +        xev.property = None;
       +
       +        xa_targets = XInternAtom(xw.dpy, "TARGETS", 0);
       +        if (xsre->target == xa_targets) {
       +                /* respond with the supported type */
       +                string = xsel.xtarget;
       +                XChangeProperty(xsre->display, xsre->requestor, xsre->property,
       +                                XA_ATOM, 32, PropModeReplace,
       +                                (uchar *) &string, 1);
       +                xev.property = xsre->property;
       +        } else if (xsre->target == xsel.xtarget || xsre->target == XA_STRING) {
       +                /*
       +                 * xith XA_STRING non ascii characters may be incorrect in the
       +                 * requestor. It is not our problem, use utf8.
       +                 */
       +                clipboard = XInternAtom(xw.dpy, "CLIPBOARD", 0);
       +                if (xsre->selection == XA_PRIMARY) {
       +                        seltext = sel.primary;
       +                } else if (xsre->selection == clipboard) {
       +                        seltext = sel.clipboard;
       +                } else {
       +                        fprintf(stderr,
       +                                "Unhandled clipboard selection 0x%lx\n",
       +                                xsre->selection);
       +                        return;
       +                }
       +                if (seltext != NULL) {
       +                        XChangeProperty(xsre->display, xsre->requestor,
       +                                        xsre->property, xsre->target,
       +                                        8, PropModeReplace,
       +                                        (uchar *)seltext, strlen(seltext));
       +                        xev.property = xsre->property;
       +                }
       +        }
       +
       +        /* all done, send a notification to the listener */
       +        if (!XSendEvent(xsre->display, xsre->requestor, 1, 0, (XEvent *) &xev))
       +                fprintf(stderr, "Error sending SelectionNotify event\n");
       +}
       +
       +void
       +xsetsel(char *str, Time t)
       +{
       +        free(sel.primary);
       +        sel.primary = str;
       +
       +        XSetSelectionOwner(xw.dpy, XA_PRIMARY, xw.win, t);
       +        if (XGetSelectionOwner(xw.dpy, XA_PRIMARY) != xw.win)
       +                selclear_(NULL);
       +}
       +
       +void
       +brelease(XEvent *e)
       +{
       +        if (IS_SET(MODE_MOUSE) && !(e->xbutton.state & forceselmod)) {
       +                mousereport(e);
       +                return;
       +        }
       +
       +        if (e->xbutton.button == Button2) {
       +                xselpaste();
       +        } else if (e->xbutton.button == Button1) {
       +                if (sel.mode == SEL_READY) {
       +                        getbuttoninfo(e);
       +                        selcopy(e->xbutton.time);
       +                } else
       +                        selclear_(NULL);
       +                sel.mode = SEL_IDLE;
       +                tsetdirt(sel.nb.y, sel.ne.y);
       +        }
       +}
       +
       +void
       +bmotion(XEvent *e)
       +{
       +        int oldey, oldex, oldsby, oldsey;
       +
       +        if (IS_SET(MODE_MOUSE) && !(e->xbutton.state & forceselmod)) {
       +                mousereport(e);
       +                return;
       +        }
       +
       +        if (!sel.mode)
       +                return;
       +
       +        sel.mode = SEL_READY;
       +        oldey = sel.oe.y;
       +        oldex = sel.oe.x;
       +        oldsby = sel.nb.y;
       +        oldsey = sel.ne.y;
       +        getbuttoninfo(e);
       +
       +        if (oldey != sel.oe.y || oldex != sel.oe.x)
       +                tsetdirt(MIN(sel.nb.y, oldsby), MAX(sel.ne.y, oldsey));
       +}
       +
       +void
       +xresize(int col, int row)
       +{
       +        win.tw = MAX(1, col * win.cw);
       +        win.th = MAX(1, row * win.ch);
       +
       +        XFreePixmap(xw.dpy, xw.buf);
       +        xw.buf = XCreatePixmap(xw.dpy, xw.win, win.w, win.h,
       +                        DefaultDepth(xw.dpy, xw.scr));
       +        XftDrawChange(xw.draw, xw.buf);
       +        xclear(0, 0, win.w, win.h);
       +}
       +
       +ushort
       +sixd_to_16bit(int x)
       +{
       +        return x == 0 ? 0 : 0x3737 + 0x2828 * x;
       +}
       +
       +int
       +xloadcolor(int i, const char *name, Color *ncolor)
       +{
       +        XRenderColor color = { .alpha = 0xffff };
       +
       +        if (!name) {
       +                if (BETWEEN(i, 16, 255)) { /* 256 color */
       +                        if (i < 6*6*6+16) { /* same colors as xterm */
       +                                color.red   = sixd_to_16bit( ((i-16)/36)%6 );
       +                                color.green = sixd_to_16bit( ((i-16)/6) %6 );
       +                                color.blue  = sixd_to_16bit( ((i-16)/1) %6 );
       +                        } else { /* greyscale */
       +                                color.red = 0x0808 + 0x0a0a * (i - (6*6*6+16));
       +                                color.green = color.blue = color.red;
       +                        }
       +                        return XftColorAllocValue(xw.dpy, xw.vis,
       +                                                  xw.cmap, &color, ncolor);
       +                } else
       +                        name = colorname[i];
       +        }
       +
       +        return XftColorAllocName(xw.dpy, xw.vis, xw.cmap, name, ncolor);
       +}
       +
       +void
       +xloadcols(void)
       +{
       +        int i;
       +        static int loaded;
       +        Color *cp;
       +
       +        dc.collen = MAX(colornamelen, 256);
       +        dc.col = xmalloc(dc.collen * sizeof(Color));
       +
       +        if (loaded) {
       +                for (cp = dc.col; cp < &dc.col[dc.collen]; ++cp)
       +                        XftColorFree(xw.dpy, xw.vis, xw.cmap, cp);
       +        }
       +
       +        for (i = 0; i < dc.collen; i++)
       +                if (!xloadcolor(i, NULL, &dc.col[i])) {
       +                        if (colorname[i])
       +                                die("Could not allocate color '%s'\n", colorname[i]);
       +                        else
       +                                die("Could not allocate color %d\n", i);
       +                }
       +        loaded = 1;
       +}
       +
       +int
       +xsetcolorname(int x, const char *name)
       +{
       +        Color ncolor;
       +
       +        if (!BETWEEN(x, 0, dc.collen))
       +                return 1;
       +
       +
       +        if (!xloadcolor(x, name, &ncolor))
       +                return 1;
       +
       +        XftColorFree(xw.dpy, xw.vis, xw.cmap, &dc.col[x]);
       +        dc.col[x] = ncolor;
       +
       +        return 0;
       +}
       +
       +/*
       + * Absolute coordinates.
       + */
       +void
       +xclear(int x1, int y1, int x2, int y2)
       +{
       +        XftDrawRect(xw.draw,
       +                        &dc.col[IS_SET(MODE_REVERSE)? defaultfg : defaultbg],
       +                        x1, y1, x2-x1, y2-y1);
       +}
       +
       +void
       +xhints(void)
       +{
       +        XClassHint class = {opt_name ? opt_name : termname,
       +                            opt_class ? opt_class : termname};
       +        XWMHints wm = {.flags = InputHint, .input = 1};
       +        XSizeHints *sizeh = NULL;
       +
       +        sizeh = XAllocSizeHints();
       +
       +        sizeh->flags = PSize | PResizeInc | PBaseSize;
       +        sizeh->height = win.h;
       +        sizeh->width = win.w;
       +        sizeh->height_inc = win.ch;
       +        sizeh->width_inc = win.cw;
       +        sizeh->base_height = 2 * borderpx;
       +        sizeh->base_width = 2 * borderpx;
       +        if (xw.isfixed) {
       +                sizeh->flags |= PMaxSize | PMinSize;
       +                sizeh->min_width = sizeh->max_width = win.w;
       +                sizeh->min_height = sizeh->max_height = win.h;
       +        }
       +        if (xw.gm & (XValue|YValue)) {
       +                sizeh->flags |= USPosition | PWinGravity;
       +                sizeh->x = xw.l;
       +                sizeh->y = xw.t;
       +                sizeh->win_gravity = xgeommasktogravity(xw.gm);
       +        }
       +
       +        XSetWMProperties(xw.dpy, xw.win, NULL, NULL, NULL, 0, sizeh, &wm,
       +                        &class);
       +        XFree(sizeh);
       +}
       +
       +int
       +xgeommasktogravity(int mask)
       +{
       +        switch (mask & (XNegative|YNegative)) {
       +        case 0:
       +                return NorthWestGravity;
       +        case XNegative:
       +                return NorthEastGravity;
       +        case YNegative:
       +                return SouthWestGravity;
       +        }
       +
       +        return SouthEastGravity;
       +}
       +
       +int
       +xloadfont(Font *f, FcPattern *pattern)
       +{
       +        FcPattern *configured;
       +        FcPattern *match;
       +        FcResult result;
       +        XGlyphInfo extents;
       +        int wantattr, haveattr;
       +
       +        /*
       +         * Manually configure instead of calling XftMatchFont
       +         * so that we can use the configured pattern for
       +         * "missing glyph" lookups.
       +         */
       +        configured = FcPatternDuplicate(pattern);
       +        if (!configured)
       +                return 1;
       +
       +        FcConfigSubstitute(NULL, configured, FcMatchPattern);
       +        XftDefaultSubstitute(xw.dpy, xw.scr, configured);
       +
       +        match = FcFontMatch(NULL, configured, &result);
       +        if (!match) {
       +                FcPatternDestroy(configured);
       +                return 1;
       +        }
       +
       +        if (!(f->match = XftFontOpenPattern(xw.dpy, match))) {
       +                FcPatternDestroy(configured);
       +                FcPatternDestroy(match);
       +                return 1;
       +        }
       +
       +        if ((XftPatternGetInteger(pattern, "slant", 0, &wantattr) ==
       +            XftResultMatch)) {
       +                /*
       +                 * Check if xft was unable to find a font with the appropriate
       +                 * slant but gave us one anyway. Try to mitigate.
       +                 */
       +                if ((XftPatternGetInteger(f->match->pattern, "slant", 0,
       +                    &haveattr) != XftResultMatch) || haveattr < wantattr) {
       +                        f->badslant = 1;
       +                        fputs("st: font slant does not match\n", stderr);
       +                }
       +        }
       +
       +        if ((XftPatternGetInteger(pattern, "weight", 0, &wantattr) ==
       +            XftResultMatch)) {
       +                if ((XftPatternGetInteger(f->match->pattern, "weight", 0,
       +                    &haveattr) != XftResultMatch) || haveattr != wantattr) {
       +                        f->badweight = 1;
       +                        fputs("st: font weight does not match\n", stderr);
       +                }
       +        }
       +
       +        XftTextExtentsUtf8(xw.dpy, f->match,
       +                (const FcChar8 *) ascii_printable,
       +                strlen(ascii_printable), &extents);
       +
       +        f->set = NULL;
       +        f->pattern = configured;
       +
       +        f->ascent = f->match->ascent;
       +        f->descent = f->match->descent;
       +        f->lbearing = 0;
       +        f->rbearing = f->match->max_advance_width;
       +
       +        f->height = f->ascent + f->descent;
       +        f->width = DIVCEIL(extents.xOff, strlen(ascii_printable));
       +
       +        return 0;
       +}
       +
       +void
       +xloadfonts(char *fontstr, double fontsize)
       +{
       +        FcPattern *pattern;
       +        double fontval;
       +        float ceilf(float);
       +
       +        if (fontstr[0] == '-') {
       +                pattern = XftXlfdParse(fontstr, False, False);
       +        } else {
       +                pattern = FcNameParse((FcChar8 *)fontstr);
       +        }
       +
       +        if (!pattern)
       +                die("st: can't open font %s\n", fontstr);
       +
       +        if (fontsize > 1) {
       +                FcPatternDel(pattern, FC_PIXEL_SIZE);
       +                FcPatternDel(pattern, FC_SIZE);
       +                FcPatternAddDouble(pattern, FC_PIXEL_SIZE, (double)fontsize);
       +                usedfontsize = fontsize;
       +        } else {
       +                if (FcPatternGetDouble(pattern, FC_PIXEL_SIZE, 0, &fontval) ==
       +                                FcResultMatch) {
       +                        usedfontsize = fontval;
       +                } else if (FcPatternGetDouble(pattern, FC_SIZE, 0, &fontval) ==
       +                                FcResultMatch) {
       +                        usedfontsize = -1;
       +                } else {
       +                        /*
       +                         * Default font size is 12, if none given. This is to
       +                         * have a known usedfontsize value.
       +                         */
       +                        FcPatternAddDouble(pattern, FC_PIXEL_SIZE, 12);
       +                        usedfontsize = 12;
       +                }
       +                defaultfontsize = usedfontsize;
       +        }
       +
       +        if (xloadfont(&dc.font, pattern))
       +                die("st: can't open font %s\n", fontstr);
       +
       +        if (usedfontsize < 0) {
       +                FcPatternGetDouble(dc.font.match->pattern,
       +                                   FC_PIXEL_SIZE, 0, &fontval);
       +                usedfontsize = fontval;
       +                if (fontsize == 0)
       +                        defaultfontsize = fontval;
       +        }
       +
       +        /* Setting character width and height. */
       +        win.cw = ceilf(dc.font.width * cwscale);
       +        win.ch = ceilf(dc.font.height * chscale);
       +
       +        FcPatternDel(pattern, FC_SLANT);
       +        FcPatternAddInteger(pattern, FC_SLANT, FC_SLANT_ITALIC);
       +        if (xloadfont(&dc.ifont, pattern))
       +                die("st: can't open font %s\n", fontstr);
       +
       +        FcPatternDel(pattern, FC_WEIGHT);
       +        FcPatternAddInteger(pattern, FC_WEIGHT, FC_WEIGHT_BOLD);
       +        if (xloadfont(&dc.ibfont, pattern))
       +                die("st: can't open font %s\n", fontstr);
       +
       +        FcPatternDel(pattern, FC_SLANT);
       +        FcPatternAddInteger(pattern, FC_SLANT, FC_SLANT_ROMAN);
       +        if (xloadfont(&dc.bfont, pattern))
       +                die("st: can't open font %s\n", fontstr);
       +
       +        FcPatternDestroy(pattern);
       +}
       +
       +void
       +xunloadfont(Font *f)
       +{
       +        XftFontClose(xw.dpy, f->match);
       +        FcPatternDestroy(f->pattern);
       +        if (f->set)
       +                FcFontSetDestroy(f->set);
       +}
       +
       +void
       +xunloadfonts(void)
       +{
       +        /* Free the loaded fonts in the font cache.  */
       +        while (frclen > 0)
       +                XftFontClose(xw.dpy, frc[--frclen].font);
       +
       +        xunloadfont(&dc.font);
       +        xunloadfont(&dc.bfont);
       +        xunloadfont(&dc.ifont);
       +        xunloadfont(&dc.ibfont);
       +}
       +
       +void
       +xinit(void)
       +{
       +        XGCValues gcvalues;
       +        Cursor cursor;
       +        Window parent;
       +        pid_t thispid = getpid();
       +        XColor xmousefg, xmousebg;
       +
       +        if (!(xw.dpy = XOpenDisplay(NULL)))
       +                die("Can't open display\n");
       +        xw.scr = XDefaultScreen(xw.dpy);
       +        xw.vis = XDefaultVisual(xw.dpy, xw.scr);
       +
       +        /* font */
       +        if (!FcInit())
       +                die("Could not init fontconfig.\n");
       +
       +        usedfont = (opt_font == NULL)? font : opt_font;
       +        xloadfonts(usedfont, 0);
       +
       +        /* colors */
       +        xw.cmap = XDefaultColormap(xw.dpy, xw.scr);
       +        xloadcols();
       +
       +        /* adjust fixed window geometry */
       +        win.w = 2 * borderpx + term.col * win.cw;
       +        win.h = 2 * borderpx + term.row * win.ch;
       +        if (xw.gm & XNegative)
       +                xw.l += DisplayWidth(xw.dpy, xw.scr) - win.w - 2;
       +        if (xw.gm & YNegative)
       +                xw.t += DisplayHeight(xw.dpy, xw.scr) - win.h - 2;
       +
       +        /* Events */
       +        xw.attrs.background_pixel = dc.col[defaultbg].pixel;
       +        xw.attrs.border_pixel = dc.col[defaultbg].pixel;
       +        xw.attrs.bit_gravity = NorthWestGravity;
       +        xw.attrs.event_mask = FocusChangeMask | KeyPressMask
       +                | ExposureMask | VisibilityChangeMask | StructureNotifyMask
       +                | ButtonMotionMask | ButtonPressMask | ButtonReleaseMask;
       +        xw.attrs.colormap = xw.cmap;
       +
       +        if (!(opt_embed && (parent = strtol(opt_embed, NULL, 0))))
       +                parent = XRootWindow(xw.dpy, xw.scr);
       +        xw.win = XCreateWindow(xw.dpy, parent, xw.l, xw.t,
       +                        win.w, win.h, 0, XDefaultDepth(xw.dpy, xw.scr), InputOutput,
       +                        xw.vis, CWBackPixel | CWBorderPixel | CWBitGravity
       +                        | CWEventMask | CWColormap, &xw.attrs);
       +
       +        memset(&gcvalues, 0, sizeof(gcvalues));
       +        gcvalues.graphics_exposures = False;
       +        dc.gc = XCreateGC(xw.dpy, parent, GCGraphicsExposures,
       +                        &gcvalues);
       +        xw.buf = XCreatePixmap(xw.dpy, xw.win, win.w, win.h,
       +                        DefaultDepth(xw.dpy, xw.scr));
       +        XSetForeground(xw.dpy, dc.gc, dc.col[defaultbg].pixel);
       +        XFillRectangle(xw.dpy, xw.buf, dc.gc, 0, 0, win.w, win.h);
       +
       +        /* Xft rendering context */
       +        xw.draw = XftDrawCreate(xw.dpy, xw.buf, xw.vis, xw.cmap);
       +
       +        /* input methods */
       +        if ((xw.xim = XOpenIM(xw.dpy, NULL, NULL, NULL)) == NULL) {
       +                XSetLocaleModifiers("@im=local");
       +                if ((xw.xim =  XOpenIM(xw.dpy, NULL, NULL, NULL)) == NULL) {
       +                        XSetLocaleModifiers("@im=");
       +                        if ((xw.xim = XOpenIM(xw.dpy,
       +                                        NULL, NULL, NULL)) == NULL) {
       +                                die("XOpenIM failed. Could not open input"
       +                                        " device.\n");
       +                        }
       +                }
       +        }
       +        xw.xic = XCreateIC(xw.xim, XNInputStyle, XIMPreeditNothing
       +                                           | XIMStatusNothing, XNClientWindow, xw.win,
       +                                           XNFocusWindow, xw.win, NULL);
       +        if (xw.xic == NULL)
       +                die("XCreateIC failed. Could not obtain input method.\n");
       +
       +        /* white cursor, black outline */
       +        cursor = XCreateFontCursor(xw.dpy, mouseshape);
       +        XDefineCursor(xw.dpy, xw.win, cursor);
       +
       +        if (XParseColor(xw.dpy, xw.cmap, colorname[mousefg], &xmousefg) == 0) {
       +                xmousefg.red   = 0xffff;
       +                xmousefg.green = 0xffff;
       +                xmousefg.blue  = 0xffff;
       +        }
       +
       +        if (XParseColor(xw.dpy, xw.cmap, colorname[mousebg], &xmousebg) == 0) {
       +                xmousebg.red   = 0x0000;
       +                xmousebg.green = 0x0000;
       +                xmousebg.blue  = 0x0000;
       +        }
       +
       +        XRecolorCursor(xw.dpy, cursor, &xmousefg, &xmousebg);
       +
       +        xw.xembed = XInternAtom(xw.dpy, "_XEMBED", False);
       +        xw.wmdeletewin = XInternAtom(xw.dpy, "WM_DELETE_WINDOW", False);
       +        xw.netwmname = XInternAtom(xw.dpy, "_NET_WM_NAME", False);
       +        XSetWMProtocols(xw.dpy, xw.win, &xw.wmdeletewin, 1);
       +
       +        xw.netwmpid = XInternAtom(xw.dpy, "_NET_WM_PID", False);
       +        XChangeProperty(xw.dpy, xw.win, xw.netwmpid, XA_CARDINAL, 32,
       +                        PropModeReplace, (uchar *)&thispid, 1);
       +
       +        resettitle();
       +        XMapWindow(xw.dpy, xw.win);
       +        xhints();
       +        XSync(xw.dpy, False);
       +
       +        xsel.xtarget = XInternAtom(xw.dpy, "UTF8_STRING", 0);
       +        if (xsel.xtarget == None)
       +                xsel.xtarget = XA_STRING;
       +}
       +
       +int
       +xmakeglyphfontspecs(XftGlyphFontSpec *specs, const Glyph *glyphs, int len, int x, int y)
       +{
       +        float winx = borderpx + x * win.cw, winy = borderpx + y * win.ch, xp, yp;
       +        ushort mode, prevmode = USHRT_MAX;
       +        Font *font = &dc.font;
       +        int frcflags = FRC_NORMAL;
       +        float runewidth = win.cw;
       +        Rune rune;
       +        FT_UInt glyphidx;
       +        FcResult fcres;
       +        FcPattern *fcpattern, *fontpattern;
       +        FcFontSet *fcsets[] = { NULL };
       +        FcCharSet *fccharset;
       +        int i, f, numspecs = 0;
       +
       +        for (i = 0, xp = winx, yp = winy + font->ascent; i < len; ++i) {
       +                /* Fetch rune and mode for current glyph. */
       +                rune = glyphs[i].u;
       +                mode = glyphs[i].mode;
       +
       +                /* Skip dummy wide-character spacing. */
       +                if (mode == ATTR_WDUMMY)
       +                        continue;
       +
       +                /* Determine font for glyph if different from previous glyph. */
       +                if (prevmode != mode) {
       +                        prevmode = mode;
       +                        font = &dc.font;
       +                        frcflags = FRC_NORMAL;
       +                        runewidth = win.cw * ((mode & ATTR_WIDE) ? 2.0f : 1.0f);
       +                        if ((mode & ATTR_ITALIC) && (mode & ATTR_BOLD)) {
       +                                font = &dc.ibfont;
       +                                frcflags = FRC_ITALICBOLD;
       +                        } else if (mode & ATTR_ITALIC) {
       +                                font = &dc.ifont;
       +                                frcflags = FRC_ITALIC;
       +                        } else if (mode & ATTR_BOLD) {
       +                                font = &dc.bfont;
       +                                frcflags = FRC_BOLD;
       +                        }
       +                        yp = winy + font->ascent;
       +                }
       +
       +                /* Lookup character index with default font. */
       +                glyphidx = XftCharIndex(xw.dpy, font->match, rune);
       +                if (glyphidx) {
       +                        specs[numspecs].font = font->match;
       +                        specs[numspecs].glyph = glyphidx;
       +                        specs[numspecs].x = (short)xp;
       +                        specs[numspecs].y = (short)yp;
       +                        xp += runewidth;
       +                        numspecs++;
       +                        continue;
       +                }
       +
       +                /* Fallback on font cache, search the font cache for match. */
       +                for (f = 0; f < frclen; f++) {
       +                        glyphidx = XftCharIndex(xw.dpy, frc[f].font, rune);
       +                        /* Everything correct. */
       +                        if (glyphidx && frc[f].flags == frcflags)
       +                                break;
       +                        /* We got a default font for a not found glyph. */
       +                        if (!glyphidx && frc[f].flags == frcflags
       +                                        && frc[f].unicodep == rune) {
       +                                break;
       +                        }
       +                }
       +
       +                /* Nothing was found. Use fontconfig to find matching font. */
       +                if (f >= frclen) {
       +                        if (!font->set)
       +                                font->set = FcFontSort(0, font->pattern,
       +                                                       1, 0, &fcres);
       +                        fcsets[0] = font->set;
       +
       +                        /*
       +                         * Nothing was found in the cache. Now use
       +                         * some dozen of Fontconfig calls to get the
       +                         * font for one single character.
       +                         *
       +                         * Xft and fontconfig are design failures.
       +                         */
       +                        fcpattern = FcPatternDuplicate(font->pattern);
       +                        fccharset = FcCharSetCreate();
       +
       +                        FcCharSetAddChar(fccharset, rune);
       +                        FcPatternAddCharSet(fcpattern, FC_CHARSET,
       +                                        fccharset);
       +                        FcPatternAddBool(fcpattern, FC_SCALABLE, 1);
       +
       +                        FcConfigSubstitute(0, fcpattern,
       +                                        FcMatchPattern);
       +                        FcDefaultSubstitute(fcpattern);
       +
       +                        fontpattern = FcFontSetMatch(0, fcsets, 1,
       +                                        fcpattern, &fcres);
       +
       +                        /*
       +                         * Overwrite or create the new cache entry.
       +                         */
       +                        if (frclen >= LEN(frc)) {
       +                                frclen = LEN(frc) - 1;
       +                                XftFontClose(xw.dpy, frc[frclen].font);
       +                                frc[frclen].unicodep = 0;
       +                        }
       +
       +                        frc[frclen].font = XftFontOpenPattern(xw.dpy,
       +                                        fontpattern);
       +                        frc[frclen].flags = frcflags;
       +                        frc[frclen].unicodep = rune;
       +
       +                        glyphidx = XftCharIndex(xw.dpy, frc[frclen].font, rune);
       +
       +                        f = frclen;
       +                        frclen++;
       +
       +                        FcPatternDestroy(fcpattern);
       +                        FcCharSetDestroy(fccharset);
       +                }
       +
       +                specs[numspecs].font = frc[f].font;
       +                specs[numspecs].glyph = glyphidx;
       +                specs[numspecs].x = (short)xp;
       +                specs[numspecs].y = (short)yp;
       +                xp += runewidth;
       +                numspecs++;
       +        }
       +
       +        return numspecs;
       +}
       +
       +void
       +xdrawglyphfontspecs(const XftGlyphFontSpec *specs, Glyph base, int len, int x, int y)
       +{
       +        int charlen = len * ((base.mode & ATTR_WIDE) ? 2 : 1);
       +        int winx = borderpx + x * win.cw, winy = borderpx + y * win.ch,
       +            width = charlen * win.cw;
       +        Color *fg, *bg, *temp, revfg, revbg, truefg, truebg;
       +        XRenderColor colfg, colbg;
       +        XRectangle r;
       +
       +        /* Fallback on color display for attributes not supported by the font */
       +        if (base.mode & ATTR_ITALIC && base.mode & ATTR_BOLD) {
       +                if (dc.ibfont.badslant || dc.ibfont.badweight)
       +                        base.fg = defaultattr;
       +        } else if ((base.mode & ATTR_ITALIC && dc.ifont.badslant) ||
       +            (base.mode & ATTR_BOLD && dc.bfont.badweight)) {
       +                base.fg = defaultattr;
       +        }
       +
       +        if (IS_TRUECOL(base.fg)) {
       +                colfg.alpha = 0xffff;
       +                colfg.red = TRUERED(base.fg);
       +                colfg.green = TRUEGREEN(base.fg);
       +                colfg.blue = TRUEBLUE(base.fg);
       +                XftColorAllocValue(xw.dpy, xw.vis, xw.cmap, &colfg, &truefg);
       +                fg = &truefg;
       +        } else {
       +                fg = &dc.col[base.fg];
       +        }
       +
       +        if (IS_TRUECOL(base.bg)) {
       +                colbg.alpha = 0xffff;
       +                colbg.green = TRUEGREEN(base.bg);
       +                colbg.red = TRUERED(base.bg);
       +                colbg.blue = TRUEBLUE(base.bg);
       +                XftColorAllocValue(xw.dpy, xw.vis, xw.cmap, &colbg, &truebg);
       +                bg = &truebg;
       +        } else {
       +                bg = &dc.col[base.bg];
       +        }
       +
       +        /* Change basic system colors [0-7] to bright system colors [8-15] */
       +        if ((base.mode & ATTR_BOLD_FAINT) == ATTR_BOLD && BETWEEN(base.fg, 0, 7))
       +                fg = &dc.col[base.fg + 8];
       +
       +        if (IS_SET(MODE_REVERSE)) {
       +                if (fg == &dc.col[defaultfg]) {
       +                        fg = &dc.col[defaultbg];
       +                } else {
       +                        colfg.red = ~fg->color.red;
       +                        colfg.green = ~fg->color.green;
       +                        colfg.blue = ~fg->color.blue;
       +                        colfg.alpha = fg->color.alpha;
       +                        XftColorAllocValue(xw.dpy, xw.vis, xw.cmap, &colfg,
       +                                        &revfg);
       +                        fg = &revfg;
       +                }
       +
       +                if (bg == &dc.col[defaultbg]) {
       +                        bg = &dc.col[defaultfg];
       +                } else {
       +                        colbg.red = ~bg->color.red;
       +                        colbg.green = ~bg->color.green;
       +                        colbg.blue = ~bg->color.blue;
       +                        colbg.alpha = bg->color.alpha;
       +                        XftColorAllocValue(xw.dpy, xw.vis, xw.cmap, &colbg,
       +                                        &revbg);
       +                        bg = &revbg;
       +                }
       +        }
       +
       +        if (base.mode & ATTR_REVERSE) {
       +                temp = fg;
       +                fg = bg;
       +                bg = temp;
       +        }
       +
       +        if ((base.mode & ATTR_BOLD_FAINT) == ATTR_FAINT) {
       +                colfg.red = fg->color.red / 2;
       +                colfg.green = fg->color.green / 2;
       +                colfg.blue = fg->color.blue / 2;
       +                XftColorAllocValue(xw.dpy, xw.vis, xw.cmap, &colfg, &revfg);
       +                fg = &revfg;
       +        }
       +
       +        if (base.mode & ATTR_BLINK && term.mode & MODE_BLINK)
       +                fg = bg;
       +
       +        if (base.mode & ATTR_INVISIBLE)
       +                fg = bg;
       +
       +        /* Intelligent cleaning up of the borders. */
       +        if (x == 0) {
       +                xclear(0, (y == 0)? 0 : winy, borderpx,
       +                        winy + win.ch + ((y >= term.row-1)? win.h : 0));
       +        }
       +        if (x + charlen >= term.col) {
       +                xclear(winx + width, (y == 0)? 0 : winy, win.w,
       +                        ((y >= term.row-1)? win.h : (winy + win.ch)));
       +        }
       +        if (y == 0)
       +                xclear(winx, 0, winx + width, borderpx);
       +        if (y == term.row-1)
       +                xclear(winx, winy + win.ch, winx + width, win.h);
       +
       +        /* Clean up the region we want to draw to. */
       +        XftDrawRect(xw.draw, bg, winx, winy, width, win.ch);
       +
       +        /* Set the clip region because Xft is sometimes dirty. */
       +        r.x = 0;
       +        r.y = 0;
       +        r.height = win.ch;
       +        r.width = width;
       +        XftDrawSetClipRectangles(xw.draw, winx, winy, &r, 1);
       +
       +        /* Render the glyphs. */
       +        XftDrawGlyphFontSpec(xw.draw, fg, specs, len);
       +
       +        /* Render underline and strikethrough. */
       +        if (base.mode & ATTR_UNDERLINE) {
       +                XftDrawRect(xw.draw, fg, winx, winy + dc.font.ascent + 1,
       +                                width, 1);
       +        }
       +
       +        if (base.mode & ATTR_STRUCK) {
       +                XftDrawRect(xw.draw, fg, winx, winy + 2 * dc.font.ascent / 3,
       +                                width, 1);
       +        }
       +
       +        /* Reset clip to none. */
       +        XftDrawSetClip(xw.draw, 0);
       +}
       +
       +void
       +xdrawglyph(Glyph g, int x, int y)
       +{
       +        int numspecs;
       +        XftGlyphFontSpec spec;
       +
       +        numspecs = xmakeglyphfontspecs(&spec, &g, 1, x, y);
       +        xdrawglyphfontspecs(&spec, g, numspecs, x, y);
       +}
       +
       +void
       +xdrawcursor(void)
       +{
       +        static int oldx = 0, oldy = 0;
       +        int curx;
       +        Glyph g = {' ', ATTR_NULL, defaultbg, defaultcs}, og;
       +        int ena_sel = sel.ob.x != -1 && sel.alt == IS_SET(MODE_ALTSCREEN);
       +        Color drawcol;
       +
       +        LIMIT(oldx, 0, term.col-1);
       +        LIMIT(oldy, 0, term.row-1);
       +
       +        curx = term.c.x;
       +
       +        /* adjust position if in dummy */
       +        if (term.line[oldy][oldx].mode & ATTR_WDUMMY)
       +                oldx--;
       +        if (term.line[term.c.y][curx].mode & ATTR_WDUMMY)
       +                curx--;
       +
       +        /* remove the old cursor */
       +        og = term.line[oldy][oldx];
       +        if (ena_sel && selected(oldx, oldy))
       +                og.mode ^= ATTR_REVERSE;
       +        xdrawglyph(og, oldx, oldy);
       +
       +        g.u = term.line[term.c.y][term.c.x].u;
       +
       +        /*
       +         * Select the right color for the right mode.
       +         */
       +        if (IS_SET(MODE_REVERSE)) {
       +                g.mode |= ATTR_REVERSE;
       +                g.bg = defaultfg;
       +                if (ena_sel && selected(term.c.x, term.c.y)) {
       +                        drawcol = dc.col[defaultcs];
       +                        g.fg = defaultrcs;
       +                } else {
       +                        drawcol = dc.col[defaultrcs];
       +                        g.fg = defaultcs;
       +                }
       +        } else {
       +                if (ena_sel && selected(term.c.x, term.c.y)) {
       +                        drawcol = dc.col[defaultrcs];
       +                        g.fg = defaultfg;
       +                        g.bg = defaultrcs;
       +                } else {
       +                        drawcol = dc.col[defaultcs];
       +                }
       +        }
       +
       +        if (IS_SET(MODE_HIDE))
       +                return;
       +
       +        /* draw the new one */
       +        if (win.state & WIN_FOCUSED) {
       +                switch (win.cursor) {
       +                case 7: /* st extension: snowman */
       +                        utf8decode("☃", &g.u, UTF_SIZ);
       +                case 0: /* Blinking Block */
       +                case 1: /* Blinking Block (Default) */
       +                case 2: /* Steady Block */
       +                        g.mode |= term.line[term.c.y][curx].mode & ATTR_WIDE;
       +                        xdrawglyph(g, term.c.x, term.c.y);
       +                        break;
       +                case 3: /* Blinking Underline */
       +                case 4: /* Steady Underline */
       +                        XftDrawRect(xw.draw, &drawcol,
       +                                        borderpx + curx * win.cw,
       +                                        borderpx + (term.c.y + 1) * win.ch - \
       +                                                cursorthickness,
       +                                        win.cw, cursorthickness);
       +                        break;
       +                case 5: /* Blinking bar */
       +                case 6: /* Steady bar */
       +                        XftDrawRect(xw.draw, &drawcol,
       +                                        borderpx + curx * win.cw,
       +                                        borderpx + term.c.y * win.ch,
       +                                        cursorthickness, win.ch);
       +                        break;
       +                }
       +        } else {
       +                XftDrawRect(xw.draw, &drawcol,
       +                                borderpx + curx * win.cw,
       +                                borderpx + term.c.y * win.ch,
       +                                win.cw - 1, 1);
       +                XftDrawRect(xw.draw, &drawcol,
       +                                borderpx + curx * win.cw,
       +                                borderpx + term.c.y * win.ch,
       +                                1, win.ch - 1);
       +                XftDrawRect(xw.draw, &drawcol,
       +                                borderpx + (curx + 1) * win.cw - 1,
       +                                borderpx + term.c.y * win.ch,
       +                                1, win.ch - 1);
       +                XftDrawRect(xw.draw, &drawcol,
       +                                borderpx + curx * win.cw,
       +                                borderpx + (term.c.y + 1) * win.ch - 1,
       +                                win.cw, 1);
       +        }
       +        oldx = curx, oldy = term.c.y;
       +}
       +
       +void
       +xsetenv(void)
       +{
       +        char buf[sizeof(long) * 8 + 1];
       +
       +        snprintf(buf, sizeof(buf), "%lu", xw.win);
       +        setenv("WINDOWID", buf, 1);
       +}
       +
       +void
       +xsettitle(char *p)
       +{
       +        XTextProperty prop;
       +
       +        Xutf8TextListToTextProperty(xw.dpy, &p, 1, XUTF8StringStyle,
       +                        &prop);
       +        XSetWMName(xw.dpy, xw.win, &prop);
       +        XSetTextProperty(xw.dpy, xw.win, &prop, xw.netwmname);
       +        XFree(prop.value);
       +}
       +
       +void
       +draw(void)
       +{
       +        drawregion(0, 0, term.col, term.row);
       +        XCopyArea(xw.dpy, xw.buf, xw.win, dc.gc, 0, 0, win.w,
       +                        win.h, 0, 0);
       +        XSetForeground(xw.dpy, dc.gc,
       +                        dc.col[IS_SET(MODE_REVERSE)?
       +                                defaultfg : defaultbg].pixel);
       +}
       +
       +void
       +drawregion(int x1, int y1, int x2, int y2)
       +{
       +        int i, x, y, ox, numspecs;
       +        Glyph base, new;
       +        XftGlyphFontSpec *specs;
       +        int ena_sel = sel.ob.x != -1 && sel.alt == IS_SET(MODE_ALTSCREEN);
       +
       +        if (!(win.state & WIN_VISIBLE))
       +                return;
       +
       +        for (y = y1; y < y2; y++) {
       +                if (!term.dirty[y])
       +                        continue;
       +
       +                term.dirty[y] = 0;
       +
       +                specs = term.specbuf;
       +                numspecs = xmakeglyphfontspecs(specs, &term.line[y][x1], x2 - x1, x1, y);
       +
       +                i = ox = 0;
       +                for (x = x1; x < x2 && i < numspecs; x++) {
       +                        new = term.line[y][x];
       +                        if (new.mode == ATTR_WDUMMY)
       +                                continue;
       +                        if (ena_sel && selected(x, y))
       +                                new.mode ^= ATTR_REVERSE;
       +                        if (i > 0 && ATTRCMP(base, new)) {
       +                                xdrawglyphfontspecs(specs, base, i, ox, y);
       +                                specs += i;
       +                                numspecs -= i;
       +                                i = 0;
       +                        }
       +                        if (i == 0) {
       +                                ox = x;
       +                                base = new;
       +                        }
       +                        i++;
       +                }
       +                if (i > 0)
       +                        xdrawglyphfontspecs(specs, base, i, ox, y);
       +        }
       +        xdrawcursor();
       +}
       +
       +void
       +expose(XEvent *ev)
       +{
       +        redraw();
       +}
       +
       +void
       +visibility(XEvent *ev)
       +{
       +        XVisibilityEvent *e = &ev->xvisibility;
       +
       +        MODBIT(win.state, e->state != VisibilityFullyObscured, WIN_VISIBLE);
       +}
       +
       +void
       +unmap(XEvent *ev)
       +{
       +        win.state &= ~WIN_VISIBLE;
       +}
       +
       +void
       +xsetpointermotion(int set)
       +{
       +        MODBIT(xw.attrs.event_mask, set, PointerMotionMask);
       +        XChangeWindowAttributes(xw.dpy, xw.win, CWEventMask, &xw.attrs);
       +}
       +
       +void
       +xseturgency(int add)
       +{
       +        XWMHints *h = XGetWMHints(xw.dpy, xw.win);
       +
       +        MODBIT(h->flags, add, XUrgencyHint);
       +        XSetWMHints(xw.dpy, xw.win, h);
       +        XFree(h);
       +}
       +
       +void
       +xbell(int vol)
       +{
       +        XkbBell(xw.dpy, xw.win, vol, (Atom)NULL);
       +}
       +
       +unsigned long
       +xwinid(void)
       +{
       +        return xw.win;
       +}
       +
       +void
       +focus(XEvent *ev)
       +{
       +        XFocusChangeEvent *e = &ev->xfocus;
       +
       +        if (e->mode == NotifyGrab)
       +                return;
       +
       +        if (ev->type == FocusIn) {
       +                XSetICFocus(xw.xic);
       +                win.state |= WIN_FOCUSED;
       +                xseturgency(0);
       +                if (IS_SET(MODE_FOCUS))
       +                        ttywrite("\033[I", 3);
       +        } else {
       +                XUnsetICFocus(xw.xic);
       +                win.state &= ~WIN_FOCUSED;
       +                if (IS_SET(MODE_FOCUS))
       +                        ttywrite("\033[O", 3);
       +        }
       +}
       +
       +void
       +kpress(XEvent *ev)
       +{
       +        XKeyEvent *e = &ev->xkey;
       +        KeySym ksym;
       +        char buf[32], *customkey;
       +        int len;
       +        Rune c;
       +        Status status;
       +        Shortcut *bp;
       +
       +        if (IS_SET(MODE_KBDLOCK))
       +                return;
       +
       +        len = XmbLookupString(xw.xic, e, buf, sizeof buf, &ksym, &status);
       +        /* 1. shortcuts */
       +        for (bp = shortcuts; bp < shortcuts + shortcutslen; bp++) {
       +                if (ksym == bp->keysym && match(bp->mod, e->state)) {
       +                        bp->func(&(bp->arg));
       +                        return;
       +                }
       +        }
       +
       +        /* 2. custom keys from config.h */
       +        if ((customkey = kmap(ksym, e->state))) {
       +                ttysend(customkey, strlen(customkey));
       +                return;
       +        }
       +
       +        /* 3. composed string from input method */
       +        if (len == 0)
       +                return;
       +        if (len == 1 && e->state & Mod1Mask) {
       +                if (IS_SET(MODE_8BIT)) {
       +                        if (*buf < 0177) {
       +                                c = *buf | 0x80;
       +                                len = utf8encode(c, buf);
       +                        }
       +                } else {
       +                        buf[1] = buf[0];
       +                        buf[0] = '\033';
       +                        len = 2;
       +                }
       +        }
       +        ttysend(buf, len);
       +}
       +
       +
       +void
       +cmessage(XEvent *e)
       +{
       +        /*
       +         * See xembed specs
       +         *  http://standards.freedesktop.org/xembed-spec/xembed-spec-latest.html
       +         */
       +        if (e->xclient.message_type == xw.xembed && e->xclient.format == 32) {
       +                if (e->xclient.data.l[1] == XEMBED_FOCUS_IN) {
       +                        win.state |= WIN_FOCUSED;
       +                        xseturgency(0);
       +                } else if (e->xclient.data.l[1] == XEMBED_FOCUS_OUT) {
       +                        win.state &= ~WIN_FOCUSED;
       +                }
       +        } else if (e->xclient.data.l[0] == xw.wmdeletewin) {
       +                /* Send SIGHUP to shell */
       +                kill(pid, SIGHUP);
       +                exit(0);
       +        }
       +}
       +
       +void
       +resize(XEvent *e)
       +{
       +        if (e->xconfigure.width == win.w && e->xconfigure.height == win.h)
       +                return;
       +
       +        cresize(e->xconfigure.width, e->xconfigure.height);
       +        ttyresize();
       +}
       +
       +void
       +run(void)
       +{
       +        XEvent ev;
       +        int w = win.w, h = win.h;
       +        fd_set rfd;
       +        int xfd = XConnectionNumber(xw.dpy), xev, blinkset = 0, dodraw = 0;
       +        struct timespec drawtimeout, *tv = NULL, now, last, lastblink;
       +        long deltatime;
       +
       +        /* Waiting for window mapping */
       +        do {
       +                XNextEvent(xw.dpy, &ev);
       +                /*
       +                 * This XFilterEvent call is required because of XOpenIM. It
       +                 * does filter out the key event and some client message for
       +                 * the input method too.
       +                 */
       +                if (XFilterEvent(&ev, None))
       +                        continue;
       +                if (ev.type == ConfigureNotify) {
       +                        w = ev.xconfigure.width;
       +                        h = ev.xconfigure.height;
       +                }
       +        } while (ev.type != MapNotify);
       +
       +        cresize(w, h);
       +        ttynew();
       +        ttyresize();
       +
       +        clock_gettime(CLOCK_MONOTONIC, &last);
       +        lastblink = last;
       +
       +        for (xev = actionfps;;) {
       +                FD_ZERO(&rfd);
       +                FD_SET(cmdfd, &rfd);
       +                FD_SET(xfd, &rfd);
       +
       +                if (pselect(MAX(xfd, cmdfd)+1, &rfd, NULL, NULL, tv, NULL) < 0) {
       +                        if (errno == EINTR)
       +                                continue;
       +                        die("select failed: %s\n", strerror(errno));
       +                }
       +                if (FD_ISSET(cmdfd, &rfd)) {
       +                        ttyread();
       +                        if (blinktimeout) {
       +                                blinkset = tattrset(ATTR_BLINK);
       +                                if (!blinkset)
       +                                        MODBIT(term.mode, 0, MODE_BLINK);
       +                        }
       +                }
       +
       +                if (FD_ISSET(xfd, &rfd))
       +                        xev = actionfps;
       +
       +                clock_gettime(CLOCK_MONOTONIC, &now);
       +                drawtimeout.tv_sec = 0;
       +                drawtimeout.tv_nsec =  (1000 * 1E6)/ xfps;
       +                tv = &drawtimeout;
       +
       +                dodraw = 0;
       +                if (blinktimeout && TIMEDIFF(now, lastblink) > blinktimeout) {
       +                        tsetdirtattr(ATTR_BLINK);
       +                        term.mode ^= MODE_BLINK;
       +                        lastblink = now;
       +                        dodraw = 1;
       +                }
       +                deltatime = TIMEDIFF(now, last);
       +                if (deltatime > 1000 / (xev ? xfps : actionfps)) {
       +                        dodraw = 1;
       +                        last = now;
       +                }
       +
       +                if (dodraw) {
       +                        while (XPending(xw.dpy)) {
       +                                XNextEvent(xw.dpy, &ev);
       +                                if (XFilterEvent(&ev, None))
       +                                        continue;
       +                                if (handler[ev.type])
       +                                        (handler[ev.type])(&ev);
       +                        }
       +
       +                        draw();
       +                        XFlush(xw.dpy);
       +
       +                        if (xev && !FD_ISSET(xfd, &rfd))
       +                                xev--;
       +                        if (!FD_ISSET(cmdfd, &rfd) && !FD_ISSET(xfd, &rfd)) {
       +                                if (blinkset) {
       +                                        if (TIMEDIFF(now, lastblink) \
       +                                                        > blinktimeout) {
       +                                                drawtimeout.tv_nsec = 1000;
       +                                        } else {
       +                                                drawtimeout.tv_nsec = (1E6 * \
       +                                                        (blinktimeout - \
       +                                                        TIMEDIFF(now,
       +                                                                lastblink)));
       +                                        }
       +                                        drawtimeout.tv_sec = \
       +                                            drawtimeout.tv_nsec / 1E9;
       +                                        drawtimeout.tv_nsec %= (long)1E9;
       +                                } else {
       +                                        tv = NULL;
       +                                }
       +                        }
       +                }
       +        }
       +}
       +
       +int
       +main(int argc, char *argv[])
       +{
       +        xw.l = xw.t = 0;
       +        xw.isfixed = False;
       +        win.cursor = cursorshape;
       +
       +        ARGBEGIN {
       +        case 'a':
       +                allowaltscreen = 0;
       +                break;
       +        case 'c':
       +                opt_class = EARGF(usage());
       +                break;
       +        case 'e':
       +                if (argc > 0)
       +                        --argc, ++argv;
       +                goto run;
       +        case 'f':
       +                opt_font = EARGF(usage());
       +                break;
       +        case 'g':
       +                xw.gm = XParseGeometry(EARGF(usage()),
       +                                &xw.l, &xw.t, &cols, &rows);
       +                break;
       +        case 'i':
       +                xw.isfixed = 1;
       +                break;
       +        case 'o':
       +                opt_io = EARGF(usage());
       +                break;
       +        case 'l':
       +                opt_line = EARGF(usage());
       +                break;
       +        case 'n':
       +                opt_name = EARGF(usage());
       +                break;
       +        case 't':
       +        case 'T':
       +                opt_title = EARGF(usage());
       +                break;
       +        case 'w':
       +                opt_embed = EARGF(usage());
       +                break;
       +        case 'v':
       +                die("%s " VERSION " (c) 2010-2016 st engineers\n", argv0);
       +                break;
       +        default:
       +                usage();
       +        } ARGEND;
       +
       +run:
       +        if (argc > 0) {
       +                /* eat all remaining arguments */
       +                opt_cmd = argv;
       +                if (!opt_title && !opt_line)
       +                        opt_title = basename(xstrdup(argv[0]));
       +        }
       +        setlocale(LC_CTYPE, "");
       +        XSetLocaleModifiers("");
       +        tnew(MAX(cols, 1), MAX(rows, 1));
       +        xinit();
       +        selinit();
       +        run();
       +
       +        return 0;
       +}