aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMichael Forney <mforney@mforney.org>2017-01-20 00:06:39 -0800
committerMichael Forney <mforney@mforney.org>2017-01-20 19:42:26 -0800
commite2ee5ee6114eb74bb08cb9abe5a3020203e92688 (patch)
tree7fa47c27df85b1803f8e35860ad6ed2603138acb
parentc63a87cd936c1eeef14c4c21572e5b782d3df4bc (diff)
downloadst-e2ee5ee6114eb74bb08cb9abe5a3020203e92688.tar.gz
st-e2ee5ee6114eb74bb08cb9abe5a3020203e92688.zip
Split X-specific code into x.c
-rw-r--r--Makefile5
-rw-r--r--config.def.h64
-rw-r--r--st.c2026
-rw-r--r--st.h272
-rw-r--r--win.h29
-rw-r--r--x.c1766
6 files changed, 2168 insertions, 1994 deletions
diff --git a/Makefile b/Makefile
index fb026c4..d8595fe 100644
--- a/Makefile
+++ b/Makefile
@@ -3,7 +3,7 @@
3 3
4include config.mk 4include config.mk
5 5
6SRC = st.c 6SRC = st.c x.c
7OBJ = ${SRC:.c=.o} 7OBJ = ${SRC:.c=.o}
8 8
9all: options st 9all: options st
@@ -21,6 +21,9 @@ config.h:
21 @echo CC $< 21 @echo CC $<
22 @${CC} -c ${CFLAGS} $< 22 @${CC} -c ${CFLAGS} $<
23 23
24st.o: config.h st.h win.h
25x.o: arg.h st.h win.h
26
24${OBJ}: config.h config.mk 27${OBJ}: config.h config.mk
25 28
26st: ${OBJ} 29st: ${OBJ}
diff --git a/config.def.h b/config.def.h
index a719e36..fd80923 100644
--- a/config.def.h
+++ b/config.def.h
@@ -5,8 +5,8 @@
5 * 5 *
6 * font: see http://freedesktop.org/software/fontconfig/fontconfig-user.html 6 * font: see http://freedesktop.org/software/fontconfig/fontconfig-user.html
7 */ 7 */
8static char font[] = "Liberation Mono:pixelsize=12:antialias=true:autohint=true"; 8char font[] = "Liberation Mono:pixelsize=12:antialias=true:autohint=true";
9static int borderpx = 2; 9int borderpx = 2;
10 10
11/* 11/*
12 * What program is execed by st depends of these precedence rules: 12 * What program is execed by st depends of these precedence rules:
@@ -24,8 +24,8 @@ static char stty_args[] = "stty raw pass8 nl -echo -iexten -cstopb 38400";
24static char vtiden[] = "\033[?6c"; 24static char vtiden[] = "\033[?6c";
25 25
26/* Kerning / character bounding-box multipliers */ 26/* Kerning / character bounding-box multipliers */
27static float cwscale = 1.0; 27float cwscale = 1.0;
28static float chscale = 1.0; 28float chscale = 1.0;
29 29
30/* 30/*
31 * word delimiter string 31 * word delimiter string
@@ -35,26 +35,26 @@ static float chscale = 1.0;
35static char worddelimiters[] = " "; 35static char worddelimiters[] = " ";
36 36
37/* selection timeouts (in milliseconds) */ 37/* selection timeouts (in milliseconds) */
38static unsigned int doubleclicktimeout = 300; 38unsigned int doubleclicktimeout = 300;
39static unsigned int tripleclicktimeout = 600; 39unsigned int tripleclicktimeout = 600;
40 40
41/* alt screens */ 41/* alt screens */
42static int allowaltscreen = 1; 42int allowaltscreen = 1;
43 43
44/* frames per second st should at maximum draw to the screen */ 44/* frames per second st should at maximum draw to the screen */
45static unsigned int xfps = 120; 45unsigned int xfps = 120;
46static unsigned int actionfps = 30; 46unsigned int actionfps = 30;
47 47
48/* 48/*
49 * blinking timeout (set to 0 to disable blinking) for the terminal blinking 49 * blinking timeout (set to 0 to disable blinking) for the terminal blinking
50 * attribute. 50 * attribute.
51 */ 51 */
52static unsigned int blinktimeout = 800; 52unsigned int blinktimeout = 800;
53 53
54/* 54/*
55 * thickness of underline and bar cursors 55 * thickness of underline and bar cursors
56 */ 56 */
57static unsigned int cursorthickness = 2; 57unsigned int cursorthickness = 2;
58 58
59/* 59/*
60 * bell volume. It must be a value between -100 and 100. Use 0 for disabling 60 * bell volume. It must be a value between -100 and 100. Use 0 for disabling
@@ -63,7 +63,7 @@ static unsigned int cursorthickness = 2;
63static int bellvolume = 0; 63static int bellvolume = 0;
64 64
65/* default TERM value */ 65/* default TERM value */
66static char termname[] = "st-256color"; 66char termname[] = "st-256color";
67 67
68/* 68/*
69 * spaces per tab 69 * spaces per tab
@@ -83,7 +83,7 @@ static char termname[] = "st-256color";
83static unsigned int tabspaces = 8; 83static unsigned int tabspaces = 8;
84 84
85/* Terminal colors (16 first used in escape sequence) */ 85/* Terminal colors (16 first used in escape sequence) */
86static const char *colorname[] = { 86const char *colorname[] = {
87 /* 8 normal colors */ 87 /* 8 normal colors */
88 "black", 88 "black",
89 "red3", 89 "red3",
@@ -116,10 +116,10 @@ static const char *colorname[] = {
116 * Default colors (colorname index) 116 * Default colors (colorname index)
117 * foreground, background, cursor, reverse cursor 117 * foreground, background, cursor, reverse cursor
118 */ 118 */
119static unsigned int defaultfg = 7; 119unsigned int defaultfg = 7;
120static unsigned int defaultbg = 0; 120unsigned int defaultbg = 0;
121static unsigned int defaultcs = 256; 121unsigned int defaultcs = 256;
122static unsigned int defaultrcs = 257; 122unsigned int defaultrcs = 257;
123 123
124/* 124/*
125 * Default shape of cursor 125 * Default shape of cursor
@@ -128,33 +128,33 @@ static unsigned int defaultrcs = 257;
128 * 6: Bar ("|") 128 * 6: Bar ("|")
129 * 7: Snowman ("☃") 129 * 7: Snowman ("☃")
130 */ 130 */
131static unsigned int cursorshape = 2; 131unsigned int cursorshape = 2;
132 132
133/* 133/*
134 * Default columns and rows numbers 134 * Default columns and rows numbers
135 */ 135 */
136 136
137static unsigned int cols = 80; 137unsigned int cols = 80;
138static unsigned int rows = 24; 138unsigned int rows = 24;
139 139
140/* 140/*
141 * Default colour and shape of the mouse cursor 141 * Default colour and shape of the mouse cursor
142 */ 142 */
143static unsigned int mouseshape = XC_xterm; 143unsigned int mouseshape = XC_xterm;
144static unsigned int mousefg = 7; 144unsigned int mousefg = 7;
145static unsigned int mousebg = 0; 145unsigned int mousebg = 0;
146 146
147/* 147/*
148 * Color used to display font attributes when fontconfig selected a font which 148 * Color used to display font attributes when fontconfig selected a font which
149 * doesn't match the ones requested. 149 * doesn't match the ones requested.
150 */ 150 */
151static unsigned int defaultattr = 11; 151unsigned int defaultattr = 11;
152 152
153/* 153/*
154 * Internal mouse shortcuts. 154 * Internal mouse shortcuts.
155 * Beware that overloading Button1 will disable the selection. 155 * Beware that overloading Button1 will disable the selection.
156 */ 156 */
157static MouseShortcut mshortcuts[] = { 157MouseShortcut mshortcuts[] = {
158 /* button mask string */ 158 /* button mask string */
159 { Button4, XK_ANY_MOD, "\031" }, 159 { Button4, XK_ANY_MOD, "\031" },
160 { Button5, XK_ANY_MOD, "\005" }, 160 { Button5, XK_ANY_MOD, "\005" },
@@ -163,15 +163,15 @@ static MouseShortcut mshortcuts[] = {
163/* Internal keyboard shortcuts. */ 163/* Internal keyboard shortcuts. */
164#define MODKEY Mod1Mask 164#define MODKEY Mod1Mask
165 165
166static Shortcut shortcuts[] = { 166Shortcut shortcuts[] = {
167 /* mask keysym function argument */ 167 /* mask keysym function argument */
168 { XK_ANY_MOD, XK_Break, sendbreak, {.i = 0} }, 168 { XK_ANY_MOD, XK_Break, sendbreak, {.i = 0} },
169 { ControlMask, XK_Print, toggleprinter, {.i = 0} }, 169 { ControlMask, XK_Print, toggleprinter, {.i = 0} },
170 { ShiftMask, XK_Print, printscreen, {.i = 0} }, 170 { ShiftMask, XK_Print, printscreen, {.i = 0} },
171 { XK_ANY_MOD, XK_Print, printsel, {.i = 0} }, 171 { XK_ANY_MOD, XK_Print, printsel, {.i = 0} },
172 { MODKEY|ShiftMask, XK_Prior, xzoom, {.f = +1} }, 172 { MODKEY|ShiftMask, XK_Prior, zoom, {.f = +1} },
173 { MODKEY|ShiftMask, XK_Next, xzoom, {.f = -1} }, 173 { MODKEY|ShiftMask, XK_Next, zoom, {.f = -1} },
174 { MODKEY|ShiftMask, XK_Home, xzoomreset, {.f = 0} }, 174 { MODKEY|ShiftMask, XK_Home, zoomreset, {.f = 0} },
175 { ShiftMask, XK_Insert, selpaste, {.i = 0} }, 175 { ShiftMask, XK_Insert, selpaste, {.i = 0} },
176 { MODKEY|ShiftMask, XK_Insert, clippaste, {.i = 0} }, 176 { MODKEY|ShiftMask, XK_Insert, clippaste, {.i = 0} },
177 { MODKEY|ShiftMask, XK_C, clipcopy, {.i = 0} }, 177 { MODKEY|ShiftMask, XK_C, clipcopy, {.i = 0} },
@@ -222,7 +222,7 @@ static uint ignoremod = Mod2Mask|XK_SWITCH_MOD;
222 * Note that if you want to use ShiftMask with selmasks, set this to an other 222 * Note that if you want to use ShiftMask with selmasks, set this to an other
223 * modifier, set to 0 to not use it. 223 * modifier, set to 0 to not use it.
224 */ 224 */
225static uint forceselmod = ShiftMask; 225uint forceselmod = ShiftMask;
226 226
227/* 227/*
228 * This is the huge key array which defines all compatibility to the Linux 228 * This is the huge key array which defines all compatibility to the Linux
@@ -451,7 +451,7 @@ static Key key[] = {
451 * ButtonRelease and MotionNotify. 451 * ButtonRelease and MotionNotify.
452 * If no match is found, regular selection is used. 452 * If no match is found, regular selection is used.
453 */ 453 */
454static uint selmasks[] = { 454uint selmasks[] = {
455 [SEL_RECTANGULAR] = Mod1Mask, 455 [SEL_RECTANGULAR] = Mod1Mask,
456}; 456};
457 457
@@ -459,7 +459,7 @@ static uint selmasks[] = {
459 * Printable characters in ASCII, used to estimate the advance width 459 * Printable characters in ASCII, used to estimate the advance width
460 * of single wide characters. 460 * of single wide characters.
461 */ 461 */
462static char ascii_printable[] = 462char ascii_printable[] =
463 " !\"#$%&'()*+,-./0123456789:;<=>?" 463 " !\"#$%&'()*+,-./0123456789:;<=>?"
464 "@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_" 464 "@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_"
465 "`abcdefghijklmnopqrstuvwxyz{|}~"; 465 "`abcdefghijklmnopqrstuvwxyz{|}~";
diff --git a/st.c b/st.c
index fbcd9e0..1e4196e 100644
--- a/st.c
+++ b/st.c
@@ -21,23 +21,21 @@
21#include <time.h> 21#include <time.h>
22#include <unistd.h> 22#include <unistd.h>
23#include <libgen.h> 23#include <libgen.h>
24#include <X11/Xatom.h>
25#include <X11/Xlib.h>
26#include <X11/Xutil.h>
27#include <X11/cursorfont.h>
28#include <X11/keysym.h>
29#include <X11/Xft/Xft.h>
30#include <X11/XKBlib.h>
31#include <fontconfig/fontconfig.h> 24#include <fontconfig/fontconfig.h>
32#include <wchar.h> 25#include <wchar.h>
33 26
34#include "arg.h" 27/* X11 */
28#include <X11/cursorfont.h>
29#include <X11/Xft/Xft.h>
35 30
36char *argv0; 31char *argv0;
37 32
38#define Glyph Glyph_ 33#define Glyph Glyph_
39#define Font Font_ 34#define Font Font_
40 35
36#include "win.h"
37#include "st.h"
38
41#if defined(__linux) 39#if defined(__linux)
42 #include <pty.h> 40 #include <pty.h>
43#elif defined(__OpenBSD__) || defined(__NetBSD__) || defined(__APPLE__) 41#elif defined(__OpenBSD__) || defined(__NetBSD__) || defined(__APPLE__)
@@ -46,67 +44,24 @@ char *argv0;
46 #include <libutil.h> 44 #include <libutil.h>
47#endif 45#endif
48 46
49
50/* XEMBED messages */
51#define XEMBED_FOCUS_IN 4
52#define XEMBED_FOCUS_OUT 5
53
54/* Arbitrary sizes */ 47/* Arbitrary sizes */
55#define UTF_INVALID 0xFFFD 48#define UTF_INVALID 0xFFFD
56#define UTF_SIZ 4
57#define ESC_BUF_SIZ (128*UTF_SIZ) 49#define ESC_BUF_SIZ (128*UTF_SIZ)
58#define ESC_ARG_SIZ 16 50#define ESC_ARG_SIZ 16
59#define STR_BUF_SIZ ESC_BUF_SIZ 51#define STR_BUF_SIZ ESC_BUF_SIZ
60#define STR_ARG_SIZ ESC_ARG_SIZ 52#define STR_ARG_SIZ ESC_ARG_SIZ
61#define XK_ANY_MOD UINT_MAX
62#define XK_NO_MOD 0
63#define XK_SWITCH_MOD (1<<13)
64 53
65/* macros */ 54/* macros */
66#define MIN(a, b) ((a) < (b) ? (a) : (b))
67#define MAX(a, b) ((a) < (b) ? (b) : (a))
68#define LEN(a) (sizeof(a) / sizeof(a)[0])
69#define NUMMAXLEN(x) ((int)(sizeof(x) * 2.56 + 0.5) + 1) 55#define NUMMAXLEN(x) ((int)(sizeof(x) * 2.56 + 0.5) + 1)
70#define DEFAULT(a, b) (a) = (a) ? (a) : (b) 56#define DEFAULT(a, b) (a) = (a) ? (a) : (b)
71#define BETWEEN(x, a, b) ((a) <= (x) && (x) <= (b))
72#define DIVCEIL(n, d) (((n) + ((d) - 1)) / (d))
73#define ISCONTROLC0(c) (BETWEEN(c, 0, 0x1f) || (c) == '\177') 57#define ISCONTROLC0(c) (BETWEEN(c, 0, 0x1f) || (c) == '\177')
74#define ISCONTROLC1(c) (BETWEEN(c, 0x80, 0x9f)) 58#define ISCONTROLC1(c) (BETWEEN(c, 0x80, 0x9f))
75#define ISCONTROL(c) (ISCONTROLC0(c) || ISCONTROLC1(c)) 59#define ISCONTROL(c) (ISCONTROLC0(c) || ISCONTROLC1(c))
76#define ISDELIM(u) (utf8strchr(worddelimiters, u) != NULL) 60#define ISDELIM(u) (utf8strchr(worddelimiters, u) != NULL)
77#define LIMIT(x, a, b) (x) = (x) < (a) ? (a) : (x) > (b) ? (b) : (x)
78#define ATTRCMP(a, b) ((a).mode != (b).mode || (a).fg != (b).fg || \
79 (a).bg != (b).bg)
80#define IS_SET(flag) ((term.mode & (flag)) != 0)
81#define TIMEDIFF(t1, t2) ((t1.tv_sec-t2.tv_sec)*1000 + \
82 (t1.tv_nsec-t2.tv_nsec)/1E6)
83#define MODBIT(x, set, bit) ((set) ? ((x) |= (bit)) : ((x) &= ~(bit)))
84
85#define TRUECOLOR(r,g,b) (1 << 24 | (r) << 16 | (g) << 8 | (b))
86#define IS_TRUECOL(x) (1 << 24 & (x))
87#define TRUERED(x) (((x) & 0xff0000) >> 8)
88#define TRUEGREEN(x) (((x) & 0xff00))
89#define TRUEBLUE(x) (((x) & 0xff) << 8)
90 61
91/* constants */ 62/* constants */
92#define ISO14755CMD "dmenu -w %lu -p codepoint: </dev/null" 63#define ISO14755CMD "dmenu -w %lu -p codepoint: </dev/null"
93 64
94enum glyph_attribute {
95 ATTR_NULL = 0,
96 ATTR_BOLD = 1 << 0,
97 ATTR_FAINT = 1 << 1,
98 ATTR_ITALIC = 1 << 2,
99 ATTR_UNDERLINE = 1 << 3,
100 ATTR_BLINK = 1 << 4,
101 ATTR_REVERSE = 1 << 5,
102 ATTR_INVISIBLE = 1 << 6,
103 ATTR_STRUCK = 1 << 7,
104 ATTR_WRAP = 1 << 8,
105 ATTR_WIDE = 1 << 9,
106 ATTR_WDUMMY = 1 << 10,
107 ATTR_BOLD_FAINT = ATTR_BOLD | ATTR_FAINT,
108};
109
110enum cursor_movement { 65enum cursor_movement {
111 CURSOR_SAVE, 66 CURSOR_SAVE,
112 CURSOR_LOAD 67 CURSOR_LOAD
@@ -118,34 +73,6 @@ enum cursor_state {
118 CURSOR_ORIGIN = 2 73 CURSOR_ORIGIN = 2
119}; 74};
120 75
121enum term_mode {
122 MODE_WRAP = 1 << 0,
123 MODE_INSERT = 1 << 1,
124 MODE_APPKEYPAD = 1 << 2,
125 MODE_ALTSCREEN = 1 << 3,
126 MODE_CRLF = 1 << 4,
127 MODE_MOUSEBTN = 1 << 5,
128 MODE_MOUSEMOTION = 1 << 6,
129 MODE_REVERSE = 1 << 7,
130 MODE_KBDLOCK = 1 << 8,
131 MODE_HIDE = 1 << 9,
132 MODE_ECHO = 1 << 10,
133 MODE_APPCURSOR = 1 << 11,
134 MODE_MOUSESGR = 1 << 12,
135 MODE_8BIT = 1 << 13,
136 MODE_BLINK = 1 << 14,
137 MODE_FBLINK = 1 << 15,
138 MODE_FOCUS = 1 << 16,
139 MODE_MOUSEX10 = 1 << 17,
140 MODE_MOUSEMANY = 1 << 18,
141 MODE_BRCKTPASTE = 1 << 19,
142 MODE_PRINT = 1 << 20,
143 MODE_UTF8 = 1 << 21,
144 MODE_SIXEL = 1 << 22,
145 MODE_MOUSE = MODE_MOUSEBTN|MODE_MOUSEMOTION|MODE_MOUSEX10\
146 |MODE_MOUSEMANY,
147};
148
149enum charset { 76enum charset {
150 CS_GRAPHIC0, 77 CS_GRAPHIC0,
151 CS_GRAPHIC1, 78 CS_GRAPHIC1,
@@ -167,53 +94,6 @@ enum escape_state {
167 ESC_DCS =128, 94 ESC_DCS =128,
168}; 95};
169 96
170enum window_state {
171 WIN_VISIBLE = 1,
172 WIN_FOCUSED = 2
173};
174
175enum selection_mode {
176 SEL_IDLE = 0,
177 SEL_EMPTY = 1,
178 SEL_READY = 2
179};
180
181enum selection_type {
182 SEL_REGULAR = 1,
183 SEL_RECTANGULAR = 2
184};
185
186enum selection_snap {
187 SNAP_WORD = 1,
188 SNAP_LINE = 2
189};
190
191typedef unsigned char uchar;
192typedef unsigned int uint;
193typedef unsigned long ulong;
194typedef unsigned short ushort;
195
196typedef uint_least32_t Rune;
197
198typedef XftDraw *Draw;
199typedef XftColor Color;
200
201typedef struct {
202 Rune u; /* character code */
203 ushort mode; /* attribute flags */
204 uint32_t fg; /* foreground */
205 uint32_t bg; /* background */
206} Glyph;
207
208typedef Glyph *Line;
209
210typedef struct {
211 Glyph attr; /* current char attributes */
212 int x;
213 int y;
214 char state;
215} TCursor;
216
217/* CSI Escape sequence structs */ 97/* CSI Escape sequence structs */
218/* ESC '[' [[ [<priv>] <arg> [;]] <mode> [<mode>]] */ 98/* ESC '[' [[ [<priv>] <arg> [;]] <mode> [<mode>]] */
219typedef struct { 99typedef struct {
@@ -235,56 +115,6 @@ typedef struct {
235 int narg; /* nb of args */ 115 int narg; /* nb of args */
236} STREscape; 116} STREscape;
237 117
238/* Internal representation of the screen */
239typedef struct {
240 int row; /* nb row */
241 int col; /* nb col */
242 Line *line; /* screen */
243 Line *alt; /* alternate screen */
244 int *dirty; /* dirtyness of lines */
245 XftGlyphFontSpec *specbuf; /* font spec buffer used for rendering */
246 TCursor c; /* cursor */
247 int top; /* top scroll limit */
248 int bot; /* bottom scroll limit */
249 int mode; /* terminal mode flags */
250 int esc; /* escape state flags */
251 char trantbl[4]; /* charset table translation */
252 int charset; /* current charset */
253 int icharset; /* selected charset for sequence */
254 int numlock; /* lock numbers in keyboard */
255 int *tabs;
256} Term;
257
258/* Purely graphic info */
259typedef struct {
260 Display *dpy;
261 Colormap cmap;
262 Window win;
263 Drawable buf;
264 Atom xembed, wmdeletewin, netwmname, netwmpid;
265 XIM xim;
266 XIC xic;
267 Draw draw;
268 Visual *vis;
269 XSetWindowAttributes attrs;
270 int scr;
271 int isfixed; /* is fixed geometry? */
272 int l, t; /* left and top offset */
273 int gm; /* geometry mask */
274 int tw, th; /* tty width and height */
275 int w, h; /* window width and height */
276 int ch; /* char height */
277 int cw; /* char width */
278 char state; /* focus, redraw, visible */
279 int cursor; /* cursor style */
280} XWindow;
281
282typedef struct {
283 uint b;
284 uint mask;
285 char *s;
286} MouseShortcut;
287
288typedef struct { 118typedef struct {
289 KeySym k; 119 KeySym k;
290 uint mask; 120 uint mask;
@@ -295,89 +125,26 @@ typedef struct {
295 signed char crlf; /* crlf mode */ 125 signed char crlf; /* crlf mode */
296} Key; 126} Key;
297 127
298typedef struct {
299 int mode;
300 int type;
301 int snap;
302 /*
303 * Selection variables:
304 * nb – normalized coordinates of the beginning of the selection
305 * ne – normalized coordinates of the end of the selection
306 * ob – original coordinates of the beginning of the selection
307 * oe – original coordinates of the end of the selection
308 */
309 struct {
310 int x, y;
311 } nb, ne, ob, oe;
312
313 char *primary, *clipboard;
314 Atom xtarget;
315 int alt;
316 struct timespec tclick1;
317 struct timespec tclick2;
318} Selection;
319
320typedef union {
321 int i;
322 uint ui;
323 float f;
324 const void *v;
325} Arg;
326
327typedef struct {
328 uint mod;
329 KeySym keysym;
330 void (*func)(const Arg *);
331 const Arg arg;
332} Shortcut;
333
334/* function definitions used in config.h */ 128/* function definitions used in config.h */
335static void clipcopy(const Arg *); 129static void clipcopy(const Arg *);
336static void clippaste(const Arg *); 130static void clippaste(const Arg *);
337static void numlock(const Arg *); 131static void numlock(const Arg *);
338static void selpaste(const Arg *); 132static void selpaste(const Arg *);
339static void xzoom(const Arg *); 133static void zoom(const Arg *);
340static void xzoomabs(const Arg *); 134static void zoomabs(const Arg *);
341static void xzoomreset(const Arg *); 135static void zoomreset(const Arg *);
342static void printsel(const Arg *); 136static void printsel(const Arg *);
343static void printscreen(const Arg *) ; 137static void printscreen(const Arg *) ;
344static void iso14755(const Arg *); 138static void iso14755(const Arg *);
345static void toggleprinter(const Arg *); 139static void toggleprinter(const Arg *);
346static void sendbreak(const Arg *); 140static void sendbreak(const Arg *);
347 141
348/* Config.h for applying patches and the configuration. */ 142/* config.h for applying patches and the configuration. */
349#include "config.h" 143#include "config.h"
350 144
351/* Font structure */
352typedef struct {
353 int height;
354 int width;
355 int ascent;
356 int descent;
357 int badslant;
358 int badweight;
359 short lbearing;
360 short rbearing;
361 XftFont *match;
362 FcFontSet *set;
363 FcPattern *pattern;
364} Font;
365
366/* Drawing Context */
367typedef struct {
368 Color col[MAX(LEN(colorname), 256)];
369 Font font, bfont, ifont, ibfont;
370 GC gc;
371} DC;
372
373static void die(const char *, ...);
374static void draw(void);
375static void redraw(void);
376static void drawregion(int, int, int, int);
377static void execsh(void); 145static void execsh(void);
378static void stty(void); 146static void stty(void);
379static void sigchld(int); 147static void sigchld(int);
380static void run(void);
381 148
382static void csidump(void); 149static void csidump(void);
383static void csihandle(void); 150static void csihandle(void);
@@ -389,7 +156,6 @@ static void strhandle(void);
389static void strparse(void); 156static void strparse(void);
390static void strreset(void); 157static void strreset(void);
391 158
392static int tattrset(int);
393static void tprinter(char *, size_t); 159static void tprinter(char *, size_t);
394static void tdumpsel(void); 160static void tdumpsel(void);
395static void tdumpline(int); 161static void tdumpline(int);
@@ -403,7 +169,6 @@ static void tinsertblankline(int);
403static int tlinelen(int); 169static int tlinelen(int);
404static void tmoveto(int, int); 170static void tmoveto(int, int);
405static void tmoveato(int, int); 171static void tmoveato(int, int);
406static void tnew(int, int);
407static void tnewline(int); 172static void tnewline(int);
408static void tputtab(int); 173static void tputtab(int);
409static void tputc(Rune); 174static void tputc(Rune);
@@ -415,8 +180,6 @@ static void tsetattr(int *, int);
415static void tsetchar(Rune, Glyph *, int, int); 180static void tsetchar(Rune, Glyph *, int, int);
416static void tsetscroll(int, int); 181static void tsetscroll(int, int);
417static void tswapscreen(void); 182static void tswapscreen(void);
418static void tsetdirt(int, int);
419static void tsetdirtattr(int);
420static void tsetmode(int, int, int *, int); 183static void tsetmode(int, int, int *, int);
421static void tfulldirt(void); 184static void tfulldirt(void);
422static void techo(Rune); 185static void techo(Rune);
@@ -425,151 +188,53 @@ static void tdectest(char );
425static void tdefutf8(char); 188static void tdefutf8(char);
426static int32_t tdefcolor(int *, int *, int); 189static int32_t tdefcolor(int *, int *, int);
427static void tdeftran(char); 190static void tdeftran(char);
428static inline int match(uint, uint);
429static void ttynew(void);
430static size_t ttyread(void);
431static void ttyresize(void);
432static void ttysend(char *, size_t);
433static void ttywrite(const char *, size_t);
434static void tstrsequence(uchar); 191static void tstrsequence(uchar);
435 192
436static inline ushort sixd_to_16bit(int);
437static int xmakeglyphfontspecs(XftGlyphFontSpec *, const Glyph *, int, int, int);
438static void xdrawglyphfontspecs(const XftGlyphFontSpec *, Glyph, int, int, int);
439static void xdrawglyph(Glyph, int, int);
440static void xhints(void);
441static void xclear(int, int, int, int);
442static void xdrawcursor(void);
443static void xinit(void);
444static void xloadcols(void);
445static int xsetcolorname(int, const char *);
446static int xgeommasktogravity(int);
447static int xloadfont(Font *, FcPattern *);
448static void xloadfonts(char *, double);
449static void xsettitle(char *);
450static void xresettitle(void);
451static void xsetpointermotion(int);
452static void xseturgency(int);
453static void xsetsel(char *, Time);
454static void xunloadfont(Font *);
455static void xunloadfonts(void);
456static void xresize(int, int);
457
458static void expose(XEvent *);
459static void visibility(XEvent *);
460static void unmap(XEvent *);
461static char *kmap(KeySym, uint);
462static void kpress(XEvent *);
463static void cmessage(XEvent *);
464static void cresize(int, int);
465static void resize(XEvent *);
466static void focus(XEvent *);
467static void brelease(XEvent *);
468static void bpress(XEvent *);
469static void bmotion(XEvent *);
470static void propnotify(XEvent *);
471static void selnotify(XEvent *);
472static void selclear(XEvent *);
473static void selrequest(XEvent *);
474
475static void selinit(void);
476static void selnormalize(void);
477static inline int selected(int, int);
478static char *getsel(void);
479static void selcopy(Time);
480static void selscroll(int, int); 193static void selscroll(int, int);
481static void selsnap(int *, int *, int); 194static void selsnap(int *, int *, int);
482static int x2col(int);
483static int y2row(int);
484static void getbuttoninfo(XEvent *);
485static void mousereport(XEvent *);
486 195
487static size_t utf8decode(char *, Rune *, size_t);
488static Rune utf8decodebyte(char, size_t *); 196static Rune utf8decodebyte(char, size_t *);
489static size_t utf8encode(Rune, char *);
490static char utf8encodebyte(Rune, size_t); 197static char utf8encodebyte(Rune, size_t);
491static char *utf8strchr(char *s, Rune u); 198static char *utf8strchr(char *s, Rune u);
492static size_t utf8validate(Rune *, size_t); 199static size_t utf8validate(Rune *, size_t);
493 200
494static ssize_t xwrite(int, const char *, size_t); 201static ssize_t xwrite(int, const char *, size_t);
495static void *xmalloc(size_t);
496static void *xrealloc(void *, size_t); 202static void *xrealloc(void *, size_t);
497static char *xstrdup(char *);
498
499static void usage(void);
500
501static void (*handler[LASTEvent])(XEvent *) = {
502 [KeyPress] = kpress,
503 [ClientMessage] = cmessage,
504 [ConfigureNotify] = resize,
505 [VisibilityNotify] = visibility,
506 [UnmapNotify] = unmap,
507 [Expose] = expose,
508 [FocusIn] = focus,
509 [FocusOut] = focus,
510 [MotionNotify] = bmotion,
511 [ButtonPress] = bpress,
512 [ButtonRelease] = brelease,
513/*
514 * Uncomment if you want the selection to disappear when you select something
515 * different in another window.
516 */
517/* [SelectionClear] = selclear, */
518 [SelectionNotify] = selnotify,
519/*
520 * PropertyNotify is only turned on when there is some INCR transfer happening
521 * for the selection retrieval.
522 */
523 [PropertyNotify] = propnotify,
524 [SelectionRequest] = selrequest,
525};
526 203
527/* Globals */ 204/* Globals */
528static DC dc; 205TermWindow win;
529static XWindow xw; 206Term term;
530static Term term; 207Selection sel;
208int cmdfd;
209pid_t pid;
210char **opt_cmd = NULL;
211char *opt_class = NULL;
212char *opt_embed = NULL;
213char *opt_font = NULL;
214char *opt_io = NULL;
215char *opt_line = NULL;
216char *opt_name = NULL;
217char *opt_title = NULL;
218int oldbutton = 3; /* button event on startup: 3 = release */
219
531static CSIEscape csiescseq; 220static CSIEscape csiescseq;
532static STREscape strescseq; 221static STREscape strescseq;
533static int cmdfd;
534static pid_t pid;
535static Selection sel;
536static int iofd = 1; 222static int iofd = 1;
537static char **opt_cmd = NULL; 223
538static char *opt_class = NULL; 224char *usedfont = NULL;
539static char *opt_embed = NULL; 225double usedfontsize = 0;
540static char *opt_font = NULL; 226double defaultfontsize = 0;
541static char *opt_io = NULL;
542static char *opt_line = NULL;
543static char *opt_name = NULL;
544static char *opt_title = NULL;
545static int oldbutton = 3; /* button event on startup: 3 = release */
546
547static char *usedfont = NULL;
548static double usedfontsize = 0;
549static double defaultfontsize = 0;
550 227
551static uchar utfbyte[UTF_SIZ + 1] = {0x80, 0, 0xC0, 0xE0, 0xF0}; 228static uchar utfbyte[UTF_SIZ + 1] = {0x80, 0, 0xC0, 0xE0, 0xF0};
552static uchar utfmask[UTF_SIZ + 1] = {0xC0, 0x80, 0xE0, 0xF0, 0xF8}; 229static uchar utfmask[UTF_SIZ + 1] = {0xC0, 0x80, 0xE0, 0xF0, 0xF8};
553static Rune utfmin[UTF_SIZ + 1] = { 0, 0, 0x80, 0x800, 0x10000}; 230static Rune utfmin[UTF_SIZ + 1] = { 0, 0, 0x80, 0x800, 0x10000};
554static Rune utfmax[UTF_SIZ + 1] = {0x10FFFF, 0x7F, 0x7FF, 0xFFFF, 0x10FFFF}; 231static Rune utfmax[UTF_SIZ + 1] = {0x10FFFF, 0x7F, 0x7FF, 0xFFFF, 0x10FFFF};
555 232
556/* Font Ring Cache */ 233/* config.h array lengths */
557enum { 234size_t colornamelen = LEN(colorname);
558 FRC_NORMAL, 235size_t mshortcutslen = LEN(mshortcuts);
559 FRC_ITALIC, 236size_t shortcutslen = LEN(shortcuts);
560 FRC_BOLD, 237size_t selmaskslen = LEN(selmasks);
561 FRC_ITALICBOLD
562};
563
564typedef struct {
565 XftFont *font;
566 int flags;
567 Rune unicodep;
568} Fontcache;
569
570/* Fontcache is an array now. A new font will be appended to the array. */
571static Fontcache frc[16];
572static int frclen = 0;
573 238
574ssize_t 239ssize_t
575xwrite(int fd, const char *s, size_t len) 240xwrite(int fd, const char *s, size_t len)
@@ -714,16 +379,13 @@ selinit(void)
714 sel.ob.x = -1; 379 sel.ob.x = -1;
715 sel.primary = NULL; 380 sel.primary = NULL;
716 sel.clipboard = NULL; 381 sel.clipboard = NULL;
717 sel.xtarget = XInternAtom(xw.dpy, "UTF8_STRING", 0);
718 if (sel.xtarget == None)
719 sel.xtarget = XA_STRING;
720} 382}
721 383
722int 384int
723x2col(int x) 385x2col(int x)
724{ 386{
725 x -= borderpx; 387 x -= borderpx;
726 x /= xw.cw; 388 x /= win.cw;
727 389
728 return LIMIT(x, 0, term.col-1); 390 return LIMIT(x, 0, term.col-1);
729} 391}
@@ -732,7 +394,7 @@ int
732y2row(int y) 394y2row(int y)
733{ 395{
734 y -= borderpx; 396 y -= borderpx;
735 y /= xw.ch; 397 y /= win.ch;
736 398
737 return LIMIT(y, 0, term.row-1); 399 return LIMIT(y, 0, term.row-1);
738} 400}
@@ -867,141 +529,6 @@ selsnap(int *x, int *y, int direction)
867 } 529 }
868} 530}
869 531
870void
871getbuttoninfo(XEvent *e)
872{
873 int type;
874 uint state = e->xbutton.state & ~(Button1Mask | forceselmod);
875
876 sel.alt = IS_SET(MODE_ALTSCREEN);
877
878 sel.oe.x = x2col(e->xbutton.x);
879 sel.oe.y = y2row(e->xbutton.y);
880 selnormalize();
881
882 sel.type = SEL_REGULAR;
883 for (type = 1; type < LEN(selmasks); ++type) {
884 if (match(selmasks[type], state)) {
885 sel.type = type;
886 break;
887 }
888 }
889}
890
891void
892mousereport(XEvent *e)
893{
894 int x = x2col(e->xbutton.x), y = y2row(e->xbutton.y),
895 button = e->xbutton.button, state = e->xbutton.state,
896 len;
897 char buf[40];
898 static int ox, oy;
899
900 /* from urxvt */
901 if (e->xbutton.type == MotionNotify) {
902 if (x == ox && y == oy)
903 return;
904 if (!IS_SET(MODE_MOUSEMOTION) && !IS_SET(MODE_MOUSEMANY))
905 return;
906 /* MOUSE_MOTION: no reporting if no button is pressed */
907 if (IS_SET(MODE_MOUSEMOTION) && oldbutton == 3)
908 return;
909
910 button = oldbutton + 32;
911 ox = x;
912 oy = y;
913 } else {
914 if (!IS_SET(MODE_MOUSESGR) && e->xbutton.type == ButtonRelease) {
915 button = 3;
916 } else {
917 button -= Button1;
918 if (button >= 3)
919 button += 64 - 3;
920 }
921 if (e->xbutton.type == ButtonPress) {
922 oldbutton = button;
923 ox = x;
924 oy = y;
925 } else if (e->xbutton.type == ButtonRelease) {
926 oldbutton = 3;
927 /* MODE_MOUSEX10: no button release reporting */
928 if (IS_SET(MODE_MOUSEX10))
929 return;
930 if (button == 64 || button == 65)
931 return;
932 }
933 }
934
935 if (!IS_SET(MODE_MOUSEX10)) {
936 button += ((state & ShiftMask ) ? 4 : 0)
937 + ((state & Mod4Mask ) ? 8 : 0)
938 + ((state & ControlMask) ? 16 : 0);
939 }
940
941 if (IS_SET(MODE_MOUSESGR)) {
942 len = snprintf(buf, sizeof(buf), "\033[<%d;%d;%d%c",
943 button, x+1, y+1,
944 e->xbutton.type == ButtonRelease ? 'm' : 'M');
945 } else if (x < 223 && y < 223) {
946 len = snprintf(buf, sizeof(buf), "\033[M%c%c%c",
947 32+button, 32+x+1, 32+y+1);
948 } else {
949 return;
950 }
951
952 ttywrite(buf, len);
953}
954
955void
956bpress(XEvent *e)
957{
958 struct timespec now;
959 MouseShortcut *ms;
960
961 if (IS_SET(MODE_MOUSE) && !(e->xbutton.state & forceselmod)) {
962 mousereport(e);
963 return;
964 }
965
966 for (ms = mshortcuts; ms < mshortcuts + LEN(mshortcuts); ms++) {
967 if (e->xbutton.button == ms->b
968 && match(ms->mask, e->xbutton.state)) {
969 ttysend(ms->s, strlen(ms->s));
970 return;
971 }
972 }
973
974 if (e->xbutton.button == Button1) {
975 clock_gettime(CLOCK_MONOTONIC, &now);
976
977 /* Clear previous selection, logically and visually. */
978 selclear(NULL);
979 sel.mode = SEL_EMPTY;
980 sel.type = SEL_REGULAR;
981 sel.oe.x = sel.ob.x = x2col(e->xbutton.x);
982 sel.oe.y = sel.ob.y = y2row(e->xbutton.y);
983
984 /*
985 * If the user clicks below predefined timeouts specific
986 * snapping behaviour is exposed.
987 */
988 if (TIMEDIFF(now, sel.tclick2) <= tripleclicktimeout) {
989 sel.snap = SNAP_LINE;
990 } else if (TIMEDIFF(now, sel.tclick1) <= doubleclicktimeout) {
991 sel.snap = SNAP_WORD;
992 } else {
993 sel.snap = 0;
994 }
995 selnormalize();
996
997 if (sel.snap != 0)
998 sel.mode = SEL_READY;
999 tsetdirt(sel.nb.y, sel.ne.y);
1000 sel.tclick2 = sel.tclick1;
1001 sel.tclick1 = now;
1002 }
1003}
1004
1005char * 532char *
1006getsel(void) 533getsel(void)
1007{ 534{
@@ -1057,148 +584,25 @@ getsel(void)
1057} 584}
1058 585
1059void 586void
1060selcopy(Time t)
1061{
1062 xsetsel(getsel(), t);
1063}
1064
1065void
1066propnotify(XEvent *e)
1067{
1068 XPropertyEvent *xpev;
1069 Atom clipboard = XInternAtom(xw.dpy, "CLIPBOARD", 0);
1070
1071 xpev = &e->xproperty;
1072 if (xpev->state == PropertyNewValue &&
1073 (xpev->atom == XA_PRIMARY ||
1074 xpev->atom == clipboard)) {
1075 selnotify(e);
1076 }
1077}
1078
1079void
1080selnotify(XEvent *e)
1081{
1082 ulong nitems, ofs, rem;
1083 int format;
1084 uchar *data, *last, *repl;
1085 Atom type, incratom, property;
1086
1087 incratom = XInternAtom(xw.dpy, "INCR", 0);
1088
1089 ofs = 0;
1090 if (e->type == SelectionNotify) {
1091 property = e->xselection.property;
1092 } else if(e->type == PropertyNotify) {
1093 property = e->xproperty.atom;
1094 } else {
1095 return;
1096 }
1097 if (property == None)
1098 return;
1099
1100 do {
1101 if (XGetWindowProperty(xw.dpy, xw.win, property, ofs,
1102 BUFSIZ/4, False, AnyPropertyType,
1103 &type, &format, &nitems, &rem,
1104 &data)) {
1105 fprintf(stderr, "Clipboard allocation failed\n");
1106 return;
1107 }
1108
1109 if (e->type == PropertyNotify && nitems == 0 && rem == 0) {
1110 /*
1111 * If there is some PropertyNotify with no data, then
1112 * this is the signal of the selection owner that all
1113 * data has been transferred. We won't need to receive
1114 * PropertyNotify events anymore.
1115 */
1116 MODBIT(xw.attrs.event_mask, 0, PropertyChangeMask);
1117 XChangeWindowAttributes(xw.dpy, xw.win, CWEventMask,
1118 &xw.attrs);
1119 }
1120
1121 if (type == incratom) {
1122 /*
1123 * Activate the PropertyNotify events so we receive
1124 * when the selection owner does send us the next
1125 * chunk of data.
1126 */
1127 MODBIT(xw.attrs.event_mask, 1, PropertyChangeMask);
1128 XChangeWindowAttributes(xw.dpy, xw.win, CWEventMask,
1129 &xw.attrs);
1130
1131 /*
1132 * Deleting the property is the transfer start signal.
1133 */
1134 XDeleteProperty(xw.dpy, xw.win, (int)property);
1135 continue;
1136 }
1137
1138 /*
1139 * As seen in getsel:
1140 * Line endings are inconsistent in the terminal and GUI world
1141 * copy and pasting. When receiving some selection data,
1142 * replace all '\n' with '\r'.
1143 * FIXME: Fix the computer world.
1144 */
1145 repl = data;
1146 last = data + nitems * format / 8;
1147 while ((repl = memchr(repl, '\n', last - repl))) {
1148 *repl++ = '\r';
1149 }
1150
1151 if (IS_SET(MODE_BRCKTPASTE) && ofs == 0)
1152 ttywrite("\033[200~", 6);
1153 ttysend((char *)data, nitems * format / 8);
1154 if (IS_SET(MODE_BRCKTPASTE) && rem == 0)
1155 ttywrite("\033[201~", 6);
1156 XFree(data);
1157 /* number of 32-bit chunks returned */
1158 ofs += nitems * format / 32;
1159 } while (rem > 0);
1160
1161 /*
1162 * Deleting the property again tells the selection owner to send the
1163 * next data chunk in the property.
1164 */
1165 XDeleteProperty(xw.dpy, xw.win, (int)property);
1166}
1167
1168void
1169selpaste(const Arg *dummy) 587selpaste(const Arg *dummy)
1170{ 588{
1171 XConvertSelection(xw.dpy, XA_PRIMARY, sel.xtarget, XA_PRIMARY, 589 xselpaste();
1172 xw.win, CurrentTime);
1173} 590}
1174 591
1175void 592void
1176clipcopy(const Arg *dummy) 593clipcopy(const Arg *dummy)
1177{ 594{
1178 Atom clipboard; 595 xclipcopy();
1179
1180 if (sel.clipboard != NULL)
1181 free(sel.clipboard);
1182
1183 if (sel.primary != NULL) {
1184 sel.clipboard = xstrdup(sel.primary);
1185 clipboard = XInternAtom(xw.dpy, "CLIPBOARD", 0);
1186 XSetSelectionOwner(xw.dpy, clipboard, xw.win, CurrentTime);
1187 }
1188} 596}
1189 597
1190void 598void
1191clippaste(const Arg *dummy) 599clippaste(const Arg *dummy)
1192{ 600{
1193 Atom clipboard; 601 xclippaste();
1194
1195 clipboard = XInternAtom(xw.dpy, "CLIPBOARD", 0);
1196 XConvertSelection(xw.dpy, clipboard, sel.xtarget, clipboard,
1197 xw.win, CurrentTime);
1198} 602}
1199 603
1200void 604void
1201selclear(XEvent *e) 605selclear(void)
1202{ 606{
1203 if (sel.ob.x == -1) 607 if (sel.ob.x == -1)
1204 return; 608 return;
@@ -1208,120 +612,6 @@ selclear(XEvent *e)
1208} 612}
1209 613
1210void 614void
1211selrequest(XEvent *e)
1212{
1213 XSelectionRequestEvent *xsre;
1214 XSelectionEvent xev;
1215 Atom xa_targets, string, clipboard;
1216 char *seltext;
1217
1218 xsre = (XSelectionRequestEvent *) e;
1219 xev.type = SelectionNotify;
1220 xev.requestor = xsre->requestor;
1221 xev.selection = xsre->selection;
1222 xev.target = xsre->target;
1223 xev.time = xsre->time;
1224 if (xsre->property == None)
1225 xsre->property = xsre->target;
1226
1227 /* reject */
1228 xev.property = None;
1229
1230 xa_targets = XInternAtom(xw.dpy, "TARGETS", 0);
1231 if (xsre->target == xa_targets) {
1232 /* respond with the supported type */
1233 string = sel.xtarget;
1234 XChangeProperty(xsre->display, xsre->requestor, xsre->property,
1235 XA_ATOM, 32, PropModeReplace,
1236 (uchar *) &string, 1);
1237 xev.property = xsre->property;
1238 } else if (xsre->target == sel.xtarget || xsre->target == XA_STRING) {
1239 /*
1240 * xith XA_STRING non ascii characters may be incorrect in the
1241 * requestor. It is not our problem, use utf8.
1242 */
1243 clipboard = XInternAtom(xw.dpy, "CLIPBOARD", 0);
1244 if (xsre->selection == XA_PRIMARY) {
1245 seltext = sel.primary;
1246 } else if (xsre->selection == clipboard) {
1247 seltext = sel.clipboard;
1248 } else {
1249 fprintf(stderr,
1250 "Unhandled clipboard selection 0x%lx\n",
1251 xsre->selection);
1252 return;
1253 }
1254 if (seltext != NULL) {
1255 XChangeProperty(xsre->display, xsre->requestor,
1256 xsre->property, xsre->target,
1257 8, PropModeReplace,
1258 (uchar *)seltext, strlen(seltext));
1259 xev.property = xsre->property;
1260 }
1261 }
1262
1263 /* all done, send a notification to the listener */
1264 if (!XSendEvent(xsre->display, xsre->requestor, 1, 0, (XEvent *) &xev))
1265 fprintf(stderr, "Error sending SelectionNotify event\n");
1266}
1267
1268void
1269xsetsel(char *str, Time t)
1270{
1271 free(sel.primary);
1272 sel.primary = str;
1273
1274 XSetSelectionOwner(xw.dpy, XA_PRIMARY, xw.win, t);
1275 if (XGetSelectionOwner(xw.dpy, XA_PRIMARY) != xw.win)
1276 selclear(0);
1277}
1278
1279void
1280brelease(XEvent *e)
1281{
1282 if (IS_SET(MODE_MOUSE) && !(e->xbutton.state & forceselmod)) {
1283 mousereport(e);
1284 return;
1285 }
1286
1287 if (e->xbutton.button == Button2) {
1288 selpaste(NULL);
1289 } else if (e->xbutton.button == Button1) {
1290 if (sel.mode == SEL_READY) {
1291 getbuttoninfo(e);
1292 selcopy(e->xbutton.time);
1293 } else
1294 selclear(NULL);
1295 sel.mode = SEL_IDLE;
1296 tsetdirt(sel.nb.y, sel.ne.y);
1297 }
1298}
1299
1300void
1301bmotion(XEvent *e)
1302{
1303 int oldey, oldex, oldsby, oldsey;
1304
1305 if (IS_SET(MODE_MOUSE) && !(e->xbutton.state & forceselmod)) {
1306 mousereport(e);
1307 return;
1308 }
1309
1310 if (!sel.mode)
1311 return;
1312
1313 sel.mode = SEL_READY;
1314 oldey = sel.oe.y;
1315 oldex = sel.oe.x;
1316 oldsby = sel.nb.y;
1317 oldsey = sel.ne.y;
1318 getbuttoninfo(e);
1319
1320 if (oldey != sel.oe.y || oldex != sel.oe.x)
1321 tsetdirt(MIN(sel.nb.y, oldsby), MAX(sel.ne.y, oldsey));
1322}
1323
1324void
1325die(const char *errstr, ...) 615die(const char *errstr, ...)
1326{ 616{
1327 va_list ap; 617 va_list ap;
@@ -1337,7 +627,6 @@ execsh(void)
1337{ 627{
1338 char **args, *sh, *prog; 628 char **args, *sh, *prog;
1339 const struct passwd *pw; 629 const struct passwd *pw;
1340 char buf[sizeof(long) * 8 + 1];
1341 630
1342 errno = 0; 631 errno = 0;
1343 if ((pw = getpwuid(getuid())) == NULL) { 632 if ((pw = getpwuid(getuid())) == NULL) {
@@ -1358,8 +647,6 @@ execsh(void)
1358 prog = sh; 647 prog = sh;
1359 args = (opt_cmd) ? opt_cmd : (char *[]) {prog, NULL}; 648 args = (opt_cmd) ? opt_cmd : (char *[]) {prog, NULL};
1360 649
1361 snprintf(buf, sizeof(buf), "%lu", xw.win);
1362
1363 unsetenv("COLUMNS"); 650 unsetenv("COLUMNS");
1364 unsetenv("LINES"); 651 unsetenv("LINES");
1365 unsetenv("TERMCAP"); 652 unsetenv("TERMCAP");
@@ -1368,7 +655,7 @@ execsh(void)
1368 setenv("SHELL", sh, 1); 655 setenv("SHELL", sh, 1);
1369 setenv("HOME", pw->pw_dir, 1); 656 setenv("HOME", pw->pw_dir, 1);
1370 setenv("TERM", termname, 1); 657 setenv("TERM", termname, 1);
1371 setenv("WINDOWID", buf, 1); 658 xsetenv();
1372 659
1373 signal(SIGCHLD, SIG_DFL); 660 signal(SIGCHLD, SIG_DFL);
1374 signal(SIGHUP, SIG_DFL); 661 signal(SIGHUP, SIG_DFL);
@@ -1606,8 +893,8 @@ ttyresize(void)
1606 893
1607 w.ws_row = term.row; 894 w.ws_row = term.row;
1608 w.ws_col = term.col; 895 w.ws_col = term.col;
1609 w.ws_xpixel = xw.tw; 896 w.ws_xpixel = win.tw;
1610 w.ws_ypixel = xw.th; 897 w.ws_ypixel = win.th;
1611 if (ioctl(cmdfd, TIOCSWINSZ, &w) < 0) 898 if (ioctl(cmdfd, TIOCSWINSZ, &w) < 0)
1612 fprintf(stderr, "Couldn't set window size: %s\n", strerror(errno)); 899 fprintf(stderr, "Couldn't set window size: %s\n", strerror(errno));
1613} 900}
@@ -1771,7 +1058,7 @@ selscroll(int orig, int n)
1771 1058
1772 if (BETWEEN(sel.ob.y, orig, term.bot) || BETWEEN(sel.oe.y, orig, term.bot)) { 1059 if (BETWEEN(sel.ob.y, orig, term.bot) || BETWEEN(sel.oe.y, orig, term.bot)) {
1773 if ((sel.ob.y += n) > term.bot || (sel.oe.y += n) < term.top) { 1060 if ((sel.ob.y += n) > term.bot || (sel.oe.y += n) < term.top) {
1774 selclear(NULL); 1061 selclear();
1775 return; 1062 return;
1776 } 1063 }
1777 if (sel.type == SEL_RECTANGULAR) { 1064 if (sel.type == SEL_RECTANGULAR) {
@@ -1917,7 +1204,7 @@ tclearregion(int x1, int y1, int x2, int y2)
1917 for (x = x1; x <= x2; x++) { 1204 for (x = x1; x <= x2; x++) {
1918 gp = &term.line[y][x]; 1205 gp = &term.line[y][x];
1919 if (selected(x, y)) 1206 if (selected(x, y))
1920 selclear(NULL); 1207 selclear();
1921 gp->fg = term.c.attr.fg; 1208 gp->fg = term.c.attr.fg;
1922 gp->bg = term.c.attr.bg; 1209 gp->bg = term.c.attr.bg;
1923 gp->mode = 0; 1210 gp->mode = 0;
@@ -2368,7 +1655,7 @@ csihandle(void)
2368 tputtab(csiescseq.arg[0]); 1655 tputtab(csiescseq.arg[0]);
2369 break; 1656 break;
2370 case 'J': /* ED -- Clear screen */ 1657 case 'J': /* ED -- Clear screen */
2371 selclear(NULL); 1658 selclear();
2372 switch (csiescseq.arg[0]) { 1659 switch (csiescseq.arg[0]) {
2373 case 0: /* below */ 1660 case 0: /* below */
2374 tclearregion(term.c.x, term.c.y, term.col-1, term.c.y); 1661 tclearregion(term.c.x, term.c.y, term.col-1, term.c.y);
@@ -2475,7 +1762,7 @@ csihandle(void)
2475 if (!BETWEEN(csiescseq.arg[0], 0, 6)) { 1762 if (!BETWEEN(csiescseq.arg[0], 0, 6)) {
2476 goto unknown; 1763 goto unknown;
2477 } 1764 }
2478 xw.cursor = csiescseq.arg[0]; 1765 win.cursor = csiescseq.arg[0];
2479 break; 1766 break;
2480 default: 1767 default:
2481 goto unknown; 1768 goto unknown;
@@ -2642,12 +1929,13 @@ tprinter(char *s, size_t len)
2642void 1929void
2643iso14755(const Arg *arg) 1930iso14755(const Arg *arg)
2644{ 1931{
2645 char cmd[sizeof(ISO14755CMD) + NUMMAXLEN(xw.win)]; 1932 unsigned long id = xwinid();
1933 char cmd[sizeof(ISO14755CMD) + NUMMAXLEN(id)];
2646 FILE *p; 1934 FILE *p;
2647 char *us, *e, codepoint[9], uc[UTF_SIZ]; 1935 char *us, *e, codepoint[9], uc[UTF_SIZ];
2648 unsigned long utf32; 1936 unsigned long utf32;
2649 1937
2650 snprintf(cmd, sizeof(cmd), ISO14755CMD, xw.win); 1938 snprintf(cmd, sizeof(cmd), ISO14755CMD, id);
2651 if (!(p = popen(cmd, "r"))) 1939 if (!(p = popen(cmd, "r")))
2652 return; 1940 return;
2653 1941
@@ -2833,10 +2121,10 @@ tcontrolcode(uchar ascii)
2833 /* backwards compatibility to xterm */ 2121 /* backwards compatibility to xterm */
2834 strhandle(); 2122 strhandle();
2835 } else { 2123 } else {
2836 if (!(xw.state & WIN_FOCUSED)) 2124 if (!(win.state & WIN_FOCUSED))
2837 xseturgency(1); 2125 xseturgency(1);
2838 if (bellvolume) 2126 if (bellvolume)
2839 XkbBell(xw.dpy, xw.win, bellvolume, (Atom)NULL); 2127 xbell(bellvolume);
2840 } 2128 }
2841 break; 2129 break;
2842 case '\033': /* ESC */ 2130 case '\033': /* ESC */
@@ -2968,7 +2256,7 @@ eschandle(uchar ascii)
2968 break; 2256 break;
2969 case 'c': /* RIS -- Reset to inital state */ 2257 case 'c': /* RIS -- Reset to inital state */
2970 treset(); 2258 treset();
2971 xresettitle(); 2259 resettitle();
2972 xloadcols(); 2260 xloadcols();
2973 break; 2261 break;
2974 case '=': /* DECPAM -- Application keypad */ 2262 case '=': /* DECPAM -- Application keypad */
@@ -3109,7 +2397,7 @@ check_control_code:
3109 return; 2397 return;
3110 } 2398 }
3111 if (sel.ob.x != -1 && BETWEEN(term.c.y, sel.ob.y, sel.oe.y)) 2399 if (sel.ob.x != -1 && BETWEEN(term.c.y, sel.ob.y, sel.oe.y))
3112 selclear(NULL); 2400 selclear();
3113 2401
3114 gp = &term.line[term.c.y][term.c.x]; 2402 gp = &term.line[term.c.y][term.c.x];
3115 if (IS_SET(MODE_WRAP) && (term.c.state & CURSOR_WRAPNEXT)) { 2403 if (IS_SET(MODE_WRAP) && (term.c.state & CURSOR_WRAPNEXT)) {
@@ -3177,7 +2465,7 @@ tresize(int col, int row)
3177 } 2465 }
3178 2466
3179 /* resize to new width */ 2467 /* resize to new width */
3180 term.specbuf = xrealloc(term.specbuf, col * sizeof(XftGlyphFontSpec)); 2468 term.specbuf = xrealloc(term.specbuf, col * sizeof(GlyphFontSpec));
3181 2469
3182 /* resize to new height */ 2470 /* resize to new height */
3183 term.line = xrealloc(term.line, row * sizeof(Line)); 2471 term.line = xrealloc(term.line, row * sizeof(Line));
@@ -3228,326 +2516,16 @@ tresize(int col, int row)
3228} 2516}
3229 2517
3230void 2518void
3231xresize(int col, int row) 2519zoom(const Arg *arg)
3232{
3233 xw.tw = MAX(1, col * xw.cw);
3234 xw.th = MAX(1, row * xw.ch);
3235
3236 XFreePixmap(xw.dpy, xw.buf);
3237 xw.buf = XCreatePixmap(xw.dpy, xw.win, xw.w, xw.h,
3238 DefaultDepth(xw.dpy, xw.scr));
3239 XftDrawChange(xw.draw, xw.buf);
3240 xclear(0, 0, xw.w, xw.h);
3241}
3242
3243ushort
3244sixd_to_16bit(int x)
3245{
3246 return x == 0 ? 0 : 0x3737 + 0x2828 * x;
3247}
3248
3249int
3250xloadcolor(int i, const char *name, Color *ncolor)
3251{
3252 XRenderColor color = { .alpha = 0xffff };
3253
3254 if (!name) {
3255 if (BETWEEN(i, 16, 255)) { /* 256 color */
3256 if (i < 6*6*6+16) { /* same colors as xterm */
3257 color.red = sixd_to_16bit( ((i-16)/36)%6 );
3258 color.green = sixd_to_16bit( ((i-16)/6) %6 );
3259 color.blue = sixd_to_16bit( ((i-16)/1) %6 );
3260 } else { /* greyscale */
3261 color.red = 0x0808 + 0x0a0a * (i - (6*6*6+16));
3262 color.green = color.blue = color.red;
3263 }
3264 return XftColorAllocValue(xw.dpy, xw.vis,
3265 xw.cmap, &color, ncolor);
3266 } else
3267 name = colorname[i];
3268 }
3269
3270 return XftColorAllocName(xw.dpy, xw.vis, xw.cmap, name, ncolor);
3271}
3272
3273void
3274xloadcols(void)
3275{
3276 int i;
3277 static int loaded;
3278 Color *cp;
3279
3280 if (loaded) {
3281 for (cp = dc.col; cp < &dc.col[LEN(dc.col)]; ++cp)
3282 XftColorFree(xw.dpy, xw.vis, xw.cmap, cp);
3283 }
3284
3285 for (i = 0; i < LEN(dc.col); i++)
3286 if (!xloadcolor(i, NULL, &dc.col[i])) {
3287 if (colorname[i])
3288 die("Could not allocate color '%s'\n", colorname[i]);
3289 else
3290 die("Could not allocate color %d\n", i);
3291 }
3292 loaded = 1;
3293}
3294
3295int
3296xsetcolorname(int x, const char *name)
3297{
3298 Color ncolor;
3299
3300 if (!BETWEEN(x, 0, LEN(dc.col)))
3301 return 1;
3302
3303
3304 if (!xloadcolor(x, name, &ncolor))
3305 return 1;
3306
3307 XftColorFree(xw.dpy, xw.vis, xw.cmap, &dc.col[x]);
3308 dc.col[x] = ncolor;
3309
3310 return 0;
3311}
3312
3313/*
3314 * Absolute coordinates.
3315 */
3316void
3317xclear(int x1, int y1, int x2, int y2)
3318{
3319 XftDrawRect(xw.draw,
3320 &dc.col[IS_SET(MODE_REVERSE)? defaultfg : defaultbg],
3321 x1, y1, x2-x1, y2-y1);
3322}
3323
3324void
3325xhints(void)
3326{
3327 XClassHint class = {opt_name ? opt_name : termname,
3328 opt_class ? opt_class : termname};
3329 XWMHints wm = {.flags = InputHint, .input = 1};
3330 XSizeHints *sizeh = NULL;
3331
3332 sizeh = XAllocSizeHints();
3333
3334 sizeh->flags = PSize | PResizeInc | PBaseSize;
3335 sizeh->height = xw.h;
3336 sizeh->width = xw.w;
3337 sizeh->height_inc = xw.ch;
3338 sizeh->width_inc = xw.cw;
3339 sizeh->base_height = 2 * borderpx;
3340 sizeh->base_width = 2 * borderpx;
3341 if (xw.isfixed) {
3342 sizeh->flags |= PMaxSize | PMinSize;
3343 sizeh->min_width = sizeh->max_width = xw.w;
3344 sizeh->min_height = sizeh->max_height = xw.h;
3345 }
3346 if (xw.gm & (XValue|YValue)) {
3347 sizeh->flags |= USPosition | PWinGravity;
3348 sizeh->x = xw.l;
3349 sizeh->y = xw.t;
3350 sizeh->win_gravity = xgeommasktogravity(xw.gm);
3351 }
3352
3353 XSetWMProperties(xw.dpy, xw.win, NULL, NULL, NULL, 0, sizeh, &wm,
3354 &class);
3355 XFree(sizeh);
3356}
3357
3358int
3359xgeommasktogravity(int mask)
3360{
3361 switch (mask & (XNegative|YNegative)) {
3362 case 0:
3363 return NorthWestGravity;
3364 case XNegative:
3365 return NorthEastGravity;
3366 case YNegative:
3367 return SouthWestGravity;
3368 }
3369
3370 return SouthEastGravity;
3371}
3372
3373int
3374xloadfont(Font *f, FcPattern *pattern)
3375{
3376 FcPattern *configured;
3377 FcPattern *match;
3378 FcResult result;
3379 XGlyphInfo extents;
3380 int wantattr, haveattr;
3381
3382 /*
3383 * Manually configure instead of calling XftMatchFont
3384 * so that we can use the configured pattern for
3385 * "missing glyph" lookups.
3386 */
3387 configured = FcPatternDuplicate(pattern);
3388 if (!configured)
3389 return 1;
3390
3391 FcConfigSubstitute(NULL, configured, FcMatchPattern);
3392 XftDefaultSubstitute(xw.dpy, xw.scr, configured);
3393
3394 match = FcFontMatch(NULL, configured, &result);
3395 if (!match) {
3396 FcPatternDestroy(configured);
3397 return 1;
3398 }
3399
3400 if (!(f->match = XftFontOpenPattern(xw.dpy, match))) {
3401 FcPatternDestroy(configured);
3402 FcPatternDestroy(match);
3403 return 1;
3404 }
3405
3406 if ((XftPatternGetInteger(pattern, "slant", 0, &wantattr) ==
3407 XftResultMatch)) {
3408 /*
3409 * Check if xft was unable to find a font with the appropriate
3410 * slant but gave us one anyway. Try to mitigate.
3411 */
3412 if ((XftPatternGetInteger(f->match->pattern, "slant", 0,
3413 &haveattr) != XftResultMatch) || haveattr < wantattr) {
3414 f->badslant = 1;
3415 fputs("st: font slant does not match\n", stderr);
3416 }
3417 }
3418
3419 if ((XftPatternGetInteger(pattern, "weight", 0, &wantattr) ==
3420 XftResultMatch)) {
3421 if ((XftPatternGetInteger(f->match->pattern, "weight", 0,
3422 &haveattr) != XftResultMatch) || haveattr != wantattr) {
3423 f->badweight = 1;
3424 fputs("st: font weight does not match\n", stderr);
3425 }
3426 }
3427
3428 XftTextExtentsUtf8(xw.dpy, f->match,
3429 (const FcChar8 *) ascii_printable,
3430 strlen(ascii_printable), &extents);
3431
3432 f->set = NULL;
3433 f->pattern = configured;
3434
3435 f->ascent = f->match->ascent;
3436 f->descent = f->match->descent;
3437 f->lbearing = 0;
3438 f->rbearing = f->match->max_advance_width;
3439
3440 f->height = f->ascent + f->descent;
3441 f->width = DIVCEIL(extents.xOff, strlen(ascii_printable));
3442
3443 return 0;
3444}
3445
3446void
3447xloadfonts(char *fontstr, double fontsize)
3448{
3449 FcPattern *pattern;
3450 double fontval;
3451 float ceilf(float);
3452
3453 if (fontstr[0] == '-') {
3454 pattern = XftXlfdParse(fontstr, False, False);
3455 } else {
3456 pattern = FcNameParse((FcChar8 *)fontstr);
3457 }
3458
3459 if (!pattern)
3460 die("st: can't open font %s\n", fontstr);
3461
3462 if (fontsize > 1) {
3463 FcPatternDel(pattern, FC_PIXEL_SIZE);
3464 FcPatternDel(pattern, FC_SIZE);
3465 FcPatternAddDouble(pattern, FC_PIXEL_SIZE, (double)fontsize);
3466 usedfontsize = fontsize;
3467 } else {
3468 if (FcPatternGetDouble(pattern, FC_PIXEL_SIZE, 0, &fontval) ==
3469 FcResultMatch) {
3470 usedfontsize = fontval;
3471 } else if (FcPatternGetDouble(pattern, FC_SIZE, 0, &fontval) ==
3472 FcResultMatch) {
3473 usedfontsize = -1;
3474 } else {
3475 /*
3476 * Default font size is 12, if none given. This is to
3477 * have a known usedfontsize value.
3478 */
3479 FcPatternAddDouble(pattern, FC_PIXEL_SIZE, 12);
3480 usedfontsize = 12;
3481 }
3482 defaultfontsize = usedfontsize;
3483 }
3484
3485 if (xloadfont(&dc.font, pattern))
3486 die("st: can't open font %s\n", fontstr);
3487
3488 if (usedfontsize < 0) {
3489 FcPatternGetDouble(dc.font.match->pattern,
3490 FC_PIXEL_SIZE, 0, &fontval);
3491 usedfontsize = fontval;
3492 if (fontsize == 0)
3493 defaultfontsize = fontval;
3494 }
3495
3496 /* Setting character width and height. */
3497 xw.cw = ceilf(dc.font.width * cwscale);
3498 xw.ch = ceilf(dc.font.height * chscale);
3499
3500 FcPatternDel(pattern, FC_SLANT);
3501 FcPatternAddInteger(pattern, FC_SLANT, FC_SLANT_ITALIC);
3502 if (xloadfont(&dc.ifont, pattern))
3503 die("st: can't open font %s\n", fontstr);
3504
3505 FcPatternDel(pattern, FC_WEIGHT);
3506 FcPatternAddInteger(pattern, FC_WEIGHT, FC_WEIGHT_BOLD);
3507 if (xloadfont(&dc.ibfont, pattern))
3508 die("st: can't open font %s\n", fontstr);
3509
3510 FcPatternDel(pattern, FC_SLANT);
3511 FcPatternAddInteger(pattern, FC_SLANT, FC_SLANT_ROMAN);
3512 if (xloadfont(&dc.bfont, pattern))
3513 die("st: can't open font %s\n", fontstr);
3514
3515 FcPatternDestroy(pattern);
3516}
3517
3518void
3519xunloadfont(Font *f)
3520{
3521 XftFontClose(xw.dpy, f->match);
3522 FcPatternDestroy(f->pattern);
3523 if (f->set)
3524 FcFontSetDestroy(f->set);
3525}
3526
3527void
3528xunloadfonts(void)
3529{
3530 /* Free the loaded fonts in the font cache. */
3531 while (frclen > 0)
3532 XftFontClose(xw.dpy, frc[--frclen].font);
3533
3534 xunloadfont(&dc.font);
3535 xunloadfont(&dc.bfont);
3536 xunloadfont(&dc.ifont);
3537 xunloadfont(&dc.ibfont);
3538}
3539
3540void
3541xzoom(const Arg *arg)
3542{ 2520{
3543 Arg larg; 2521 Arg larg;
3544 2522
3545 larg.f = usedfontsize + arg->f; 2523 larg.f = usedfontsize + arg->f;
3546 xzoomabs(&larg); 2524 zoomabs(&larg);
3547} 2525}
3548 2526
3549void 2527void
3550xzoomabs(const Arg *arg) 2528zoomabs(const Arg *arg)
3551{ 2529{
3552 xunloadfonts(); 2530 xunloadfonts();
3553 xloadfonts(usedfont, arg->f); 2531 xloadfonts(usedfont, arg->f);
@@ -3558,520 +2536,18 @@ xzoomabs(const Arg *arg)
3558} 2536}
3559 2537
3560void 2538void
3561xzoomreset(const Arg *arg) 2539zoomreset(const Arg *arg)
3562{ 2540{
3563 Arg larg; 2541 Arg larg;
3564 2542
3565 if (defaultfontsize > 0) { 2543 if (defaultfontsize > 0) {
3566 larg.f = defaultfontsize; 2544 larg.f = defaultfontsize;
3567 xzoomabs(&larg); 2545 zoomabs(&larg);
3568 }
3569}
3570
3571void
3572xinit(void)
3573{
3574 XGCValues gcvalues;
3575 Cursor cursor;
3576 Window parent;
3577 pid_t thispid = getpid();
3578 XColor xmousefg, xmousebg;
3579
3580 if (!(xw.dpy = XOpenDisplay(NULL)))
3581 die("Can't open display\n");
3582 xw.scr = XDefaultScreen(xw.dpy);
3583 xw.vis = XDefaultVisual(xw.dpy, xw.scr);
3584
3585 /* font */
3586 if (!FcInit())
3587 die("Could not init fontconfig.\n");
3588
3589 usedfont = (opt_font == NULL)? font : opt_font;
3590 xloadfonts(usedfont, 0);
3591
3592 /* colors */
3593 xw.cmap = XDefaultColormap(xw.dpy, xw.scr);
3594 xloadcols();
3595
3596 /* adjust fixed window geometry */
3597 xw.w = 2 * borderpx + term.col * xw.cw;
3598 xw.h = 2 * borderpx + term.row * xw.ch;
3599 if (xw.gm & XNegative)
3600 xw.l += DisplayWidth(xw.dpy, xw.scr) - xw.w - 2;
3601 if (xw.gm & YNegative)
3602 xw.t += DisplayHeight(xw.dpy, xw.scr) - xw.h - 2;
3603
3604 /* Events */
3605 xw.attrs.background_pixel = dc.col[defaultbg].pixel;
3606 xw.attrs.border_pixel = dc.col[defaultbg].pixel;
3607 xw.attrs.bit_gravity = NorthWestGravity;
3608 xw.attrs.event_mask = FocusChangeMask | KeyPressMask
3609 | ExposureMask | VisibilityChangeMask | StructureNotifyMask
3610 | ButtonMotionMask | ButtonPressMask | ButtonReleaseMask;
3611 xw.attrs.colormap = xw.cmap;
3612
3613 if (!(opt_embed && (parent = strtol(opt_embed, NULL, 0))))
3614 parent = XRootWindow(xw.dpy, xw.scr);
3615 xw.win = XCreateWindow(xw.dpy, parent, xw.l, xw.t,
3616 xw.w, xw.h, 0, XDefaultDepth(xw.dpy, xw.scr), InputOutput,
3617 xw.vis, CWBackPixel | CWBorderPixel | CWBitGravity
3618 | CWEventMask | CWColormap, &xw.attrs);
3619
3620 memset(&gcvalues, 0, sizeof(gcvalues));
3621 gcvalues.graphics_exposures = False;
3622 dc.gc = XCreateGC(xw.dpy, parent, GCGraphicsExposures,
3623 &gcvalues);
3624 xw.buf = XCreatePixmap(xw.dpy, xw.win, xw.w, xw.h,
3625 DefaultDepth(xw.dpy, xw.scr));
3626 XSetForeground(xw.dpy, dc.gc, dc.col[defaultbg].pixel);
3627 XFillRectangle(xw.dpy, xw.buf, dc.gc, 0, 0, xw.w, xw.h);
3628
3629 /* Xft rendering context */
3630 xw.draw = XftDrawCreate(xw.dpy, xw.buf, xw.vis, xw.cmap);
3631
3632 /* input methods */
3633 if ((xw.xim = XOpenIM(xw.dpy, NULL, NULL, NULL)) == NULL) {
3634 XSetLocaleModifiers("@im=local");
3635 if ((xw.xim = XOpenIM(xw.dpy, NULL, NULL, NULL)) == NULL) {
3636 XSetLocaleModifiers("@im=");
3637 if ((xw.xim = XOpenIM(xw.dpy,
3638 NULL, NULL, NULL)) == NULL) {
3639 die("XOpenIM failed. Could not open input"
3640 " device.\n");
3641 }
3642 }
3643 }
3644 xw.xic = XCreateIC(xw.xim, XNInputStyle, XIMPreeditNothing
3645 | XIMStatusNothing, XNClientWindow, xw.win,
3646 XNFocusWindow, xw.win, NULL);
3647 if (xw.xic == NULL)
3648 die("XCreateIC failed. Could not obtain input method.\n");
3649
3650 /* white cursor, black outline */
3651 cursor = XCreateFontCursor(xw.dpy, mouseshape);
3652 XDefineCursor(xw.dpy, xw.win, cursor);
3653
3654 if (XParseColor(xw.dpy, xw.cmap, colorname[mousefg], &xmousefg) == 0) {
3655 xmousefg.red = 0xffff;
3656 xmousefg.green = 0xffff;
3657 xmousefg.blue = 0xffff;
3658 }
3659
3660 if (XParseColor(xw.dpy, xw.cmap, colorname[mousebg], &xmousebg) == 0) {
3661 xmousebg.red = 0x0000;
3662 xmousebg.green = 0x0000;
3663 xmousebg.blue = 0x0000;
3664 }
3665
3666 XRecolorCursor(xw.dpy, cursor, &xmousefg, &xmousebg);
3667
3668 xw.xembed = XInternAtom(xw.dpy, "_XEMBED", False);
3669 xw.wmdeletewin = XInternAtom(xw.dpy, "WM_DELETE_WINDOW", False);
3670 xw.netwmname = XInternAtom(xw.dpy, "_NET_WM_NAME", False);
3671 XSetWMProtocols(xw.dpy, xw.win, &xw.wmdeletewin, 1);
3672
3673 xw.netwmpid = XInternAtom(xw.dpy, "_NET_WM_PID", False);
3674 XChangeProperty(xw.dpy, xw.win, xw.netwmpid, XA_CARDINAL, 32,
3675 PropModeReplace, (uchar *)&thispid, 1);
3676
3677 xresettitle();
3678 XMapWindow(xw.dpy, xw.win);
3679 xhints();
3680 XSync(xw.dpy, False);
3681}
3682
3683int
3684xmakeglyphfontspecs(XftGlyphFontSpec *specs, const Glyph *glyphs, int len, int x, int y)
3685{
3686 float winx = borderpx + x * xw.cw, winy = borderpx + y * xw.ch, xp, yp;
3687 ushort mode, prevmode = USHRT_MAX;
3688 Font *font = &dc.font;
3689 int frcflags = FRC_NORMAL;
3690 float runewidth = xw.cw;
3691 Rune rune;
3692 FT_UInt glyphidx;
3693 FcResult fcres;
3694 FcPattern *fcpattern, *fontpattern;
3695 FcFontSet *fcsets[] = { NULL };
3696 FcCharSet *fccharset;
3697 int i, f, numspecs = 0;
3698
3699 for (i = 0, xp = winx, yp = winy + font->ascent; i < len; ++i) {
3700 /* Fetch rune and mode for current glyph. */
3701 rune = glyphs[i].u;
3702 mode = glyphs[i].mode;
3703
3704 /* Skip dummy wide-character spacing. */
3705 if (mode == ATTR_WDUMMY)
3706 continue;
3707
3708 /* Determine font for glyph if different from previous glyph. */
3709 if (prevmode != mode) {
3710 prevmode = mode;
3711 font = &dc.font;
3712 frcflags = FRC_NORMAL;
3713 runewidth = xw.cw * ((mode & ATTR_WIDE) ? 2.0f : 1.0f);
3714 if ((mode & ATTR_ITALIC) && (mode & ATTR_BOLD)) {
3715 font = &dc.ibfont;
3716 frcflags = FRC_ITALICBOLD;
3717 } else if (mode & ATTR_ITALIC) {
3718 font = &dc.ifont;
3719 frcflags = FRC_ITALIC;
3720 } else if (mode & ATTR_BOLD) {
3721 font = &dc.bfont;
3722 frcflags = FRC_BOLD;
3723 }
3724 yp = winy + font->ascent;
3725 }
3726
3727 /* Lookup character index with default font. */
3728 glyphidx = XftCharIndex(xw.dpy, font->match, rune);
3729 if (glyphidx) {
3730 specs[numspecs].font = font->match;
3731 specs[numspecs].glyph = glyphidx;
3732 specs[numspecs].x = (short)xp;
3733 specs[numspecs].y = (short)yp;
3734 xp += runewidth;
3735 numspecs++;
3736 continue;
3737 }
3738
3739 /* Fallback on font cache, search the font cache for match. */
3740 for (f = 0; f < frclen; f++) {
3741 glyphidx = XftCharIndex(xw.dpy, frc[f].font, rune);
3742 /* Everything correct. */
3743 if (glyphidx && frc[f].flags == frcflags)
3744 break;
3745 /* We got a default font for a not found glyph. */
3746 if (!glyphidx && frc[f].flags == frcflags
3747 && frc[f].unicodep == rune) {
3748 break;
3749 }
3750 }
3751
3752 /* Nothing was found. Use fontconfig to find matching font. */
3753 if (f >= frclen) {
3754 if (!font->set)
3755 font->set = FcFontSort(0, font->pattern,
3756 1, 0, &fcres);
3757 fcsets[0] = font->set;
3758
3759 /*
3760 * Nothing was found in the cache. Now use
3761 * some dozen of Fontconfig calls to get the
3762 * font for one single character.
3763 *
3764 * Xft and fontconfig are design failures.
3765 */
3766 fcpattern = FcPatternDuplicate(font->pattern);
3767 fccharset = FcCharSetCreate();
3768
3769 FcCharSetAddChar(fccharset, rune);
3770 FcPatternAddCharSet(fcpattern, FC_CHARSET,
3771 fccharset);
3772 FcPatternAddBool(fcpattern, FC_SCALABLE, 1);
3773
3774 FcConfigSubstitute(0, fcpattern,
3775 FcMatchPattern);
3776 FcDefaultSubstitute(fcpattern);
3777
3778 fontpattern = FcFontSetMatch(0, fcsets, 1,
3779 fcpattern, &fcres);
3780
3781 /*
3782 * Overwrite or create the new cache entry.
3783 */
3784 if (frclen >= LEN(frc)) {
3785 frclen = LEN(frc) - 1;
3786 XftFontClose(xw.dpy, frc[frclen].font);
3787 frc[frclen].unicodep = 0;
3788 }
3789
3790 frc[frclen].font = XftFontOpenPattern(xw.dpy,
3791 fontpattern);
3792 frc[frclen].flags = frcflags;
3793 frc[frclen].unicodep = rune;
3794
3795 glyphidx = XftCharIndex(xw.dpy, frc[frclen].font, rune);
3796
3797 f = frclen;
3798 frclen++;
3799
3800 FcPatternDestroy(fcpattern);
3801 FcCharSetDestroy(fccharset);
3802 }
3803
3804 specs[numspecs].font = frc[f].font;
3805 specs[numspecs].glyph = glyphidx;
3806 specs[numspecs].x = (short)xp;
3807 specs[numspecs].y = (short)yp;
3808 xp += runewidth;
3809 numspecs++;
3810 }
3811
3812 return numspecs;
3813}
3814
3815void
3816xdrawglyphfontspecs(const XftGlyphFontSpec *specs, Glyph base, int len, int x, int y)
3817{
3818 int charlen = len * ((base.mode & ATTR_WIDE) ? 2 : 1);
3819 int winx = borderpx + x * xw.cw, winy = borderpx + y * xw.ch,
3820 width = charlen * xw.cw;
3821 Color *fg, *bg, *temp, revfg, revbg, truefg, truebg;
3822 XRenderColor colfg, colbg;
3823 XRectangle r;
3824
3825 /* Fallback on color display for attributes not supported by the font */
3826 if (base.mode & ATTR_ITALIC && base.mode & ATTR_BOLD) {
3827 if (dc.ibfont.badslant || dc.ibfont.badweight)
3828 base.fg = defaultattr;
3829 } else if ((base.mode & ATTR_ITALIC && dc.ifont.badslant) ||
3830 (base.mode & ATTR_BOLD && dc.bfont.badweight)) {
3831 base.fg = defaultattr;
3832 }
3833
3834 if (IS_TRUECOL(base.fg)) {
3835 colfg.alpha = 0xffff;
3836 colfg.red = TRUERED(base.fg);
3837 colfg.green = TRUEGREEN(base.fg);
3838 colfg.blue = TRUEBLUE(base.fg);
3839 XftColorAllocValue(xw.dpy, xw.vis, xw.cmap, &colfg, &truefg);
3840 fg = &truefg;
3841 } else {
3842 fg = &dc.col[base.fg];
3843 }
3844
3845 if (IS_TRUECOL(base.bg)) {
3846 colbg.alpha = 0xffff;
3847 colbg.green = TRUEGREEN(base.bg);
3848 colbg.red = TRUERED(base.bg);
3849 colbg.blue = TRUEBLUE(base.bg);
3850 XftColorAllocValue(xw.dpy, xw.vis, xw.cmap, &colbg, &truebg);
3851 bg = &truebg;
3852 } else {
3853 bg = &dc.col[base.bg];
3854 }
3855
3856 /* Change basic system colors [0-7] to bright system colors [8-15] */
3857 if ((base.mode & ATTR_BOLD_FAINT) == ATTR_BOLD && BETWEEN(base.fg, 0, 7))
3858 fg = &dc.col[base.fg + 8];
3859
3860 if (IS_SET(MODE_REVERSE)) {
3861 if (fg == &dc.col[defaultfg]) {
3862 fg = &dc.col[defaultbg];
3863 } else {
3864 colfg.red = ~fg->color.red;
3865 colfg.green = ~fg->color.green;
3866 colfg.blue = ~fg->color.blue;
3867 colfg.alpha = fg->color.alpha;
3868 XftColorAllocValue(xw.dpy, xw.vis, xw.cmap, &colfg,
3869 &revfg);
3870 fg = &revfg;
3871 }
3872
3873 if (bg == &dc.col[defaultbg]) {
3874 bg = &dc.col[defaultfg];
3875 } else {
3876 colbg.red = ~bg->color.red;
3877 colbg.green = ~bg->color.green;
3878 colbg.blue = ~bg->color.blue;
3879 colbg.alpha = bg->color.alpha;
3880 XftColorAllocValue(xw.dpy, xw.vis, xw.cmap, &colbg,
3881 &revbg);
3882 bg = &revbg;
3883 }
3884 }
3885
3886 if (base.mode & ATTR_REVERSE) {
3887 temp = fg;
3888 fg = bg;
3889 bg = temp;
3890 }
3891
3892 if ((base.mode & ATTR_BOLD_FAINT) == ATTR_FAINT) {
3893 colfg.red = fg->color.red / 2;
3894 colfg.green = fg->color.green / 2;
3895 colfg.blue = fg->color.blue / 2;
3896 XftColorAllocValue(xw.dpy, xw.vis, xw.cmap, &colfg, &revfg);
3897 fg = &revfg;
3898 }
3899
3900 if (base.mode & ATTR_BLINK && term.mode & MODE_BLINK)
3901 fg = bg;
3902
3903 if (base.mode & ATTR_INVISIBLE)
3904 fg = bg;
3905
3906 /* Intelligent cleaning up of the borders. */
3907 if (x == 0) {
3908 xclear(0, (y == 0)? 0 : winy, borderpx,
3909 winy + xw.ch + ((y >= term.row-1)? xw.h : 0));
3910 }
3911 if (x + charlen >= term.col) {
3912 xclear(winx + width, (y == 0)? 0 : winy, xw.w,
3913 ((y >= term.row-1)? xw.h : (winy + xw.ch)));
3914 }
3915 if (y == 0)
3916 xclear(winx, 0, winx + width, borderpx);
3917 if (y == term.row-1)
3918 xclear(winx, winy + xw.ch, winx + width, xw.h);
3919
3920 /* Clean up the region we want to draw to. */
3921 XftDrawRect(xw.draw, bg, winx, winy, width, xw.ch);
3922
3923 /* Set the clip region because Xft is sometimes dirty. */
3924 r.x = 0;
3925 r.y = 0;
3926 r.height = xw.ch;
3927 r.width = width;
3928 XftDrawSetClipRectangles(xw.draw, winx, winy, &r, 1);
3929
3930 /* Render the glyphs. */
3931 XftDrawGlyphFontSpec(xw.draw, fg, specs, len);
3932
3933 /* Render underline and strikethrough. */
3934 if (base.mode & ATTR_UNDERLINE) {
3935 XftDrawRect(xw.draw, fg, winx, winy + dc.font.ascent + 1,
3936 width, 1);
3937 }
3938
3939 if (base.mode & ATTR_STRUCK) {
3940 XftDrawRect(xw.draw, fg, winx, winy + 2 * dc.font.ascent / 3,
3941 width, 1);
3942 }
3943
3944 /* Reset clip to none. */
3945 XftDrawSetClip(xw.draw, 0);
3946}
3947
3948void
3949xdrawglyph(Glyph g, int x, int y)
3950{
3951 int numspecs;
3952 XftGlyphFontSpec spec;
3953
3954 numspecs = xmakeglyphfontspecs(&spec, &g, 1, x, y);
3955 xdrawglyphfontspecs(&spec, g, numspecs, x, y);
3956}
3957
3958void
3959xdrawcursor(void)
3960{
3961 static int oldx = 0, oldy = 0;
3962 int curx;
3963 Glyph g = {' ', ATTR_NULL, defaultbg, defaultcs}, og;
3964 int ena_sel = sel.ob.x != -1 && sel.alt == IS_SET(MODE_ALTSCREEN);
3965 Color drawcol;
3966
3967 LIMIT(oldx, 0, term.col-1);
3968 LIMIT(oldy, 0, term.row-1);
3969
3970 curx = term.c.x;
3971
3972 /* adjust position if in dummy */
3973 if (term.line[oldy][oldx].mode & ATTR_WDUMMY)
3974 oldx--;
3975 if (term.line[term.c.y][curx].mode & ATTR_WDUMMY)
3976 curx--;
3977
3978 /* remove the old cursor */
3979 og = term.line[oldy][oldx];
3980 if (ena_sel && selected(oldx, oldy))
3981 og.mode ^= ATTR_REVERSE;
3982 xdrawglyph(og, oldx, oldy);
3983
3984 g.u = term.line[term.c.y][term.c.x].u;
3985
3986 /*
3987 * Select the right color for the right mode.
3988 */
3989 if (IS_SET(MODE_REVERSE)) {
3990 g.mode |= ATTR_REVERSE;
3991 g.bg = defaultfg;
3992 if (ena_sel && selected(term.c.x, term.c.y)) {
3993 drawcol = dc.col[defaultcs];
3994 g.fg = defaultrcs;
3995 } else {
3996 drawcol = dc.col[defaultrcs];
3997 g.fg = defaultcs;
3998 }
3999 } else {
4000 if (ena_sel && selected(term.c.x, term.c.y)) {
4001 drawcol = dc.col[defaultrcs];
4002 g.fg = defaultfg;
4003 g.bg = defaultrcs;
4004 } else {
4005 drawcol = dc.col[defaultcs];
4006 }
4007 }
4008
4009 if (IS_SET(MODE_HIDE))
4010 return;
4011
4012 /* draw the new one */
4013 if (xw.state & WIN_FOCUSED) {
4014 switch (xw.cursor) {
4015 case 7: /* st extension: snowman */
4016 utf8decode("☃", &g.u, UTF_SIZ);
4017 case 0: /* Blinking Block */
4018 case 1: /* Blinking Block (Default) */
4019 case 2: /* Steady Block */
4020 g.mode |= term.line[term.c.y][curx].mode & ATTR_WIDE;
4021 xdrawglyph(g, term.c.x, term.c.y);
4022 break;
4023 case 3: /* Blinking Underline */
4024 case 4: /* Steady Underline */
4025 XftDrawRect(xw.draw, &drawcol,
4026 borderpx + curx * xw.cw,
4027 borderpx + (term.c.y + 1) * xw.ch - \
4028 cursorthickness,
4029 xw.cw, cursorthickness);
4030 break;
4031 case 5: /* Blinking bar */
4032 case 6: /* Steady bar */
4033 XftDrawRect(xw.draw, &drawcol,
4034 borderpx + curx * xw.cw,
4035 borderpx + term.c.y * xw.ch,
4036 cursorthickness, xw.ch);
4037 break;
4038 }
4039 } else {
4040 XftDrawRect(xw.draw, &drawcol,
4041 borderpx + curx * xw.cw,
4042 borderpx + term.c.y * xw.ch,
4043 xw.cw - 1, 1);
4044 XftDrawRect(xw.draw, &drawcol,
4045 borderpx + curx * xw.cw,
4046 borderpx + term.c.y * xw.ch,
4047 1, xw.ch - 1);
4048 XftDrawRect(xw.draw, &drawcol,
4049 borderpx + (curx + 1) * xw.cw - 1,
4050 borderpx + term.c.y * xw.ch,
4051 1, xw.ch - 1);
4052 XftDrawRect(xw.draw, &drawcol,
4053 borderpx + curx * xw.cw,
4054 borderpx + (term.c.y + 1) * xw.ch - 1,
4055 xw.cw, 1);
4056 } 2546 }
4057 oldx = curx, oldy = term.c.y;
4058} 2547}
4059 2548
4060
4061void 2549void
4062xsettitle(char *p) 2550resettitle(void)
4063{
4064 XTextProperty prop;
4065
4066 Xutf8TextListToTextProperty(xw.dpy, &p, 1, XUTF8StringStyle,
4067 &prop);
4068 XSetWMName(xw.dpy, xw.win, &prop);
4069 XSetTextProperty(xw.dpy, xw.win, &prop, xw.netwmname);
4070 XFree(prop.value);
4071}
4072
4073void
4074xresettitle(void)
4075{ 2551{
4076 xsettitle(opt_title ? opt_title : "st"); 2552 xsettitle(opt_title ? opt_title : "st");
4077} 2553}
@@ -4083,121 +2559,6 @@ redraw(void)
4083 draw(); 2559 draw();
4084} 2560}
4085 2561
4086void
4087draw(void)
4088{
4089 drawregion(0, 0, term.col, term.row);
4090 XCopyArea(xw.dpy, xw.buf, xw.win, dc.gc, 0, 0, xw.w,
4091 xw.h, 0, 0);
4092 XSetForeground(xw.dpy, dc.gc,
4093 dc.col[IS_SET(MODE_REVERSE)?
4094 defaultfg : defaultbg].pixel);
4095}
4096
4097void
4098drawregion(int x1, int y1, int x2, int y2)
4099{
4100 int i, x, y, ox, numspecs;
4101 Glyph base, new;
4102 XftGlyphFontSpec *specs;
4103 int ena_sel = sel.ob.x != -1 && sel.alt == IS_SET(MODE_ALTSCREEN);
4104
4105 if (!(xw.state & WIN_VISIBLE))
4106 return;
4107
4108 for (y = y1; y < y2; y++) {
4109 if (!term.dirty[y])
4110 continue;
4111
4112 term.dirty[y] = 0;
4113
4114 specs = term.specbuf;
4115 numspecs = xmakeglyphfontspecs(specs, &term.line[y][x1], x2 - x1, x1, y);
4116
4117 i = ox = 0;
4118 for (x = x1; x < x2 && i < numspecs; x++) {
4119 new = term.line[y][x];
4120 if (new.mode == ATTR_WDUMMY)
4121 continue;
4122 if (ena_sel && selected(x, y))
4123 new.mode ^= ATTR_REVERSE;
4124 if (i > 0 && ATTRCMP(base, new)) {
4125 xdrawglyphfontspecs(specs, base, i, ox, y);
4126 specs += i;
4127 numspecs -= i;
4128 i = 0;
4129 }
4130 if (i == 0) {
4131 ox = x;
4132 base = new;
4133 }
4134 i++;
4135 }
4136 if (i > 0)
4137 xdrawglyphfontspecs(specs, base, i, ox, y);
4138 }
4139 xdrawcursor();
4140}
4141
4142void
4143expose(XEvent *ev)
4144{
4145 redraw();
4146}
4147
4148void
4149visibility(XEvent *ev)
4150{
4151 XVisibilityEvent *e = &ev->xvisibility;
4152
4153 MODBIT(xw.state, e->state != VisibilityFullyObscured, WIN_VISIBLE);
4154}
4155
4156void
4157unmap(XEvent *ev)
4158{
4159 xw.state &= ~WIN_VISIBLE;
4160}
4161
4162void
4163xsetpointermotion(int set)
4164{
4165 MODBIT(xw.attrs.event_mask, set, PointerMotionMask);
4166 XChangeWindowAttributes(xw.dpy, xw.win, CWEventMask, &xw.attrs);
4167}
4168
4169void
4170xseturgency(int add)
4171{
4172 XWMHints *h = XGetWMHints(xw.dpy, xw.win);
4173
4174 MODBIT(h->flags, add, XUrgencyHint);
4175 XSetWMHints(xw.dpy, xw.win, h);
4176 XFree(h);
4177}
4178
4179void
4180focus(XEvent *ev)
4181{
4182 XFocusChangeEvent *e = &ev->xfocus;
4183
4184 if (e->mode == NotifyGrab)
4185 return;
4186
4187 if (ev->type == FocusIn) {
4188 XSetICFocus(xw.xic);
4189 xw.state |= WIN_FOCUSED;
4190 xseturgency(0);
4191 if (IS_SET(MODE_FOCUS))
4192 ttywrite("\033[I", 3);
4193 } else {
4194 XUnsetICFocus(xw.xic);
4195 xw.state &= ~WIN_FOCUSED;
4196 if (IS_SET(MODE_FOCUS))
4197 ttywrite("\033[O", 3);
4198 }
4199}
4200
4201int 2562int
4202match(uint mask, uint state) 2563match(uint mask, uint state)
4203{ 2564{
@@ -4251,211 +2612,23 @@ kmap(KeySym k, uint state)
4251} 2612}
4252 2613
4253void 2614void
4254kpress(XEvent *ev)
4255{
4256 XKeyEvent *e = &ev->xkey;
4257 KeySym ksym;
4258 char buf[32], *customkey;
4259 int len;
4260 Rune c;
4261 Status status;
4262 Shortcut *bp;
4263
4264 if (IS_SET(MODE_KBDLOCK))
4265 return;
4266
4267 len = XmbLookupString(xw.xic, e, buf, sizeof buf, &ksym, &status);
4268 /* 1. shortcuts */
4269 for (bp = shortcuts; bp < shortcuts + LEN(shortcuts); bp++) {
4270 if (ksym == bp->keysym && match(bp->mod, e->state)) {
4271 bp->func(&(bp->arg));
4272 return;
4273 }
4274 }
4275
4276 /* 2. custom keys from config.h */
4277 if ((customkey = kmap(ksym, e->state))) {
4278 ttysend(customkey, strlen(customkey));
4279 return;
4280 }
4281
4282 /* 3. composed string from input method */
4283 if (len == 0)
4284 return;
4285 if (len == 1 && e->state & Mod1Mask) {
4286 if (IS_SET(MODE_8BIT)) {
4287 if (*buf < 0177) {
4288 c = *buf | 0x80;
4289 len = utf8encode(c, buf);
4290 }
4291 } else {
4292 buf[1] = buf[0];
4293 buf[0] = '\033';
4294 len = 2;
4295 }
4296 }
4297 ttysend(buf, len);
4298}
4299
4300
4301void
4302cmessage(XEvent *e)
4303{
4304 /*
4305 * See xembed specs
4306 * http://standards.freedesktop.org/xembed-spec/xembed-spec-latest.html
4307 */
4308 if (e->xclient.message_type == xw.xembed && e->xclient.format == 32) {
4309 if (e->xclient.data.l[1] == XEMBED_FOCUS_IN) {
4310 xw.state |= WIN_FOCUSED;
4311 xseturgency(0);
4312 } else if (e->xclient.data.l[1] == XEMBED_FOCUS_OUT) {
4313 xw.state &= ~WIN_FOCUSED;
4314 }
4315 } else if (e->xclient.data.l[0] == xw.wmdeletewin) {
4316 /* Send SIGHUP to shell */
4317 kill(pid, SIGHUP);
4318 exit(0);
4319 }
4320}
4321
4322void
4323cresize(int width, int height) 2615cresize(int width, int height)
4324{ 2616{
4325 int col, row; 2617 int col, row;
4326 2618
4327 if (width != 0) 2619 if (width != 0)
4328 xw.w = width; 2620 win.w = width;
4329 if (height != 0) 2621 if (height != 0)
4330 xw.h = height; 2622 win.h = height;
4331 2623
4332 col = (xw.w - 2 * borderpx) / xw.cw; 2624 col = (win.w - 2 * borderpx) / win.cw;
4333 row = (xw.h - 2 * borderpx) / xw.ch; 2625 row = (win.h - 2 * borderpx) / win.ch;
4334 2626
4335 tresize(col, row); 2627 tresize(col, row);
4336 xresize(col, row); 2628 xresize(col, row);
4337} 2629}
4338 2630
4339void 2631void
4340resize(XEvent *e)
4341{
4342 if (e->xconfigure.width == xw.w && e->xconfigure.height == xw.h)
4343 return;
4344
4345 cresize(e->xconfigure.width, e->xconfigure.height);
4346 ttyresize();
4347}
4348
4349void
4350run(void)
4351{
4352 XEvent ev;
4353 int w = xw.w, h = xw.h;
4354 fd_set rfd;
4355 int xfd = XConnectionNumber(xw.dpy), xev, blinkset = 0, dodraw = 0;
4356 struct timespec drawtimeout, *tv = NULL, now, last, lastblink;
4357 long deltatime;
4358
4359 /* Waiting for window mapping */
4360 do {
4361 XNextEvent(xw.dpy, &ev);
4362 /*
4363 * This XFilterEvent call is required because of XOpenIM. It
4364 * does filter out the key event and some client message for
4365 * the input method too.
4366 */
4367 if (XFilterEvent(&ev, None))
4368 continue;
4369 if (ev.type == ConfigureNotify) {
4370 w = ev.xconfigure.width;
4371 h = ev.xconfigure.height;
4372 }
4373 } while (ev.type != MapNotify);
4374
4375 cresize(w, h);
4376 ttynew();
4377 ttyresize();
4378
4379 clock_gettime(CLOCK_MONOTONIC, &last);
4380 lastblink = last;
4381
4382 for (xev = actionfps;;) {
4383 FD_ZERO(&rfd);
4384 FD_SET(cmdfd, &rfd);
4385 FD_SET(xfd, &rfd);
4386
4387 if (pselect(MAX(xfd, cmdfd)+1, &rfd, NULL, NULL, tv, NULL) < 0) {
4388 if (errno == EINTR)
4389 continue;
4390 die("select failed: %s\n", strerror(errno));
4391 }
4392 if (FD_ISSET(cmdfd, &rfd)) {
4393 ttyread();
4394 if (blinktimeout) {
4395 blinkset = tattrset(ATTR_BLINK);
4396 if (!blinkset)
4397 MODBIT(term.mode, 0, MODE_BLINK);
4398 }
4399 }
4400
4401 if (FD_ISSET(xfd, &rfd))
4402 xev = actionfps;
4403
4404 clock_gettime(CLOCK_MONOTONIC, &now);
4405 drawtimeout.tv_sec = 0;
4406 drawtimeout.tv_nsec = (1000 * 1E6)/ xfps;
4407 tv = &drawtimeout;
4408
4409 dodraw = 0;
4410 if (blinktimeout && TIMEDIFF(now, lastblink) > blinktimeout) {
4411 tsetdirtattr(ATTR_BLINK);
4412 term.mode ^= MODE_BLINK;
4413 lastblink = now;
4414 dodraw = 1;
4415 }
4416 deltatime = TIMEDIFF(now, last);
4417 if (deltatime > 1000 / (xev ? xfps : actionfps)) {
4418 dodraw = 1;
4419 last = now;
4420 }
4421
4422 if (dodraw) {
4423 while (XPending(xw.dpy)) {
4424 XNextEvent(xw.dpy, &ev);
4425 if (XFilterEvent(&ev, None))
4426 continue;
4427 if (handler[ev.type])
4428 (handler[ev.type])(&ev);
4429 }
4430
4431 draw();
4432 XFlush(xw.dpy);
4433
4434 if (xev && !FD_ISSET(xfd, &rfd))
4435 xev--;
4436 if (!FD_ISSET(cmdfd, &rfd) && !FD_ISSET(xfd, &rfd)) {
4437 if (blinkset) {
4438 if (TIMEDIFF(now, lastblink) \
4439 > blinktimeout) {
4440 drawtimeout.tv_nsec = 1000;
4441 } else {
4442 drawtimeout.tv_nsec = (1E6 * \
4443 (blinktimeout - \
4444 TIMEDIFF(now,
4445 lastblink)));
4446 }
4447 drawtimeout.tv_sec = \
4448 drawtimeout.tv_nsec / 1E9;
4449 drawtimeout.tv_nsec %= (long)1E9;
4450 } else {
4451 tv = NULL;
4452 }
4453 }
4454 }
4455 }
4456}
4457
4458void
4459usage(void) 2632usage(void)
4460{ 2633{
4461 die("usage: %s [-aiv] [-c class] [-f font] [-g geometry]" 2634 die("usage: %s [-aiv] [-c class] [-f font] [-g geometry]"
@@ -4467,72 +2640,3 @@ usage(void)
4467 " [-T title] [-t title] [-w windowid] -l line" 2640 " [-T title] [-t title] [-w windowid] -l line"
4468 " [stty_args ...]\n", argv0, argv0); 2641 " [stty_args ...]\n", argv0, argv0);
4469} 2642}
4470
4471int
4472main(int argc, char *argv[])
4473{
4474 xw.l = xw.t = 0;
4475 xw.isfixed = False;
4476 xw.cursor = cursorshape;
4477
4478 ARGBEGIN {
4479 case 'a':
4480 allowaltscreen = 0;
4481 break;
4482 case 'c':
4483 opt_class = EARGF(usage());
4484 break;
4485 case 'e':
4486 if (argc > 0)
4487 --argc, ++argv;
4488 goto run;
4489 case 'f':
4490 opt_font = EARGF(usage());
4491 break;
4492 case 'g':
4493 xw.gm = XParseGeometry(EARGF(usage()),
4494 &xw.l, &xw.t, &cols, &rows);
4495 break;
4496 case 'i':
4497 xw.isfixed = 1;
4498 break;
4499 case 'o':
4500 opt_io = EARGF(usage());
4501 break;
4502 case 'l':
4503 opt_line = EARGF(usage());
4504 break;
4505 case 'n':
4506 opt_name = EARGF(usage());
4507 break;
4508 case 't':
4509 case 'T':
4510 opt_title = EARGF(usage());
4511 break;
4512 case 'w':
4513 opt_embed = EARGF(usage());
4514 break;
4515 case 'v':
4516 die("%s " VERSION " (c) 2010-2016 st engineers\n", argv0);
4517 break;
4518 default:
4519 usage();
4520 } ARGEND;
4521
4522run:
4523 if (argc > 0) {
4524 /* eat all remaining arguments */
4525 opt_cmd = argv;
4526 if (!opt_title && !opt_line)
4527 opt_title = basename(xstrdup(argv[0]));
4528 }
4529 setlocale(LC_CTYPE, "");
4530 XSetLocaleModifiers("");
4531 tnew(MAX(cols, 1), MAX(rows, 1));
4532 xinit();
4533 selinit();
4534 run();
4535
4536 return 0;
4537}
4538
diff --git a/st.h b/st.h
new file mode 100644
index 0000000..44d4938
--- /dev/null
+++ b/st.h
@@ -0,0 +1,272 @@
1/* See LICENSE for license details. */
2
3/* Arbitrary sizes */
4#define UTF_SIZ 4
5
6/* macros */
7#define MIN(a, b) ((a) < (b) ? (a) : (b))
8#define MAX(a, b) ((a) < (b) ? (b) : (a))
9#define LEN(a) (sizeof(a) / sizeof(a)[0])
10#define BETWEEN(x, a, b) ((a) <= (x) && (x) <= (b))
11#define DIVCEIL(n, d) (((n) + ((d) - 1)) / (d))
12#define LIMIT(x, a, b) (x) = (x) < (a) ? (a) : (x) > (b) ? (b) : (x)
13#define ATTRCMP(a, b) ((a).mode != (b).mode || (a).fg != (b).fg || \
14 (a).bg != (b).bg)
15#define IS_SET(flag) ((term.mode & (flag)) != 0)
16#define TIMEDIFF(t1, t2) ((t1.tv_sec-t2.tv_sec)*1000 + \
17 (t1.tv_nsec-t2.tv_nsec)/1E6)
18#define MODBIT(x, set, bit) ((set) ? ((x) |= (bit)) : ((x) &= ~(bit)))
19
20#define TRUECOLOR(r,g,b) (1 << 24 | (r) << 16 | (g) << 8 | (b))
21#define IS_TRUECOL(x) (1 << 24 & (x))
22
23enum glyph_attribute {
24 ATTR_NULL = 0,
25 ATTR_BOLD = 1 << 0,
26 ATTR_FAINT = 1 << 1,
27 ATTR_ITALIC = 1 << 2,
28 ATTR_UNDERLINE = 1 << 3,
29 ATTR_BLINK = 1 << 4,
30 ATTR_REVERSE = 1 << 5,
31 ATTR_INVISIBLE = 1 << 6,
32 ATTR_STRUCK = 1 << 7,
33 ATTR_WRAP = 1 << 8,
34 ATTR_WIDE = 1 << 9,
35 ATTR_WDUMMY = 1 << 10,
36 ATTR_BOLD_FAINT = ATTR_BOLD | ATTR_FAINT,
37};
38
39enum term_mode {
40 MODE_WRAP = 1 << 0,
41 MODE_INSERT = 1 << 1,
42 MODE_APPKEYPAD = 1 << 2,
43 MODE_ALTSCREEN = 1 << 3,
44 MODE_CRLF = 1 << 4,
45 MODE_MOUSEBTN = 1 << 5,
46 MODE_MOUSEMOTION = 1 << 6,
47 MODE_REVERSE = 1 << 7,
48 MODE_KBDLOCK = 1 << 8,
49 MODE_HIDE = 1 << 9,
50 MODE_ECHO = 1 << 10,
51 MODE_APPCURSOR = 1 << 11,
52 MODE_MOUSESGR = 1 << 12,
53 MODE_8BIT = 1 << 13,
54 MODE_BLINK = 1 << 14,
55 MODE_FBLINK = 1 << 15,
56 MODE_FOCUS = 1 << 16,
57 MODE_MOUSEX10 = 1 << 17,
58 MODE_MOUSEMANY = 1 << 18,
59 MODE_BRCKTPASTE = 1 << 19,
60 MODE_PRINT = 1 << 20,
61 MODE_UTF8 = 1 << 21,
62 MODE_SIXEL = 1 << 22,
63 MODE_MOUSE = MODE_MOUSEBTN|MODE_MOUSEMOTION|MODE_MOUSEX10\
64 |MODE_MOUSEMANY,
65};
66
67enum selection_mode {
68 SEL_IDLE = 0,
69 SEL_EMPTY = 1,
70 SEL_READY = 2
71};
72
73enum selection_type {
74 SEL_REGULAR = 1,
75 SEL_RECTANGULAR = 2
76};
77
78enum selection_snap {
79 SNAP_WORD = 1,
80 SNAP_LINE = 2
81};
82
83enum window_state {
84 WIN_VISIBLE = 1,
85 WIN_FOCUSED = 2
86};
87
88typedef unsigned char uchar;
89typedef unsigned int uint;
90typedef unsigned long ulong;
91typedef unsigned short ushort;
92
93typedef uint_least32_t Rune;
94
95typedef struct {
96 Rune u; /* character code */
97 ushort mode; /* attribute flags */
98 uint32_t fg; /* foreground */
99 uint32_t bg; /* background */
100} Glyph;
101
102typedef Glyph *Line;
103
104typedef struct {
105 Glyph attr; /* current char attributes */
106 int x;
107 int y;
108 char state;
109} TCursor;
110
111/* Internal representation of the screen */
112typedef struct {
113 int row; /* nb row */
114 int col; /* nb col */
115 Line *line; /* screen */
116 Line *alt; /* alternate screen */
117 int *dirty; /* dirtyness of lines */
118 GlyphFontSpec *specbuf; /* font spec buffer used for rendering */
119 TCursor c; /* cursor */
120 int top; /* top scroll limit */
121 int bot; /* bottom scroll limit */
122 int mode; /* terminal mode flags */
123 int esc; /* escape state flags */
124 char trantbl[4]; /* charset table translation */
125 int charset; /* current charset */
126 int icharset; /* selected charset for sequence */
127 int numlock; /* lock numbers in keyboard */
128 int *tabs;
129} Term;
130
131/* Purely graphic info */
132typedef struct {
133 int tw, th; /* tty width and height */
134 int w, h; /* window width and height */
135 int ch; /* char height */
136 int cw; /* char width */
137 char state; /* focus, redraw, visible */
138 int cursor; /* cursor style */
139} TermWindow;
140
141typedef struct {
142 uint b;
143 uint mask;
144 char *s;
145} MouseShortcut;
146
147typedef struct {
148 int mode;
149 int type;
150 int snap;
151 /*
152 * Selection variables:
153 * nb – normalized coordinates of the beginning of the selection
154 * ne – normalized coordinates of the end of the selection
155 * ob – original coordinates of the beginning of the selection
156 * oe – original coordinates of the end of the selection
157 */
158 struct {
159 int x, y;
160 } nb, ne, ob, oe;
161
162 char *primary, *clipboard;
163 int alt;
164 struct timespec tclick1;
165 struct timespec tclick2;
166
167 //Atom xtarget;
168} Selection;
169
170typedef union {
171 int i;
172 uint ui;
173 float f;
174 const void *v;
175} Arg;
176
177typedef struct {
178 uint mod;
179 KeySym keysym;
180 void (*func)(const Arg *);
181 const Arg arg;
182} Shortcut;
183
184void die(const char *, ...);
185void redraw(void);
186
187int tattrset(int);
188void tnew(int, int);
189void tsetdirt(int, int);
190void tsetdirtattr(int);
191int match(uint, uint);
192void ttynew(void);
193size_t ttyread(void);
194void ttyresize(void);
195void ttysend(char *, size_t);
196void ttywrite(const char *, size_t);
197
198void resettitle(void);
199
200char *kmap(KeySym, uint);
201void cresize(int, int);
202void selclear(void);
203
204void selinit(void);
205void selnormalize(void);
206int selected(int, int);
207char *getsel(void);
208int x2col(int);
209int y2row(int);
210
211size_t utf8decode(char *, Rune *, size_t);
212size_t utf8encode(Rune, char *);
213
214void *xmalloc(size_t);
215char *xstrdup(char *);
216
217void usage(void);
218
219/* Globals */
220extern TermWindow win;
221extern Term term;
222extern Selection sel;
223extern int cmdfd;
224extern pid_t pid;
225extern char **opt_cmd;
226extern char *opt_class;
227extern char *opt_embed;
228extern char *opt_font;
229extern char *opt_io;
230extern char *opt_line;
231extern char *opt_name;
232extern char *opt_title;
233extern int oldbutton;
234
235extern char *usedfont;
236extern double usedfontsize;
237extern double defaultfontsize;
238
239/* config.h globals */
240extern char font[];
241extern int borderpx;
242extern float cwscale;
243extern float chscale;
244extern unsigned int doubleclicktimeout;
245extern unsigned int tripleclicktimeout;
246extern int allowaltscreen;
247extern unsigned int xfps;
248extern unsigned int actionfps;
249extern unsigned int cursorthickness;
250extern unsigned int blinktimeout;
251extern char termname[];
252extern const char *colorname[];
253extern size_t colornamelen;
254extern unsigned int defaultfg;
255extern unsigned int defaultbg;
256extern unsigned int defaultcs;
257extern unsigned int defaultrcs;
258extern unsigned int cursorshape;
259extern unsigned int cols;
260extern unsigned int rows;
261extern unsigned int mouseshape;
262extern unsigned int mousefg;
263extern unsigned int mousebg;
264extern unsigned int defaultattr;
265extern MouseShortcut mshortcuts[];
266extern size_t mshortcutslen;
267extern Shortcut shortcuts[];
268extern size_t shortcutslen;
269extern uint forceselmod;
270extern uint selmasks[];
271extern size_t selmaskslen;
272extern char ascii_printable[];
diff --git a/win.h b/win.h
new file mode 100644
index 0000000..d684797
--- /dev/null
+++ b/win.h
@@ -0,0 +1,29 @@
1/* See LICENSE for license details. */
2
3/* X modifiers */
4#define XK_ANY_MOD UINT_MAX
5#define XK_NO_MOD 0
6#define XK_SWITCH_MOD (1<<13)
7
8typedef XftGlyphFontSpec GlyphFontSpec;
9
10void draw(void);
11void drawregion(int, int, int, int);
12void run(void);
13
14void xbell(int);
15void xclipcopy(void);
16void xclippaste(void);
17void xhints(void);
18void xinit(void);
19void xloadcols(void);
20int xsetcolorname(int, const char *);
21void xloadfonts(char *, double);
22void xsetenv(void);
23void xsettitle(char *);
24void xsetpointermotion(int);
25void xseturgency(int);
26void xunloadfonts(void);
27void xresize(int, int);
28void xselpaste(void);
29unsigned long xwinid(void);
diff --git a/x.c b/x.c
new file mode 100644
index 0000000..6474a01
--- /dev/null
+++ b/x.c
@@ -0,0 +1,1766 @@
1/* See LICENSE for license details. */
2#include <errno.h>
3#include <locale.h>
4#include <signal.h>
5#include <stdint.h>
6#include <sys/select.h>
7#include <time.h>
8#include <unistd.h>
9#include <libgen.h>
10#include <X11/Xatom.h>
11#include <X11/Xlib.h>
12#include <X11/Xutil.h>
13#include <X11/cursorfont.h>
14#include <X11/keysym.h>
15#include <X11/Xft/Xft.h>
16#include <X11/XKBlib.h>
17
18#include "arg.h"
19
20#define Glyph Glyph_
21#define Font Font_
22
23#include "win.h"
24#include "st.h"
25
26/* XEMBED messages */
27#define XEMBED_FOCUS_IN 4
28#define XEMBED_FOCUS_OUT 5
29
30/* macros */
31#define TRUERED(x) (((x) & 0xff0000) >> 8)
32#define TRUEGREEN(x) (((x) & 0xff00))
33#define TRUEBLUE(x) (((x) & 0xff) << 8)
34
35typedef XftDraw *Draw;
36typedef XftColor Color;
37
38/* Purely graphic info */
39typedef struct {
40 Display *dpy;
41 Colormap cmap;
42 Window win;
43 Drawable buf;
44 Atom xembed, wmdeletewin, netwmname, netwmpid;
45 XIM xim;
46 XIC xic;
47 Draw draw;
48 Visual *vis;
49 XSetWindowAttributes attrs;
50 int scr;
51 int isfixed; /* is fixed geometry? */
52 int l, t; /* left and top offset */
53 int gm; /* geometry mask */
54} XWindow;
55
56typedef struct {
57 Atom xtarget;
58} XSelection;
59
60/* Font structure */
61typedef struct {
62 int height;
63 int width;
64 int ascent;
65 int descent;
66 int badslant;
67 int badweight;
68 short lbearing;
69 short rbearing;
70 XftFont *match;
71 FcFontSet *set;
72 FcPattern *pattern;
73} Font;
74
75/* Drawing Context */
76typedef struct {
77 Color *col;
78 size_t collen;
79 Font font, bfont, ifont, ibfont;
80 GC gc;
81} DC;
82
83static inline ushort sixd_to_16bit(int);
84static int xmakeglyphfontspecs(XftGlyphFontSpec *, const Glyph *, int, int, int);
85static void xdrawglyphfontspecs(const XftGlyphFontSpec *, Glyph, int, int, int);
86static void xdrawglyph(Glyph, int, int);
87static void xclear(int, int, int, int);
88static void xdrawcursor(void);
89static int xgeommasktogravity(int);
90static int xloadfont(Font *, FcPattern *);
91static void xsetsel(char *, Time);
92static void xunloadfont(Font *);
93
94static void expose(XEvent *);
95static void visibility(XEvent *);
96static void unmap(XEvent *);
97static void kpress(XEvent *);
98static void cmessage(XEvent *);
99static void resize(XEvent *);
100static void focus(XEvent *);
101static void brelease(XEvent *);
102static void bpress(XEvent *);
103static void bmotion(XEvent *);
104static void propnotify(XEvent *);
105static void selnotify(XEvent *);
106static void selclear_(XEvent *);
107static void selrequest(XEvent *);
108
109static void selcopy(Time);
110static void getbuttoninfo(XEvent *);
111static void mousereport(XEvent *);
112
113static void (*handler[LASTEvent])(XEvent *) = {
114 [KeyPress] = kpress,
115 [ClientMessage] = cmessage,
116 [ConfigureNotify] = resize,
117 [VisibilityNotify] = visibility,
118 [UnmapNotify] = unmap,
119 [Expose] = expose,
120 [FocusIn] = focus,
121 [FocusOut] = focus,
122 [MotionNotify] = bmotion,
123 [ButtonPress] = bpress,
124 [ButtonRelease] = brelease,
125/*
126 * Uncomment if you want the selection to disappear when you select something
127 * different in another window.
128 */
129/* [SelectionClear] = selclear_, */
130 [SelectionNotify] = selnotify,
131/*
132 * PropertyNotify is only turned on when there is some INCR transfer happening
133 * for the selection retrieval.
134 */
135 [PropertyNotify] = propnotify,
136 [SelectionRequest] = selrequest,
137};
138
139/* Globals */
140static DC dc;
141static XWindow xw;
142static XSelection xsel;
143
144/* Font Ring Cache */
145enum {
146 FRC_NORMAL,
147 FRC_ITALIC,
148 FRC_BOLD,
149 FRC_ITALICBOLD
150};
151
152typedef struct {
153 XftFont *font;
154 int flags;
155 Rune unicodep;
156} Fontcache;
157
158/* Fontcache is an array now. A new font will be appended to the array. */
159static Fontcache frc[16];
160static int frclen = 0;
161
162void
163getbuttoninfo(XEvent *e)
164{
165 int type;
166 uint state = e->xbutton.state & ~(Button1Mask | forceselmod);
167
168 sel.alt = IS_SET(MODE_ALTSCREEN);
169
170 sel.oe.x = x2col(e->xbutton.x);
171 sel.oe.y = y2row(e->xbutton.y);
172 selnormalize();
173
174 sel.type = SEL_REGULAR;
175 for (type = 1; type < selmaskslen; ++type) {
176 if (match(selmasks[type], state)) {
177 sel.type = type;
178 break;
179 }
180 }
181}
182
183void
184mousereport(XEvent *e)
185{
186 int x = x2col(e->xbutton.x), y = y2row(e->xbutton.y),
187 button = e->xbutton.button, state = e->xbutton.state,
188 len;
189 char buf[40];
190 static int ox, oy;
191
192 /* from urxvt */
193 if (e->xbutton.type == MotionNotify) {
194 if (x == ox && y == oy)
195 return;
196 if (!IS_SET(MODE_MOUSEMOTION) && !IS_SET(MODE_MOUSEMANY))
197 return;
198 /* MOUSE_MOTION: no reporting if no button is pressed */
199 if (IS_SET(MODE_MOUSEMOTION) && oldbutton == 3)
200 return;
201
202 button = oldbutton + 32;
203 ox = x;
204 oy = y;
205 } else {
206 if (!IS_SET(MODE_MOUSESGR) && e->xbutton.type == ButtonRelease) {
207 button = 3;
208 } else {
209 button -= Button1;
210 if (button >= 3)
211 button += 64 - 3;
212 }
213 if (e->xbutton.type == ButtonPress) {
214 oldbutton = button;
215 ox = x;
216 oy = y;
217 } else if (e->xbutton.type == ButtonRelease) {
218 oldbutton = 3;
219 /* MODE_MOUSEX10: no button release reporting */
220 if (IS_SET(MODE_MOUSEX10))
221 return;
222 if (button == 64 || button == 65)
223 return;
224 }
225 }
226
227 if (!IS_SET(MODE_MOUSEX10)) {
228 button += ((state & ShiftMask ) ? 4 : 0)
229 + ((state & Mod4Mask ) ? 8 : 0)
230 + ((state & ControlMask) ? 16 : 0);
231 }
232
233 if (IS_SET(MODE_MOUSESGR)) {
234 len = snprintf(buf, sizeof(buf), "\033[<%d;%d;%d%c",
235 button, x+1, y+1,
236 e->xbutton.type == ButtonRelease ? 'm' : 'M');
237 } else if (x < 223 && y < 223) {
238 len = snprintf(buf, sizeof(buf), "\033[M%c%c%c",
239 32+button, 32+x+1, 32+y+1);
240 } else {
241 return;
242 }
243
244 ttywrite(buf, len);
245}
246
247void
248bpress(XEvent *e)
249{
250 struct timespec now;
251 MouseShortcut *ms;
252
253 if (IS_SET(MODE_MOUSE) && !(e->xbutton.state & forceselmod)) {
254 mousereport(e);
255 return;
256 }
257
258 for (ms = mshortcuts; ms < mshortcuts + mshortcutslen; ms++) {
259 if (e->xbutton.button == ms->b
260 && match(ms->mask, e->xbutton.state)) {
261 ttysend(ms->s, strlen(ms->s));
262 return;
263 }
264 }
265
266 if (e->xbutton.button == Button1) {
267 clock_gettime(CLOCK_MONOTONIC, &now);
268
269 /* Clear previous selection, logically and visually. */
270 selclear_(NULL);
271 sel.mode = SEL_EMPTY;
272 sel.type = SEL_REGULAR;
273 sel.oe.x = sel.ob.x = x2col(e->xbutton.x);
274 sel.oe.y = sel.ob.y = y2row(e->xbutton.y);
275
276 /*
277 * If the user clicks below predefined timeouts specific
278 * snapping behaviour is exposed.
279 */
280 if (TIMEDIFF(now, sel.tclick2) <= tripleclicktimeout) {
281 sel.snap = SNAP_LINE;
282 } else if (TIMEDIFF(now, sel.tclick1) <= doubleclicktimeout) {
283 sel.snap = SNAP_WORD;
284 } else {
285 sel.snap = 0;
286 }
287 selnormalize();
288
289 if (sel.snap != 0)
290 sel.mode = SEL_READY;
291 tsetdirt(sel.nb.y, sel.ne.y);
292 sel.tclick2 = sel.tclick1;
293 sel.tclick1 = now;
294 }
295}
296
297void
298selcopy(Time t)
299{
300 xsetsel(getsel(), t);
301}
302
303void
304propnotify(XEvent *e)
305{
306 XPropertyEvent *xpev;
307 Atom clipboard = XInternAtom(xw.dpy, "CLIPBOARD", 0);
308
309 xpev = &e->xproperty;
310 if (xpev->state == PropertyNewValue &&
311 (xpev->atom == XA_PRIMARY ||
312 xpev->atom == clipboard)) {
313 selnotify(e);
314 }
315}
316
317void
318selnotify(XEvent *e)
319{
320 ulong nitems, ofs, rem;
321 int format;
322 uchar *data, *last, *repl;
323 Atom type, incratom, property;
324
325 incratom = XInternAtom(xw.dpy, "INCR", 0);
326
327 ofs = 0;
328 if (e->type == SelectionNotify) {
329 property = e->xselection.property;
330 } else if(e->type == PropertyNotify) {
331 property = e->xproperty.atom;
332 } else {
333 return;
334 }
335 if (property == None)
336 return;
337
338 do {
339 if (XGetWindowProperty(xw.dpy, xw.win, property, ofs,
340 BUFSIZ/4, False, AnyPropertyType,
341 &type, &format, &nitems, &rem,
342 &data)) {
343 fprintf(stderr, "Clipboard allocation failed\n");
344 return;
345 }
346
347 if (e->type == PropertyNotify && nitems == 0 && rem == 0) {
348 /*
349 * If there is some PropertyNotify with no data, then
350 * this is the signal of the selection owner that all
351 * data has been transferred. We won't need to receive
352 * PropertyNotify events anymore.
353 */
354 MODBIT(xw.attrs.event_mask, 0, PropertyChangeMask);
355 XChangeWindowAttributes(xw.dpy, xw.win, CWEventMask,
356 &xw.attrs);
357 }
358
359 if (type == incratom) {
360 /*
361 * Activate the PropertyNotify events so we receive
362 * when the selection owner does send us the next
363 * chunk of data.
364 */
365 MODBIT(xw.attrs.event_mask, 1, PropertyChangeMask);
366 XChangeWindowAttributes(xw.dpy, xw.win, CWEventMask,
367 &xw.attrs);
368
369 /*
370 * Deleting the property is the transfer start signal.
371 */
372 XDeleteProperty(xw.dpy, xw.win, (int)property);
373 continue;
374 }
375
376 /*
377 * As seen in getsel:
378 * Line endings are inconsistent in the terminal and GUI world
379 * copy and pasting. When receiving some selection data,
380 * replace all '\n' with '\r'.
381 * FIXME: Fix the computer world.
382 */
383 repl = data;
384 last = data + nitems * format / 8;
385 while ((repl = memchr(repl, '\n', last - repl))) {
386 *repl++ = '\r';
387 }
388
389 if (IS_SET(MODE_BRCKTPASTE) && ofs == 0)
390 ttywrite("\033[200~", 6);
391 ttysend((char *)data, nitems * format / 8);
392 if (IS_SET(MODE_BRCKTPASTE) && rem == 0)
393 ttywrite("\033[201~", 6);
394 XFree(data);
395 /* number of 32-bit chunks returned */
396 ofs += nitems * format / 32;
397 } while (rem > 0);
398
399 /*
400 * Deleting the property again tells the selection owner to send the
401 * next data chunk in the property.
402 */
403 XDeleteProperty(xw.dpy, xw.win, (int)property);
404}
405
406void
407xselpaste(void)
408{
409 XConvertSelection(xw.dpy, XA_PRIMARY, xsel.xtarget, XA_PRIMARY,
410 xw.win, CurrentTime);
411}
412
413void
414xclipcopy(void)
415{
416 Atom clipboard;
417
418 if (sel.clipboard != NULL)
419 free(sel.clipboard);
420
421 if (sel.primary != NULL) {
422 sel.clipboard = xstrdup(sel.primary);
423 clipboard = XInternAtom(xw.dpy, "CLIPBOARD", 0);
424 XSetSelectionOwner(xw.dpy, clipboard, xw.win, CurrentTime);
425 }
426}
427
428void
429xclippaste(void)
430{
431 Atom clipboard;
432
433 clipboard = XInternAtom(xw.dpy, "CLIPBOARD", 0);
434 XConvertSelection(xw.dpy, clipboard, xsel.xtarget, clipboard,
435 xw.win, CurrentTime);
436}
437
438void
439selclear_(XEvent *e)
440{
441 selclear();
442}
443
444void
445selrequest(XEvent *e)
446{
447 XSelectionRequestEvent *xsre;
448 XSelectionEvent xev;
449 Atom xa_targets, string, clipboard;
450 char *seltext;
451
452 xsre = (XSelectionRequestEvent *) e;
453 xev.type = SelectionNotify;
454 xev.requestor = xsre->requestor;
455 xev.selection = xsre->selection;
456 xev.target = xsre->target;
457 xev.time = xsre->time;
458 if (xsre->property == None)
459 xsre->property = xsre->target;
460
461 /* reject */
462 xev.property = None;
463
464 xa_targets = XInternAtom(xw.dpy, "TARGETS", 0);
465 if (xsre->target == xa_targets) {
466 /* respond with the supported type */
467 string = xsel.xtarget;
468 XChangeProperty(xsre->display, xsre->requestor, xsre->property,
469 XA_ATOM, 32, PropModeReplace,
470 (uchar *) &string, 1);
471 xev.property = xsre->property;
472 } else if (xsre->target == xsel.xtarget || xsre->target == XA_STRING) {
473 /*
474 * xith XA_STRING non ascii characters may be incorrect in the
475 * requestor. It is not our problem, use utf8.
476 */
477 clipboard = XInternAtom(xw.dpy, "CLIPBOARD", 0);
478 if (xsre->selection == XA_PRIMARY) {
479 seltext = sel.primary;
480 } else if (xsre->selection == clipboard) {
481 seltext = sel.clipboard;
482 } else {
483 fprintf(stderr,
484 "Unhandled clipboard selection 0x%lx\n",
485 xsre->selection);
486 return;
487 }
488 if (seltext != NULL) {
489 XChangeProperty(xsre->display, xsre->requestor,
490 xsre->property, xsre->target,
491 8, PropModeReplace,
492 (uchar *)seltext, strlen(seltext));
493 xev.property = xsre->property;
494 }
495 }
496
497 /* all done, send a notification to the listener */
498 if (!XSendEvent(xsre->display, xsre->requestor, 1, 0, (XEvent *) &xev))
499 fprintf(stderr, "Error sending SelectionNotify event\n");
500}
501
502void
503xsetsel(char *str, Time t)
504{
505 free(sel.primary);
506 sel.primary = str;
507
508 XSetSelectionOwner(xw.dpy, XA_PRIMARY, xw.win, t);
509 if (XGetSelectionOwner(xw.dpy, XA_PRIMARY) != xw.win)
510 selclear_(NULL);
511}
512
513void
514brelease(XEvent *e)
515{
516 if (IS_SET(MODE_MOUSE) && !(e->xbutton.state & forceselmod)) {
517 mousereport(e);
518 return;
519 }
520
521 if (e->xbutton.button == Button2) {
522 xselpaste();
523 } else if (e->xbutton.button == Button1) {
524 if (sel.mode == SEL_READY) {
525 getbuttoninfo(e);
526 selcopy(e->xbutton.time);
527 } else
528 selclear_(NULL);
529 sel.mode = SEL_IDLE;
530 tsetdirt(sel.nb.y, sel.ne.y);
531 }
532}
533
534void
535bmotion(XEvent *e)
536{
537 int oldey, oldex, oldsby, oldsey;
538
539 if (IS_SET(MODE_MOUSE) && !(e->xbutton.state & forceselmod)) {
540 mousereport(e);
541 return;
542 }
543
544 if (!sel.mode)
545 return;
546
547 sel.mode = SEL_READY;
548 oldey = sel.oe.y;
549 oldex = sel.oe.x;
550 oldsby = sel.nb.y;
551 oldsey = sel.ne.y;
552 getbuttoninfo(e);
553
554 if (oldey != sel.oe.y || oldex != sel.oe.x)
555 tsetdirt(MIN(sel.nb.y, oldsby), MAX(sel.ne.y, oldsey));
556}
557
558void
559xresize(int col, int row)
560{
561 win.tw = MAX(1, col * win.cw);
562 win.th = MAX(1, row * win.ch);
563
564 XFreePixmap(xw.dpy, xw.buf);
565 xw.buf = XCreatePixmap(xw.dpy, xw.win, win.w, win.h,
566 DefaultDepth(xw.dpy, xw.scr));
567 XftDrawChange(xw.draw, xw.buf);
568 xclear(0, 0, win.w, win.h);
569}
570
571ushort
572sixd_to_16bit(int x)
573{
574 return x == 0 ? 0 : 0x3737 + 0x2828 * x;
575}
576
577int
578xloadcolor(int i, const char *name, Color *ncolor)
579{
580 XRenderColor color = { .alpha = 0xffff };
581
582 if (!name) {
583 if (BETWEEN(i, 16, 255)) { /* 256 color */
584 if (i < 6*6*6+16) { /* same colors as xterm */
585 color.red = sixd_to_16bit( ((i-16)/36)%6 );
586 color.green = sixd_to_16bit( ((i-16)/6) %6 );
587 color.blue = sixd_to_16bit( ((i-16)/1) %6 );
588 } else { /* greyscale */
589 color.red = 0x0808 + 0x0a0a * (i - (6*6*6+16));
590 color.green = color.blue = color.red;
591 }
592 return XftColorAllocValue(xw.dpy, xw.vis,
593 xw.cmap, &color, ncolor);
594 } else
595 name = colorname[i];
596 }
597
598 return XftColorAllocName(xw.dpy, xw.vis, xw.cmap, name, ncolor);
599}
600
601void
602xloadcols(void)
603{
604 int i;
605 static int loaded;
606 Color *cp;
607
608 dc.collen = MAX(colornamelen, 256);
609 dc.col = xmalloc(dc.collen * sizeof(Color));
610
611 if (loaded) {
612 for (cp = dc.col; cp < &dc.col[dc.collen]; ++cp)
613 XftColorFree(xw.dpy, xw.vis, xw.cmap, cp);
614 }
615
616 for (i = 0; i < dc.collen; i++)
617 if (!xloadcolor(i, NULL, &dc.col[i])) {
618 if (colorname[i])
619 die("Could not allocate color '%s'\n", colorname[i]);
620 else
621 die("Could not allocate color %d\n", i);
622 }
623 loaded = 1;
624}
625
626int
627xsetcolorname(int x, const char *name)
628{
629 Color ncolor;
630
631 if (!BETWEEN(x, 0, dc.collen))
632 return 1;
633
634
635 if (!xloadcolor(x, name, &ncolor))
636 return 1;
637
638 XftColorFree(xw.dpy, xw.vis, xw.cmap, &dc.col[x]);
639 dc.col[x] = ncolor;
640
641 return 0;
642}
643
644/*
645 * Absolute coordinates.
646 */
647void
648xclear(int x1, int y1, int x2, int y2)
649{
650 XftDrawRect(xw.draw,
651 &dc.col[IS_SET(MODE_REVERSE)? defaultfg : defaultbg],
652 x1, y1, x2-x1, y2-y1);
653}
654
655void
656xhints(void)
657{
658 XClassHint class = {opt_name ? opt_name : termname,
659 opt_class ? opt_class : termname};
660 XWMHints wm = {.flags = InputHint, .input = 1};
661 XSizeHints *sizeh = NULL;
662
663 sizeh = XAllocSizeHints();
664
665 sizeh->flags = PSize | PResizeInc | PBaseSize;
666 sizeh->height = win.h;
667 sizeh->width = win.w;
668 sizeh->height_inc = win.ch;
669 sizeh->width_inc = win.cw;
670 sizeh->base_height = 2 * borderpx;
671 sizeh->base_width = 2 * borderpx;
672 if (xw.isfixed) {
673 sizeh->flags |= PMaxSize | PMinSize;
674 sizeh->min_width = sizeh->max_width = win.w;
675 sizeh->min_height = sizeh->max_height = win.h;
676 }
677 if (xw.gm & (XValue|YValue)) {
678 sizeh->flags |= USPosition | PWinGravity;
679 sizeh->x = xw.l;
680 sizeh->y = xw.t;
681 sizeh->win_gravity = xgeommasktogravity(xw.gm);
682 }
683
684 XSetWMProperties(xw.dpy, xw.win, NULL, NULL, NULL, 0, sizeh, &wm,
685 &class);
686 XFree(sizeh);
687}
688
689int
690xgeommasktogravity(int mask)
691{
692 switch (mask & (XNegative|YNegative)) {
693 case 0:
694 return NorthWestGravity;
695 case XNegative:
696 return NorthEastGravity;
697 case YNegative:
698 return SouthWestGravity;
699 }
700
701 return SouthEastGravity;
702}
703
704int
705xloadfont(Font *f, FcPattern *pattern)
706{
707 FcPattern *configured;
708 FcPattern *match;
709 FcResult result;
710 XGlyphInfo extents;
711 int wantattr, haveattr;
712
713 /*
714 * Manually configure instead of calling XftMatchFont
715 * so that we can use the configured pattern for
716 * "missing glyph" lookups.
717 */
718 configured = FcPatternDuplicate(pattern);
719 if (!configured)
720 return 1;
721
722 FcConfigSubstitute(NULL, configured, FcMatchPattern);
723 XftDefaultSubstitute(xw.dpy, xw.scr, configured);
724
725 match = FcFontMatch(NULL, configured, &result);
726 if (!match) {
727 FcPatternDestroy(configured);
728 return 1;
729 }
730
731 if (!(f->match = XftFontOpenPattern(xw.dpy, match))) {
732 FcPatternDestroy(configured);
733 FcPatternDestroy(match);
734 return 1;
735 }
736
737 if ((XftPatternGetInteger(pattern, "slant", 0, &wantattr) ==
738 XftResultMatch)) {
739 /*
740 * Check if xft was unable to find a font with the appropriate
741 * slant but gave us one anyway. Try to mitigate.
742 */
743 if ((XftPatternGetInteger(f->match->pattern, "slant", 0,
744 &haveattr) != XftResultMatch) || haveattr < wantattr) {
745 f->badslant = 1;
746 fputs("st: font slant does not match\n", stderr);
747 }
748 }
749
750 if ((XftPatternGetInteger(pattern, "weight", 0, &wantattr) ==
751 XftResultMatch)) {
752 if ((XftPatternGetInteger(f->match->pattern, "weight", 0,
753 &haveattr) != XftResultMatch) || haveattr != wantattr) {
754 f->badweight = 1;
755 fputs("st: font weight does not match\n", stderr);
756 }
757 }
758
759 XftTextExtentsUtf8(xw.dpy, f->match,
760 (const FcChar8 *) ascii_printable,
761 strlen(ascii_printable), &extents);
762
763 f->set = NULL;
764 f->pattern = configured;
765
766 f->ascent = f->match->ascent;
767 f->descent = f->match->descent;
768 f->lbearing = 0;
769 f->rbearing = f->match->max_advance_width;
770
771 f->height = f->ascent + f->descent;
772 f->width = DIVCEIL(extents.xOff, strlen(ascii_printable));
773
774 return 0;
775}
776
777void
778xloadfonts(char *fontstr, double fontsize)
779{
780 FcPattern *pattern;
781 double fontval;
782 float ceilf(float);
783
784 if (fontstr[0] == '-') {
785 pattern = XftXlfdParse(fontstr, False, False);
786 } else {
787 pattern = FcNameParse((FcChar8 *)fontstr);
788 }
789
790 if (!pattern)
791 die("st: can't open font %s\n", fontstr);
792
793 if (fontsize > 1) {
794 FcPatternDel(pattern, FC_PIXEL_SIZE);
795 FcPatternDel(pattern, FC_SIZE);
796 FcPatternAddDouble(pattern, FC_PIXEL_SIZE, (double)fontsize);
797 usedfontsize = fontsize;
798 } else {
799 if (FcPatternGetDouble(pattern, FC_PIXEL_SIZE, 0, &fontval) ==
800 FcResultMatch) {
801 usedfontsize = fontval;
802 } else if (FcPatternGetDouble(pattern, FC_SIZE, 0, &fontval) ==
803 FcResultMatch) {
804 usedfontsize = -1;
805 } else {
806 /*
807 * Default font size is 12, if none given. This is to
808 * have a known usedfontsize value.
809 */
810 FcPatternAddDouble(pattern, FC_PIXEL_SIZE, 12);
811 usedfontsize = 12;
812 }
813 defaultfontsize = usedfontsize;
814 }
815
816 if (xloadfont(&dc.font, pattern))
817 die("st: can't open font %s\n", fontstr);
818
819 if (usedfontsize < 0) {
820 FcPatternGetDouble(dc.font.match->pattern,
821 FC_PIXEL_SIZE, 0, &fontval);
822 usedfontsize = fontval;
823 if (fontsize == 0)
824 defaultfontsize = fontval;
825 }
826
827 /* Setting character width and height. */
828 win.cw = ceilf(dc.font.width * cwscale);
829 win.ch = ceilf(dc.font.height * chscale);
830
831 FcPatternDel(pattern, FC_SLANT);
832 FcPatternAddInteger(pattern, FC_SLANT, FC_SLANT_ITALIC);
833 if (xloadfont(&dc.ifont, pattern))
834 die("st: can't open font %s\n", fontstr);
835
836 FcPatternDel(pattern, FC_WEIGHT);
837 FcPatternAddInteger(pattern, FC_WEIGHT, FC_WEIGHT_BOLD);
838 if (xloadfont(&dc.ibfont, pattern))
839 die("st: can't open font %s\n", fontstr);
840
841 FcPatternDel(pattern, FC_SLANT);
842 FcPatternAddInteger(pattern, FC_SLANT, FC_SLANT_ROMAN);
843 if (xloadfont(&dc.bfont, pattern))
844 die("st: can't open font %s\n", fontstr);
845
846 FcPatternDestroy(pattern);
847}
848
849void
850xunloadfont(Font *f)
851{
852 XftFontClose(xw.dpy, f->match);
853 FcPatternDestroy(f->pattern);
854 if (f->set)
855 FcFontSetDestroy(f->set);
856}
857
858void
859xunloadfonts(void)
860{
861 /* Free the loaded fonts in the font cache. */
862 while (frclen > 0)
863 XftFontClose(xw.dpy, frc[--frclen].font);
864
865 xunloadfont(&dc.font);
866 xunloadfont(&dc.bfont);
867 xunloadfont(&dc.ifont);
868 xunloadfont(&dc.ibfont);
869}
870
871void
872xinit(void)
873{
874 XGCValues gcvalues;
875 Cursor cursor;
876 Window parent;
877 pid_t thispid = getpid();
878 XColor xmousefg, xmousebg;
879
880 if (!(xw.dpy = XOpenDisplay(NULL)))
881 die("Can't open display\n");
882 xw.scr = XDefaultScreen(xw.dpy);
883 xw.vis = XDefaultVisual(xw.dpy, xw.scr);
884
885 /* font */
886 if (!FcInit())
887 die("Could not init fontconfig.\n");
888
889 usedfont = (opt_font == NULL)? font : opt_font;
890 xloadfonts(usedfont, 0);
891
892 /* colors */
893 xw.cmap = XDefaultColormap(xw.dpy, xw.scr);
894 xloadcols();
895
896 /* adjust fixed window geometry */
897 win.w = 2 * borderpx + term.col * win.cw;
898 win.h = 2 * borderpx + term.row * win.ch;
899 if (xw.gm & XNegative)
900 xw.l += DisplayWidth(xw.dpy, xw.scr) - win.w - 2;
901 if (xw.gm & YNegative)
902 xw.t += DisplayHeight(xw.dpy, xw.scr) - win.h - 2;
903
904 /* Events */
905 xw.attrs.background_pixel = dc.col[defaultbg].pixel;
906 xw.attrs.border_pixel = dc.col[defaultbg].pixel;
907 xw.attrs.bit_gravity = NorthWestGravity;
908 xw.attrs.event_mask = FocusChangeMask | KeyPressMask
909 | ExposureMask | VisibilityChangeMask | StructureNotifyMask
910 | ButtonMotionMask | ButtonPressMask | ButtonReleaseMask;
911 xw.attrs.colormap = xw.cmap;
912
913 if (!(opt_embed && (parent = strtol(opt_embed, NULL, 0))))
914 parent = XRootWindow(xw.dpy, xw.scr);
915 xw.win = XCreateWindow(xw.dpy, parent, xw.l, xw.t,
916 win.w, win.h, 0, XDefaultDepth(xw.dpy, xw.scr), InputOutput,
917 xw.vis, CWBackPixel | CWBorderPixel | CWBitGravity
918 | CWEventMask | CWColormap, &xw.attrs);
919
920 memset(&gcvalues, 0, sizeof(gcvalues));
921 gcvalues.graphics_exposures = False;
922 dc.gc = XCreateGC(xw.dpy, parent, GCGraphicsExposures,
923 &gcvalues);
924 xw.buf = XCreatePixmap(xw.dpy, xw.win, win.w, win.h,
925 DefaultDepth(xw.dpy, xw.scr));
926 XSetForeground(xw.dpy, dc.gc, dc.col[defaultbg].pixel);
927 XFillRectangle(xw.dpy, xw.buf, dc.gc, 0, 0, win.w, win.h);
928
929 /* Xft rendering context */
930 xw.draw = XftDrawCreate(xw.dpy, xw.buf, xw.vis, xw.cmap);
931
932 /* input methods */
933 if ((xw.xim = XOpenIM(xw.dpy, NULL, NULL, NULL)) == NULL) {
934 XSetLocaleModifiers("@im=local");
935 if ((xw.xim = XOpenIM(xw.dpy, NULL, NULL, NULL)) == NULL) {
936 XSetLocaleModifiers("@im=");
937 if ((xw.xim = XOpenIM(xw.dpy,
938 NULL, NULL, NULL)) == NULL) {
939 die("XOpenIM failed. Could not open input"
940 " device.\n");
941 }
942 }
943 }
944 xw.xic = XCreateIC(xw.xim, XNInputStyle, XIMPreeditNothing
945 | XIMStatusNothing, XNClientWindow, xw.win,
946 XNFocusWindow, xw.win, NULL);
947 if (xw.xic == NULL)
948 die("XCreateIC failed. Could not obtain input method.\n");
949
950 /* white cursor, black outline */
951 cursor = XCreateFontCursor(xw.dpy, mouseshape);
952 XDefineCursor(xw.dpy, xw.win, cursor);
953
954 if (XParseColor(xw.dpy, xw.cmap, colorname[mousefg], &xmousefg) == 0) {
955 xmousefg.red = 0xffff;
956 xmousefg.green = 0xffff;
957 xmousefg.blue = 0xffff;
958 }
959
960 if (XParseColor(xw.dpy, xw.cmap, colorname[mousebg], &xmousebg) == 0) {
961 xmousebg.red = 0x0000;
962 xmousebg.green = 0x0000;
963 xmousebg.blue = 0x0000;
964 }
965
966 XRecolorCursor(xw.dpy, cursor, &xmousefg, &xmousebg);
967
968 xw.xembed = XInternAtom(xw.dpy, "_XEMBED", False);
969 xw.wmdeletewin = XInternAtom(xw.dpy, "WM_DELETE_WINDOW", False);
970 xw.netwmname = XInternAtom(xw.dpy, "_NET_WM_NAME", False);
971 XSetWMProtocols(xw.dpy, xw.win, &xw.wmdeletewin, 1);
972
973 xw.netwmpid = XInternAtom(xw.dpy, "_NET_WM_PID", False);
974 XChangeProperty(xw.dpy, xw.win, xw.netwmpid, XA_CARDINAL, 32,
975 PropModeReplace, (uchar *)&thispid, 1);
976
977 resettitle();
978 XMapWindow(xw.dpy, xw.win);
979 xhints();
980 XSync(xw.dpy, False);
981
982 xsel.xtarget = XInternAtom(xw.dpy, "UTF8_STRING", 0);
983 if (xsel.xtarget == None)
984 xsel.xtarget = XA_STRING;
985}
986
987int
988xmakeglyphfontspecs(XftGlyphFontSpec *specs, const Glyph *glyphs, int len, int x, int y)
989{
990 float winx = borderpx + x * win.cw, winy = borderpx + y * win.ch, xp, yp;
991 ushort mode, prevmode = USHRT_MAX;
992 Font *font = &dc.font;
993 int frcflags = FRC_NORMAL;
994 float runewidth = win.cw;
995 Rune rune;
996 FT_UInt glyphidx;
997 FcResult fcres;
998 FcPattern *fcpattern, *fontpattern;
999 FcFontSet *fcsets[] = { NULL };
1000 FcCharSet *fccharset;
1001 int i, f, numspecs = 0;
1002
1003 for (i = 0, xp = winx, yp = winy + font->ascent; i < len; ++i) {
1004 /* Fetch rune and mode for current glyph. */
1005 rune = glyphs[i].u;
1006 mode = glyphs[i].mode;
1007
1008 /* Skip dummy wide-character spacing. */
1009 if (mode == ATTR_WDUMMY)
1010 continue;
1011
1012 /* Determine font for glyph if different from previous glyph. */
1013 if (prevmode != mode) {
1014 prevmode = mode;
1015 font = &dc.font;
1016 frcflags = FRC_NORMAL;
1017 runewidth = win.cw * ((mode & ATTR_WIDE) ? 2.0f : 1.0f);
1018 if ((mode & ATTR_ITALIC) && (mode & ATTR_BOLD)) {
1019 font = &dc.ibfont;
1020 frcflags = FRC_ITALICBOLD;
1021 } else if (mode & ATTR_ITALIC) {
1022 font = &dc.ifont;
1023 frcflags = FRC_ITALIC;
1024 } else if (mode & ATTR_BOLD) {
1025 font = &dc.bfont;
1026 frcflags = FRC_BOLD;
1027 }
1028 yp = winy + font->ascent;
1029 }
1030
1031 /* Lookup character index with default font. */
1032 glyphidx = XftCharIndex(xw.dpy, font->match, rune);
1033 if (glyphidx) {
1034 specs[numspecs].font = font->match;
1035 specs[numspecs].glyph = glyphidx;
1036 specs[numspecs].x = (short)xp;
1037 specs[numspecs].y = (short)yp;
1038 xp += runewidth;
1039 numspecs++;
1040 continue;
1041 }
1042
1043 /* Fallback on font cache, search the font cache for match. */
1044 for (f = 0; f < frclen; f++) {
1045 glyphidx = XftCharIndex(xw.dpy, frc[f].font, rune);
1046 /* Everything correct. */
1047 if (glyphidx && frc[f].flags == frcflags)
1048 break;
1049 /* We got a default font for a not found glyph. */
1050 if (!glyphidx && frc[f].flags == frcflags
1051 && frc[f].unicodep == rune) {
1052 break;
1053 }
1054 }
1055
1056 /* Nothing was found. Use fontconfig to find matching font. */
1057 if (f >= frclen) {
1058 if (!font->set)
1059 font->set = FcFontSort(0, font->pattern,
1060 1, 0, &fcres);
1061 fcsets[0] = font->set;
1062
1063 /*
1064 * Nothing was found in the cache. Now use
1065 * some dozen of Fontconfig calls to get the
1066 * font for one single character.
1067 *
1068 * Xft and fontconfig are design failures.
1069 */
1070 fcpattern = FcPatternDuplicate(font->pattern);
1071 fccharset = FcCharSetCreate();
1072
1073 FcCharSetAddChar(fccharset, rune);
1074 FcPatternAddCharSet(fcpattern, FC_CHARSET,
1075 fccharset);
1076 FcPatternAddBool(fcpattern, FC_SCALABLE, 1);
1077
1078 FcConfigSubstitute(0, fcpattern,
1079 FcMatchPattern);
1080 FcDefaultSubstitute(fcpattern);
1081
1082 fontpattern = FcFontSetMatch(0, fcsets, 1,
1083 fcpattern, &fcres);
1084
1085 /*
1086 * Overwrite or create the new cache entry.
1087 */
1088 if (frclen >= LEN(frc)) {
1089 frclen = LEN(frc) - 1;
1090 XftFontClose(xw.dpy, frc[frclen].font);
1091 frc[frclen].unicodep = 0;
1092 }
1093
1094 frc[frclen].font = XftFontOpenPattern(xw.dpy,
1095 fontpattern);
1096 frc[frclen].flags = frcflags;
1097 frc[frclen].unicodep = rune;
1098
1099 glyphidx = XftCharIndex(xw.dpy, frc[frclen].font, rune);
1100
1101 f = frclen;
1102 frclen++;
1103
1104 FcPatternDestroy(fcpattern);
1105 FcCharSetDestroy(fccharset);
1106 }
1107
1108 specs[numspecs].font = frc[f].font;
1109 specs[numspecs].glyph = glyphidx;
1110 specs[numspecs].x = (short)xp;
1111 specs[numspecs].y = (short)yp;
1112 xp += runewidth;
1113 numspecs++;
1114 }
1115
1116 return numspecs;
1117}
1118
1119void
1120xdrawglyphfontspecs(const XftGlyphFontSpec *specs, Glyph base, int len, int x, int y)
1121{
1122 int charlen = len * ((base.mode & ATTR_WIDE) ? 2 : 1);
1123 int winx = borderpx + x * win.cw, winy = borderpx + y * win.ch,
1124 width = charlen * win.cw;
1125 Color *fg, *bg, *temp, revfg, revbg, truefg, truebg;
1126 XRenderColor colfg, colbg;
1127 XRectangle r;
1128
1129 /* Fallback on color display for attributes not supported by the font */
1130 if (base.mode & ATTR_ITALIC && base.mode & ATTR_BOLD) {
1131 if (dc.ibfont.badslant || dc.ibfont.badweight)
1132 base.fg = defaultattr;
1133 } else if ((base.mode & ATTR_ITALIC && dc.ifont.badslant) ||
1134 (base.mode & ATTR_BOLD && dc.bfont.badweight)) {
1135 base.fg = defaultattr;
1136 }
1137
1138 if (IS_TRUECOL(base.fg)) {
1139 colfg.alpha = 0xffff;
1140 colfg.red = TRUERED(base.fg);
1141 colfg.green = TRUEGREEN(base.fg);
1142 colfg.blue = TRUEBLUE(base.fg);
1143 XftColorAllocValue(xw.dpy, xw.vis, xw.cmap, &colfg, &truefg);
1144 fg = &truefg;
1145 } else {
1146 fg = &dc.col[base.fg];
1147 }
1148
1149 if (IS_TRUECOL(base.bg)) {
1150 colbg.alpha = 0xffff;
1151 colbg.green = TRUEGREEN(base.bg);
1152 colbg.red = TRUERED(base.bg);
1153 colbg.blue = TRUEBLUE(base.bg);
1154 XftColorAllocValue(xw.dpy, xw.vis, xw.cmap, &colbg, &truebg);
1155 bg = &truebg;
1156 } else {
1157 bg = &dc.col[base.bg];
1158 }
1159
1160 /* Change basic system colors [0-7] to bright system colors [8-15] */
1161 if ((base.mode & ATTR_BOLD_FAINT) == ATTR_BOLD && BETWEEN(base.fg, 0, 7))
1162 fg = &dc.col[base.fg + 8];
1163
1164 if (IS_SET(MODE_REVERSE)) {
1165 if (fg == &dc.col[defaultfg]) {
1166 fg = &dc.col[defaultbg];
1167 } else {
1168 colfg.red = ~fg->color.red;
1169 colfg.green = ~fg->color.green;
1170 colfg.blue = ~fg->color.blue;
1171 colfg.alpha = fg->color.alpha;
1172 XftColorAllocValue(xw.dpy, xw.vis, xw.cmap, &colfg,
1173 &revfg);
1174 fg = &revfg;
1175 }
1176
1177 if (bg == &dc.col[defaultbg]) {
1178 bg = &dc.col[defaultfg];
1179 } else {
1180 colbg.red = ~bg->color.red;
1181 colbg.green = ~bg->color.green;
1182 colbg.blue = ~bg->color.blue;
1183 colbg.alpha = bg->color.alpha;
1184 XftColorAllocValue(xw.dpy, xw.vis, xw.cmap, &colbg,
1185 &revbg);
1186 bg = &revbg;
1187 }
1188 }
1189
1190 if (base.mode & ATTR_REVERSE) {
1191 temp = fg;
1192 fg = bg;
1193 bg = temp;
1194 }
1195
1196 if ((base.mode & ATTR_BOLD_FAINT) == ATTR_FAINT) {
1197 colfg.red = fg->color.red / 2;
1198 colfg.green = fg->color.green / 2;
1199 colfg.blue = fg->color.blue / 2;
1200 XftColorAllocValue(xw.dpy, xw.vis, xw.cmap, &colfg, &revfg);
1201 fg = &revfg;
1202 }
1203
1204 if (base.mode & ATTR_BLINK && term.mode & MODE_BLINK)
1205 fg = bg;
1206
1207 if (base.mode & ATTR_INVISIBLE)
1208 fg = bg;
1209
1210 /* Intelligent cleaning up of the borders. */
1211 if (x == 0) {
1212 xclear(0, (y == 0)? 0 : winy, borderpx,
1213 winy + win.ch + ((y >= term.row-1)? win.h : 0));
1214 }
1215 if (x + charlen >= term.col) {
1216 xclear(winx + width, (y == 0)? 0 : winy, win.w,
1217 ((y >= term.row-1)? win.h : (winy + win.ch)));
1218 }
1219 if (y == 0)
1220 xclear(winx, 0, winx + width, borderpx);
1221 if (y == term.row-1)
1222 xclear(winx, winy + win.ch, winx + width, win.h);
1223
1224 /* Clean up the region we want to draw to. */
1225 XftDrawRect(xw.draw, bg, winx, winy, width, win.ch);
1226
1227 /* Set the clip region because Xft is sometimes dirty. */
1228 r.x = 0;
1229 r.y = 0;
1230 r.height = win.ch;
1231 r.width = width;
1232 XftDrawSetClipRectangles(xw.draw, winx, winy, &r, 1);
1233
1234 /* Render the glyphs. */
1235 XftDrawGlyphFontSpec(xw.draw, fg, specs, len);
1236
1237 /* Render underline and strikethrough. */
1238 if (base.mode & ATTR_UNDERLINE) {
1239 XftDrawRect(xw.draw, fg, winx, winy + dc.font.ascent + 1,
1240 width, 1);
1241 }
1242
1243 if (base.mode & ATTR_STRUCK) {
1244 XftDrawRect(xw.draw, fg, winx, winy + 2 * dc.font.ascent / 3,
1245 width, 1);
1246 }
1247
1248 /* Reset clip to none. */
1249 XftDrawSetClip(xw.draw, 0);
1250}
1251
1252void
1253xdrawglyph(Glyph g, int x, int y)
1254{
1255 int numspecs;
1256 XftGlyphFontSpec spec;
1257
1258 numspecs = xmakeglyphfontspecs(&spec, &g, 1, x, y);
1259 xdrawglyphfontspecs(&spec, g, numspecs, x, y);
1260}
1261
1262void
1263xdrawcursor(void)
1264{
1265 static int oldx = 0, oldy = 0;
1266 int curx;
1267 Glyph g = {' ', ATTR_NULL, defaultbg, defaultcs}, og;
1268 int ena_sel = sel.ob.x != -1 && sel.alt == IS_SET(MODE_ALTSCREEN);
1269 Color drawcol;
1270
1271 LIMIT(oldx, 0, term.col-1);
1272 LIMIT(oldy, 0, term.row-1);
1273
1274 curx = term.c.x;
1275
1276 /* adjust position if in dummy */
1277 if (term.line[oldy][oldx].mode & ATTR_WDUMMY)
1278 oldx--;
1279 if (term.line[term.c.y][curx].mode & ATTR_WDUMMY)
1280 curx--;
1281
1282 /* remove the old cursor */
1283 og = term.line[oldy][oldx];
1284 if (ena_sel && selected(oldx, oldy))
1285 og.mode ^= ATTR_REVERSE;
1286 xdrawglyph(og, oldx, oldy);
1287
1288 g.u = term.line[term.c.y][term.c.x].u;
1289
1290 /*
1291 * Select the right color for the right mode.
1292 */
1293 if (IS_SET(MODE_REVERSE)) {
1294 g.mode |= ATTR_REVERSE;
1295 g.bg = defaultfg;
1296 if (ena_sel && selected(term.c.x, term.c.y)) {
1297 drawcol = dc.col[defaultcs];
1298 g.fg = defaultrcs;
1299 } else {
1300 drawcol = dc.col[defaultrcs];
1301 g.fg = defaultcs;
1302 }
1303 } else {
1304 if (ena_sel && selected(term.c.x, term.c.y)) {
1305 drawcol = dc.col[defaultrcs];
1306 g.fg = defaultfg;
1307 g.bg = defaultrcs;
1308 } else {
1309 drawcol = dc.col[defaultcs];
1310 }
1311 }
1312
1313 if (IS_SET(MODE_HIDE))
1314 return;
1315
1316 /* draw the new one */
1317 if (win.state & WIN_FOCUSED) {
1318 switch (win.cursor) {
1319 case 7: /* st extension: snowman */
1320 utf8decode("☃", &g.u, UTF_SIZ);
1321 case 0: /* Blinking Block */
1322 case 1: /* Blinking Block (Default) */
1323 case 2: /* Steady Block */
1324 g.mode |= term.line[term.c.y][curx].mode & ATTR_WIDE;
1325 xdrawglyph(g, term.c.x, term.c.y);
1326 break;
1327 case 3: /* Blinking Underline */
1328 case 4: /* Steady Underline */
1329 XftDrawRect(xw.draw, &drawcol,
1330 borderpx + curx * win.cw,
1331 borderpx + (term.c.y + 1) * win.ch - \
1332 cursorthickness,
1333 win.cw, cursorthickness);
1334 break;
1335 case 5: /* Blinking bar */
1336 case 6: /* Steady bar */
1337 XftDrawRect(xw.draw, &drawcol,
1338 borderpx + curx * win.cw,
1339 borderpx + term.c.y * win.ch,
1340 cursorthickness, win.ch);
1341 break;
1342 }
1343 } else {
1344 XftDrawRect(xw.draw, &drawcol,
1345 borderpx + curx * win.cw,
1346 borderpx + term.c.y * win.ch,
1347 win.cw - 1, 1);
1348 XftDrawRect(xw.draw, &drawcol,
1349 borderpx + curx * win.cw,
1350 borderpx + term.c.y * win.ch,
1351 1, win.ch - 1);
1352 XftDrawRect(xw.draw, &drawcol,
1353 borderpx + (curx + 1) * win.cw - 1,
1354 borderpx + term.c.y * win.ch,
1355 1, win.ch - 1);
1356 XftDrawRect(xw.draw, &drawcol,
1357 borderpx + curx * win.cw,
1358 borderpx + (term.c.y + 1) * win.ch - 1,
1359 win.cw, 1);
1360 }
1361 oldx = curx, oldy = term.c.y;
1362}
1363
1364void
1365xsetenv(void)
1366{
1367 char buf[sizeof(long) * 8 + 1];
1368
1369 snprintf(buf, sizeof(buf), "%lu", xw.win);
1370 setenv("WINDOWID", buf, 1);
1371}
1372
1373void
1374xsettitle(char *p)
1375{
1376 XTextProperty prop;
1377
1378 Xutf8TextListToTextProperty(xw.dpy, &p, 1, XUTF8StringStyle,
1379 &prop);
1380 XSetWMName(xw.dpy, xw.win, &prop);
1381 XSetTextProperty(xw.dpy, xw.win, &prop, xw.netwmname);
1382 XFree(prop.value);
1383}
1384
1385void
1386draw(void)
1387{
1388 drawregion(0, 0, term.col, term.row);
1389 XCopyArea(xw.dpy, xw.buf, xw.win, dc.gc, 0, 0, win.w,
1390 win.h, 0, 0);
1391 XSetForeground(xw.dpy, dc.gc,
1392 dc.col[IS_SET(MODE_REVERSE)?
1393 defaultfg : defaultbg].pixel);
1394}
1395
1396void
1397drawregion(int x1, int y1, int x2, int y2)
1398{
1399 int i, x, y, ox, numspecs;
1400 Glyph base, new;
1401 XftGlyphFontSpec *specs;
1402 int ena_sel = sel.ob.x != -1 && sel.alt == IS_SET(MODE_ALTSCREEN);
1403
1404 if (!(win.state & WIN_VISIBLE))
1405 return;
1406
1407 for (y = y1; y < y2; y++) {
1408 if (!term.dirty[y])
1409 continue;
1410
1411 term.dirty[y] = 0;
1412
1413 specs = term.specbuf;
1414 numspecs = xmakeglyphfontspecs(specs, &term.line[y][x1], x2 - x1, x1, y);
1415
1416 i = ox = 0;
1417 for (x = x1; x < x2 && i < numspecs; x++) {
1418 new = term.line[y][x];
1419 if (new.mode == ATTR_WDUMMY)
1420 continue;
1421 if (ena_sel && selected(x, y))
1422 new.mode ^= ATTR_REVERSE;
1423 if (i > 0 && ATTRCMP(base, new)) {
1424 xdrawglyphfontspecs(specs, base, i, ox, y);
1425 specs += i;
1426 numspecs -= i;
1427 i = 0;
1428 }
1429 if (i == 0) {
1430 ox = x;
1431 base = new;
1432 }
1433 i++;
1434 }
1435 if (i > 0)
1436 xdrawglyphfontspecs(specs, base, i, ox, y);
1437 }
1438 xdrawcursor();
1439}
1440
1441void
1442expose(XEvent *ev)
1443{
1444 redraw();
1445}
1446
1447void
1448visibility(XEvent *ev)
1449{
1450 XVisibilityEvent *e = &ev->xvisibility;
1451
1452 MODBIT(win.state, e->state != VisibilityFullyObscured, WIN_VISIBLE);
1453}
1454
1455void
1456unmap(XEvent *ev)
1457{
1458 win.state &= ~WIN_VISIBLE;
1459}
1460
1461void
1462xsetpointermotion(int set)
1463{
1464 MODBIT(xw.attrs.event_mask, set, PointerMotionMask);
1465 XChangeWindowAttributes(xw.dpy, xw.win, CWEventMask, &xw.attrs);
1466}
1467
1468void
1469xseturgency(int add)
1470{
1471 XWMHints *h = XGetWMHints(xw.dpy, xw.win);
1472
1473 MODBIT(h->flags, add, XUrgencyHint);
1474 XSetWMHints(xw.dpy, xw.win, h);
1475 XFree(h);
1476}
1477
1478void
1479xbell(int vol)
1480{
1481 XkbBell(xw.dpy, xw.win, vol, (Atom)NULL);
1482}
1483
1484unsigned long
1485xwinid(void)
1486{
1487 return xw.win;
1488}
1489
1490void
1491focus(XEvent *ev)
1492{
1493 XFocusChangeEvent *e = &ev->xfocus;
1494
1495 if (e->mode == NotifyGrab)
1496 return;
1497
1498 if (ev->type == FocusIn) {
1499 XSetICFocus(xw.xic);
1500 win.state |= WIN_FOCUSED;
1501 xseturgency(0);
1502 if (IS_SET(MODE_FOCUS))
1503 ttywrite("\033[I", 3);
1504 } else {
1505 XUnsetICFocus(xw.xic);
1506 win.state &= ~WIN_FOCUSED;
1507 if (IS_SET(MODE_FOCUS))
1508 ttywrite("\033[O", 3);
1509 }
1510}
1511
1512void
1513kpress(XEvent *ev)
1514{
1515 XKeyEvent *e = &ev->xkey;
1516 KeySym ksym;
1517 char buf[32], *customkey;
1518 int len;
1519 Rune c;
1520 Status status;
1521 Shortcut *bp;
1522
1523 if (IS_SET(MODE_KBDLOCK))
1524 return;
1525
1526 len = XmbLookupString(xw.xic, e, buf, sizeof buf, &ksym, &status);
1527 /* 1. shortcuts */
1528 for (bp = shortcuts; bp < shortcuts + shortcutslen; bp++) {
1529 if (ksym == bp->keysym && match(bp->mod, e->state)) {
1530 bp->func(&(bp->arg));
1531 return;
1532 }
1533 }
1534
1535 /* 2. custom keys from config.h */
1536 if ((customkey = kmap(ksym, e->state))) {
1537 ttysend(customkey, strlen(customkey));
1538 return;
1539 }
1540
1541 /* 3. composed string from input method */
1542 if (len == 0)
1543 return;
1544 if (len == 1 && e->state & Mod1Mask) {
1545 if (IS_SET(MODE_8BIT)) {
1546 if (*buf < 0177) {
1547 c = *buf | 0x80;
1548 len = utf8encode(c, buf);
1549 }
1550 } else {
1551 buf[1] = buf[0];
1552 buf[0] = '\033';
1553 len = 2;
1554 }
1555 }
1556 ttysend(buf, len);
1557}
1558
1559
1560void
1561cmessage(XEvent *e)
1562{
1563 /*
1564 * See xembed specs
1565 * http://standards.freedesktop.org/xembed-spec/xembed-spec-latest.html
1566 */
1567 if (e->xclient.message_type == xw.xembed && e->xclient.format == 32) {
1568 if (e->xclient.data.l[1] == XEMBED_FOCUS_IN) {
1569 win.state |= WIN_FOCUSED;
1570 xseturgency(0);
1571 } else if (e->xclient.data.l[1] == XEMBED_FOCUS_OUT) {
1572 win.state &= ~WIN_FOCUSED;
1573 }
1574 } else if (e->xclient.data.l[0] == xw.wmdeletewin) {
1575 /* Send SIGHUP to shell */
1576 kill(pid, SIGHUP);
1577 exit(0);
1578 }
1579}
1580
1581void
1582resize(XEvent *e)
1583{
1584 if (e->xconfigure.width == win.w && e->xconfigure.height == win.h)
1585 return;
1586
1587 cresize(e->xconfigure.width, e->xconfigure.height);
1588 ttyresize();
1589}
1590
1591void
1592run(void)
1593{
1594 XEvent ev;
1595 int w = win.w, h = win.h;
1596 fd_set rfd;
1597 int xfd = XConnectionNumber(xw.dpy), xev, blinkset = 0, dodraw = 0;
1598 struct timespec drawtimeout, *tv = NULL, now, last, lastblink;
1599 long deltatime;
1600
1601 /* Waiting for window mapping */
1602 do {
1603 XNextEvent(xw.dpy, &ev);
1604 /*
1605 * This XFilterEvent call is required because of XOpenIM. It
1606 * does filter out the key event and some client message for
1607 * the input method too.
1608 */
1609 if (XFilterEvent(&ev, None))
1610 continue;
1611 if (ev.type == ConfigureNotify) {
1612 w = ev.xconfigure.width;
1613 h = ev.xconfigure.height;
1614 }
1615 } while (ev.type != MapNotify);
1616
1617 cresize(w, h);
1618 ttynew();
1619 ttyresize();
1620
1621 clock_gettime(CLOCK_MONOTONIC, &last);
1622 lastblink = last;
1623
1624 for (xev = actionfps;;) {
1625 FD_ZERO(&rfd);
1626 FD_SET(cmdfd, &rfd);
1627 FD_SET(xfd, &rfd);
1628
1629 if (pselect(MAX(xfd, cmdfd)+1, &rfd, NULL, NULL, tv, NULL) < 0) {
1630 if (errno == EINTR)
1631 continue;
1632 die("select failed: %s\n", strerror(errno));
1633 }
1634 if (FD_ISSET(cmdfd, &rfd)) {
1635 ttyread();
1636 if (blinktimeout) {
1637 blinkset = tattrset(ATTR_BLINK);
1638 if (!blinkset)
1639 MODBIT(term.mode, 0, MODE_BLINK);
1640 }
1641 }
1642
1643 if (FD_ISSET(xfd, &rfd))
1644 xev = actionfps;
1645
1646 clock_gettime(CLOCK_MONOTONIC, &now);
1647 drawtimeout.tv_sec = 0;
1648 drawtimeout.tv_nsec = (1000 * 1E6)/ xfps;
1649 tv = &drawtimeout;
1650
1651 dodraw = 0;
1652 if (blinktimeout && TIMEDIFF(now, lastblink) > blinktimeout) {
1653 tsetdirtattr(ATTR_BLINK);
1654 term.mode ^= MODE_BLINK;
1655 lastblink = now;
1656 dodraw = 1;
1657 }
1658 deltatime = TIMEDIFF(now, last);
1659 if (deltatime > 1000 / (xev ? xfps : actionfps)) {
1660 dodraw = 1;
1661 last = now;
1662 }
1663
1664 if (dodraw) {
1665 while (XPending(xw.dpy)) {
1666 XNextEvent(xw.dpy, &ev);
1667 if (XFilterEvent(&ev, None))
1668 continue;
1669 if (handler[ev.type])
1670 (handler[ev.type])(&ev);
1671 }
1672
1673 draw();
1674 XFlush(xw.dpy);
1675
1676 if (xev && !FD_ISSET(xfd, &rfd))
1677 xev--;
1678 if (!FD_ISSET(cmdfd, &rfd) && !FD_ISSET(xfd, &rfd)) {
1679 if (blinkset) {
1680 if (TIMEDIFF(now, lastblink) \
1681 > blinktimeout) {
1682 drawtimeout.tv_nsec = 1000;
1683 } else {
1684 drawtimeout.tv_nsec = (1E6 * \
1685 (blinktimeout - \
1686 TIMEDIFF(now,
1687 lastblink)));
1688 }
1689 drawtimeout.tv_sec = \
1690 drawtimeout.tv_nsec / 1E9;
1691 drawtimeout.tv_nsec %= (long)1E9;
1692 } else {
1693 tv = NULL;
1694 }
1695 }
1696 }
1697 }
1698}
1699
1700int
1701main(int argc, char *argv[])
1702{
1703 xw.l = xw.t = 0;
1704 xw.isfixed = False;
1705 win.cursor = cursorshape;
1706
1707 ARGBEGIN {
1708 case 'a':
1709 allowaltscreen = 0;
1710 break;
1711 case 'c':
1712 opt_class = EARGF(usage());
1713 break;
1714 case 'e':
1715 if (argc > 0)
1716 --argc, ++argv;
1717 goto run;
1718 case 'f':
1719 opt_font = EARGF(usage());
1720 break;
1721 case 'g':
1722 xw.gm = XParseGeometry(EARGF(usage()),
1723 &xw.l, &xw.t, &cols, &rows);
1724 break;
1725 case 'i':
1726 xw.isfixed = 1;
1727 break;
1728 case 'o':
1729 opt_io = EARGF(usage());
1730 break;
1731 case 'l':
1732 opt_line = EARGF(usage());
1733 break;
1734 case 'n':
1735 opt_name = EARGF(usage());
1736 break;
1737 case 't':
1738 case 'T':
1739 opt_title = EARGF(usage());
1740 break;
1741 case 'w':
1742 opt_embed = EARGF(usage());
1743 break;
1744 case 'v':
1745 die("%s " VERSION " (c) 2010-2016 st engineers\n", argv0);
1746 break;
1747 default:
1748 usage();
1749 } ARGEND;
1750
1751run:
1752 if (argc > 0) {
1753 /* eat all remaining arguments */
1754 opt_cmd = argv;
1755 if (!opt_title && !opt_line)
1756 opt_title = basename(xstrdup(argv[0]));
1757 }
1758 setlocale(LC_CTYPE, "");
1759 XSetLocaleModifiers("");
1760 tnew(MAX(cols, 1), MAX(rows, 1));
1761 xinit();
1762 selinit();
1763 run();
1764
1765 return 0;
1766}