tSplit X-specific code into x.c - st - [fork] customized build of st, the simpl… | |
git clone git://src.adamsgaard.dk/st | |
Log | |
Files | |
Refs | |
README | |
LICENSE | |
--- | |
commit e2ee5ee6114eb74bb08cb9abe5a3020203e92688 | |
parent c63a87cd936c1eeef14c4c21572e5b782d3df4bc | |
Author: Michael Forney <[email protected]> | |
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(-) | |
--- | |
diff --git a/Makefile b/Makefile | |
t@@ -3,7 +3,7 @@ | |
include config.mk | |
-SRC = st.c | |
+SRC = st.c x.c | |
OBJ = ${SRC:.c=.o} | |
all: options st | |
t@@ -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} | |
diff --git a/config.def.h b/config.def.h | |
t@@ -5,8 +5,8 @@ | |
* | |
* font: see http://freedesktop.org/software/fontconfig/fontconfig-user.html | |
*/ | |
-static char font[] = "Liberation Mono:pixelsize=12:antialias=true:autohint=tru… | |
-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: | |
t@@ -24,8 +24,8 @@ static char stty_args[] = "stty raw pass8 nl -echo -iexten -… | |
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 | |
t@@ -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 | |
t@@ -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 | |
t@@ -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", | |
t@@ -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 | |
t@@ -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" }, | |
t@@ -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} }, | |
t@@ -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 | |
t@@ -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, | |
}; | |
t@@ -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{|}~"; | |
diff --git a/st.c b/st.c | |
t@@ -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__) | |
t@@ -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) … | |
-#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 | |
t@@ -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, | |
t@@ -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 { | |
t@@ -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; | |
t@@ -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); | |
t@@ -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); | |
t@@ -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); | |
t@@ -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); | |
t@@ -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, in… | |
-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) | |
t@@ -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); | |
} | |
t@@ -732,7 +394,7 @@ int | |
y2row(int y) | |
{ | |
y -= borderpx; | |
- y /= xw.ch; | |
+ y /= win.ch; | |
return LIMIT(y, 0, term.row-1); | |
} | |
t@@ -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) | |
{ | |
t@@ -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; | |
t@@ -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; | |
t@@ -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) { | |
t@@ -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"); | |
t@@ -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); | |
t@@ -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(err… | |
} | |
t@@ -1771,7 +1058,7 @@ selscroll(int orig, int n) | |
if (BETWEEN(sel.ob.y, orig, term.bot) || BETWEEN(sel.oe.y, orig, term.… | |
if ((sel.ob.y += n) > term.bot || (sel.oe.y += n) < term.top) { | |
- selclear(NULL); | |
+ selclear(); | |
return; | |
} | |
if (sel.type == SEL_RECTANGULAR) { | |
t@@ -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; | |
t@@ -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); | |
t@@ -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; | |
t@@ -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; | |
t@@ -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 */ | |
t@@ -2968,7 +2256,7 @@ eschandle(uchar ascii) | |
break; | |
case 'c': /* RIS -- Reset to inital state */ | |
treset(); | |
- xresettitle(); | |
+ resettitle(); | |
xloadcols(); | |
break; | |
case '=': /* DECPAM -- Application keypad */ | |
t@@ -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)) { | |
t@@ -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)); | |
t@@ -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", colorna… | |
- 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), InputOut… | |
- 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,… | |
- 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… | |
-{ | |
- 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 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, … | |
- 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 - … | |
- | |
- 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++) { | |
t@@ -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.ht… | |
- */ | |
- 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]" | |
t@@ -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; | |
-} | |
- | |
diff --git a/st.h b/st.h | |
t@@ -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) … | |
+#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[]; | |
diff --git a/win.h b/win.h | |
t@@ -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); | |
diff --git a/x.c b/x.c | |
t@@ -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, in… | |
+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", colorna… | |
+ 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), InputO… | |
+ 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,… | |
+ 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… | |
+{ | |
+ float winx = borderpx + x * win.cw, winy = borderpx + y * win.ch, xp, … | |
+ 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 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, … | |
+ 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 - … | |
+ | |
+ 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.ht… | |
+ */ | |
+ 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; | |
+} |