aboutsummaryrefslogtreecommitdiff
path: root/st.c
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 /st.c
parentc63a87cd936c1eeef14c4c21572e5b782d3df4bc (diff)
downloadst-e2ee5ee6114eb74bb08cb9abe5a3020203e92688.tar.gz
st-e2ee5ee6114eb74bb08cb9abe5a3020203e92688.zip
Split X-specific code into x.c
Diffstat (limited to 'st.c')
-rw-r--r--st.c2026
1 files changed, 65 insertions, 1961 deletions
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