|
0
|
1 /* See LICENSE for license details. */ |
|
|
2 #include <errno.h> |
|
|
3 #include <math.h> |
|
|
4 #include <limits.h> |
|
|
5 #include <locale.h> |
|
|
6 #include <signal.h> |
|
|
7 #include <sys/select.h> |
|
|
8 #include <time.h> |
|
|
9 #include <unistd.h> |
|
|
10 #include <libgen.h> |
|
|
11 #include <X11/Xatom.h> |
|
|
12 #include <X11/Xlib.h> |
|
|
13 #include <X11/cursorfont.h> |
|
|
14 #include <X11/keysym.h> |
|
|
15 #include <X11/Xft/Xft.h> |
|
|
16 #include <X11/XKBlib.h> |
|
|
17 |
|
|
18 char *argv0; |
|
|
19 #include "arg.h" |
|
|
20 #include "st.h" |
|
|
21 #include "win.h" |
|
|
22 |
|
|
23 /* types used in config.h */ |
|
|
24 typedef struct { |
|
|
25 uint mod; |
|
|
26 KeySym keysym; |
|
|
27 void (*func)(const Arg *); |
|
|
28 const Arg arg; |
|
|
29 } Shortcut; |
|
|
30 |
|
|
31 typedef struct { |
|
|
32 uint mod; |
|
|
33 uint button; |
|
|
34 void (*func)(const Arg *); |
|
|
35 const Arg arg; |
|
|
36 uint release; |
|
|
37 } MouseShortcut; |
|
|
38 |
|
|
39 typedef struct { |
|
|
40 KeySym k; |
|
|
41 uint mask; |
|
|
42 char *s; |
|
|
43 /* three-valued logic variables: 0 indifferent, 1 on, -1 off */ |
|
|
44 signed char appkey; /* application keypad */ |
|
|
45 signed char appcursor; /* application cursor */ |
|
|
46 } Key; |
|
|
47 |
|
|
48 /* X modifiers */ |
|
|
49 #define XK_ANY_MOD UINT_MAX |
|
|
50 #define XK_NO_MOD 0 |
|
|
51 #define XK_SWITCH_MOD (1<<13|1<<14) |
|
|
52 |
|
|
53 /* function definitions used in config.h */ |
|
|
54 static void clipcopy(const Arg *); |
|
|
55 static void clippaste(const Arg *); |
|
|
56 static void numlock(const Arg *); |
|
|
57 static void selpaste(const Arg *); |
|
|
58 static void zoom(const Arg *); |
|
|
59 static void zoomabs(const Arg *); |
|
|
60 static void zoomreset(const Arg *); |
|
|
61 static void ttysend(const Arg *); |
|
|
62 |
|
|
63 /* config.h for applying patches and the configuration. */ |
|
|
64 #include "config.h" |
|
|
65 |
|
|
66 /* XEMBED messages */ |
|
|
67 #define XEMBED_FOCUS_IN 4 |
|
|
68 #define XEMBED_FOCUS_OUT 5 |
|
|
69 |
|
|
70 /* macros */ |
|
|
71 #define IS_SET(flag) ((win.mode & (flag)) != 0) |
|
|
72 #define TRUERED(x) (((x) & 0xff0000) >> 8) |
|
|
73 #define TRUEGREEN(x) (((x) & 0xff00)) |
|
|
74 #define TRUEBLUE(x) (((x) & 0xff) << 8) |
|
|
75 |
|
|
76 typedef XftDraw *Draw; |
|
|
77 typedef XftColor Color; |
|
|
78 typedef XftGlyphFontSpec GlyphFontSpec; |
|
|
79 |
|
|
80 /* Purely graphic info */ |
|
|
81 typedef struct { |
|
|
82 int tw, th; /* tty width and height */ |
|
|
83 int w, h; /* window width and height */ |
|
|
84 int ch; /* char height */ |
|
|
85 int cw; /* char width */ |
|
|
86 int mode; /* window state/mode flags */ |
|
|
87 int cursor; /* cursor style */ |
|
|
88 } TermWindow; |
|
|
89 |
|
|
90 typedef struct { |
|
|
91 Display *dpy; |
|
|
92 Colormap cmap; |
|
|
93 Window win; |
|
|
94 Drawable buf; |
|
|
95 GlyphFontSpec *specbuf; /* font spec buffer used for rendering */ |
|
|
96 Atom xembed, wmdeletewin, netwmname, netwmiconname, netwmpid; |
|
|
97 struct { |
|
|
98 XIM xim; |
|
|
99 XIC xic; |
|
|
100 XPoint spot; |
|
|
101 XVaNestedList spotlist; |
|
|
102 } ime; |
|
|
103 Draw draw; |
|
|
104 Visual *vis; |
|
|
105 XSetWindowAttributes attrs; |
|
|
106 int scr; |
|
|
107 int isfixed; /* is fixed geometry? */ |
|
|
108 int depth; /* bit depth */ |
|
|
109 int l, t; /* left and top offset */ |
|
|
110 int gm; /* geometry mask */ |
|
|
111 } XWindow; |
|
|
112 |
|
|
113 typedef struct { |
|
|
114 Atom xtarget; |
|
|
115 char *primary, *clipboard; |
|
|
116 struct timespec tclick1; |
|
|
117 struct timespec tclick2; |
|
|
118 } XSelection; |
|
|
119 |
|
|
120 /* Font structure */ |
|
|
121 #define Font Font_ |
|
|
122 typedef struct { |
|
|
123 int height; |
|
|
124 int width; |
|
|
125 int ascent; |
|
|
126 int descent; |
|
|
127 int badslant; |
|
|
128 int badweight; |
|
|
129 short lbearing; |
|
|
130 short rbearing; |
|
|
131 XftFont *match; |
|
|
132 FcFontSet *set; |
|
|
133 FcPattern *pattern; |
|
|
134 } Font; |
|
|
135 |
|
|
136 /* Drawing Context */ |
|
|
137 typedef struct { |
|
|
138 Color *col; |
|
|
139 size_t collen; |
|
|
140 Font font, bfont, ifont, ibfont; |
|
|
141 GC gc; |
|
|
142 } DC; |
|
|
143 |
|
|
144 static inline ushort sixd_to_16bit(int); |
|
|
145 static int xmakeglyphfontspecs(XftGlyphFontSpec *, const Glyph *, int, int, int); |
|
|
146 static void xdrawglyphfontspecs(const XftGlyphFontSpec *, Glyph, int, int, int); |
|
|
147 static void xdrawglyph(Glyph, int, int); |
|
|
148 static void xclear(int, int, int, int); |
|
|
149 static int xgeommasktogravity(int); |
|
|
150 static int ximopen(Display *); |
|
|
151 static void ximinstantiate(Display *, XPointer, XPointer); |
|
|
152 static void ximdestroy(XIM, XPointer, XPointer); |
|
|
153 static int xicdestroy(XIC, XPointer, XPointer); |
|
|
154 static void xinit(int, int); |
|
|
155 static void cresize(int, int); |
|
|
156 static void xresize(int, int); |
|
|
157 static void xhints(void); |
|
|
158 static int xloadcolor(int, const char *, Color *); |
|
|
159 static int xloadfont(Font *, FcPattern *); |
|
|
160 static void xloadfonts(const char *, double); |
|
|
161 static void xunloadfont(Font *); |
|
|
162 static void xunloadfonts(void); |
|
|
163 static void xsetenv(void); |
|
|
164 static void xseturgency(int); |
|
|
165 static int evcol(XEvent *); |
|
|
166 static int evrow(XEvent *); |
|
|
167 |
|
|
168 static void expose(XEvent *); |
|
|
169 static void visibility(XEvent *); |
|
|
170 static void unmap(XEvent *); |
|
|
171 static void kpress(XEvent *); |
|
|
172 static void cmessage(XEvent *); |
|
|
173 static void resize(XEvent *); |
|
|
174 static void focus(XEvent *); |
|
|
175 static uint buttonmask(uint); |
|
|
176 static int mouseaction(XEvent *, uint); |
|
|
177 static void brelease(XEvent *); |
|
|
178 static void bpress(XEvent *); |
|
|
179 static void bmotion(XEvent *); |
|
|
180 static void propnotify(XEvent *); |
|
|
181 static void selnotify(XEvent *); |
|
|
182 static void selclear_(XEvent *); |
|
|
183 static void selrequest(XEvent *); |
|
|
184 static void setsel(char *, Time); |
|
|
185 static void mousesel(XEvent *, int); |
|
|
186 static void mousereport(XEvent *); |
|
|
187 static char *kmap(KeySym, uint); |
|
|
188 static int match(uint, uint); |
|
|
189 |
|
|
190 static void run(void); |
|
|
191 static void usage(void); |
|
|
192 |
|
|
193 static void (*handler[LASTEvent])(XEvent *) = { |
|
|
194 [KeyPress] = kpress, |
|
|
195 [ClientMessage] = cmessage, |
|
|
196 [ConfigureNotify] = resize, |
|
|
197 [VisibilityNotify] = visibility, |
|
|
198 [UnmapNotify] = unmap, |
|
|
199 [Expose] = expose, |
|
|
200 [FocusIn] = focus, |
|
|
201 [FocusOut] = focus, |
|
|
202 [MotionNotify] = bmotion, |
|
|
203 [ButtonPress] = bpress, |
|
|
204 [ButtonRelease] = brelease, |
|
|
205 /* |
|
|
206 * Uncomment if you want the selection to disappear when you select something |
|
|
207 * different in another window. |
|
|
208 */ |
|
|
209 /* [SelectionClear] = selclear_, */ |
|
|
210 [SelectionNotify] = selnotify, |
|
|
211 /* |
|
|
212 * PropertyNotify is only turned on when there is some INCR transfer happening |
|
|
213 * for the selection retrieval. |
|
|
214 */ |
|
|
215 [PropertyNotify] = propnotify, |
|
|
216 [SelectionRequest] = selrequest, |
|
|
217 }; |
|
|
218 |
|
|
219 /* Globals */ |
|
|
220 static DC dc; |
|
|
221 static XWindow xw; |
|
|
222 static XSelection xsel; |
|
|
223 static TermWindow win; |
|
|
224 |
|
|
225 /* Font Ring Cache */ |
|
|
226 enum { |
|
|
227 FRC_NORMAL, |
|
|
228 FRC_ITALIC, |
|
|
229 FRC_BOLD, |
|
|
230 FRC_ITALICBOLD |
|
|
231 }; |
|
|
232 |
|
|
233 typedef struct { |
|
|
234 XftFont *font; |
|
|
235 int flags; |
|
|
236 Rune unicodep; |
|
|
237 } Fontcache; |
|
|
238 |
|
|
239 /* Fontcache is an array now. A new font will be appended to the array. */ |
|
|
240 static Fontcache *frc = NULL; |
|
|
241 static int frclen = 0; |
|
|
242 static int frccap = 0; |
|
|
243 static char *usedfont = NULL; |
|
|
244 static double usedfontsize = 0; |
|
|
245 static double defaultfontsize = 0; |
|
|
246 |
|
|
247 static char *opt_class = NULL; |
|
|
248 static char **opt_cmd = NULL; |
|
|
249 static char *opt_embed = NULL; |
|
|
250 static char *opt_font = NULL; |
|
|
251 static char *opt_io = NULL; |
|
|
252 static char *opt_line = NULL; |
|
|
253 static char *opt_name = NULL; |
|
|
254 static char *opt_title = NULL; |
|
|
255 |
|
|
256 static uint buttons; /* bit field of pressed buttons */ |
|
|
257 |
|
|
258 void |
|
|
259 clipcopy(const Arg *dummy) |
|
|
260 { |
|
|
261 Atom clipboard; |
|
|
262 |
|
|
263 free(xsel.clipboard); |
|
|
264 xsel.clipboard = NULL; |
|
|
265 |
|
|
266 if (xsel.primary != NULL) { |
|
|
267 xsel.clipboard = xstrdup(xsel.primary); |
|
|
268 clipboard = XInternAtom(xw.dpy, "CLIPBOARD", 0); |
|
|
269 XSetSelectionOwner(xw.dpy, clipboard, xw.win, CurrentTime); |
|
|
270 } |
|
|
271 } |
|
|
272 |
|
|
273 void |
|
|
274 clippaste(const Arg *dummy) |
|
|
275 { |
|
|
276 Atom clipboard; |
|
|
277 |
|
|
278 clipboard = XInternAtom(xw.dpy, "CLIPBOARD", 0); |
|
|
279 XConvertSelection(xw.dpy, clipboard, xsel.xtarget, clipboard, |
|
|
280 xw.win, CurrentTime); |
|
|
281 } |
|
|
282 |
|
|
283 void |
|
|
284 selpaste(const Arg *dummy) |
|
|
285 { |
|
|
286 XConvertSelection(xw.dpy, XA_PRIMARY, xsel.xtarget, XA_PRIMARY, |
|
|
287 xw.win, CurrentTime); |
|
|
288 } |
|
|
289 |
|
|
290 void |
|
|
291 numlock(const Arg *dummy) |
|
|
292 { |
|
|
293 win.mode ^= MODE_NUMLOCK; |
|
|
294 } |
|
|
295 |
|
|
296 void |
|
|
297 zoom(const Arg *arg) |
|
|
298 { |
|
|
299 Arg larg; |
|
|
300 |
|
|
301 larg.f = usedfontsize + arg->f; |
|
|
302 zoomabs(&larg); |
|
|
303 } |
|
|
304 |
|
|
305 void |
|
|
306 zoomabs(const Arg *arg) |
|
|
307 { |
|
|
308 xunloadfonts(); |
|
|
309 xloadfonts(usedfont, arg->f); |
|
|
310 cresize(0, 0); |
|
|
311 redraw(); |
|
|
312 xhints(); |
|
|
313 } |
|
|
314 |
|
|
315 void |
|
|
316 zoomreset(const Arg *arg) |
|
|
317 { |
|
|
318 Arg larg; |
|
|
319 |
|
|
320 if (defaultfontsize > 0) { |
|
|
321 larg.f = defaultfontsize; |
|
|
322 zoomabs(&larg); |
|
|
323 } |
|
|
324 } |
|
|
325 |
|
|
326 void |
|
|
327 ttysend(const Arg *arg) |
|
|
328 { |
|
|
329 ttywrite(arg->s, strlen(arg->s), 1); |
|
|
330 } |
|
|
331 |
|
|
332 int |
|
|
333 evcol(XEvent *e) |
|
|
334 { |
|
|
335 int x = e->xbutton.x - borderpx; |
|
|
336 LIMIT(x, 0, win.tw - 1); |
|
|
337 return x / win.cw; |
|
|
338 } |
|
|
339 |
|
|
340 int |
|
|
341 evrow(XEvent *e) |
|
|
342 { |
|
|
343 int y = e->xbutton.y - borderpx; |
|
|
344 LIMIT(y, 0, win.th - 1); |
|
|
345 return y / win.ch; |
|
|
346 } |
|
|
347 |
|
|
348 void |
|
|
349 mousesel(XEvent *e, int done) |
|
|
350 { |
|
|
351 int type, seltype = SEL_REGULAR; |
|
|
352 uint state = e->xbutton.state & ~(Button1Mask | forcemousemod); |
|
|
353 |
|
|
354 for (type = 1; type < LEN(selmasks); ++type) { |
|
|
355 if (match(selmasks[type], state)) { |
|
|
356 seltype = type; |
|
|
357 break; |
|
|
358 } |
|
|
359 } |
|
|
360 selextend(evcol(e), evrow(e), seltype, done); |
|
|
361 if (done) |
|
|
362 setsel(getsel(), e->xbutton.time); |
|
|
363 } |
|
|
364 |
|
|
365 void |
|
|
366 mousereport(XEvent *e) |
|
|
367 { |
|
|
368 int len, btn, code; |
|
|
369 int x = evcol(e), y = evrow(e); |
|
|
370 int state = e->xbutton.state; |
|
|
371 char buf[40]; |
|
|
372 static int ox, oy; |
|
|
373 |
|
|
374 if (e->type == MotionNotify) { |
|
|
375 if (x == ox && y == oy) |
|
|
376 return; |
|
|
377 if (!IS_SET(MODE_MOUSEMOTION) && !IS_SET(MODE_MOUSEMANY)) |
|
|
378 return; |
|
|
379 /* MODE_MOUSEMOTION: no reporting if no button is pressed */ |
|
|
380 if (IS_SET(MODE_MOUSEMOTION) && buttons == 0) |
|
|
381 return; |
|
|
382 /* Set btn to lowest-numbered pressed button, or 12 if no |
|
|
383 * buttons are pressed. */ |
|
|
384 for (btn = 1; btn <= 11 && !(buttons & (1<<(btn-1))); btn++) |
|
|
385 ; |
|
|
386 code = 32; |
|
|
387 } else { |
|
|
388 btn = e->xbutton.button; |
|
|
389 /* Only buttons 1 through 11 can be encoded */ |
|
|
390 if (btn < 1 || btn > 11) |
|
|
391 return; |
|
|
392 if (e->type == ButtonRelease) { |
|
|
393 /* MODE_MOUSEX10: no button release reporting */ |
|
|
394 if (IS_SET(MODE_MOUSEX10)) |
|
|
395 return; |
|
|
396 /* Don't send release events for the scroll wheel */ |
|
|
397 if (btn == 4 || btn == 5) |
|
|
398 return; |
|
|
399 } |
|
|
400 code = 0; |
|
|
401 } |
|
|
402 |
|
|
403 ox = x; |
|
|
404 oy = y; |
|
|
405 |
|
|
406 /* Encode btn into code. If no button is pressed for a motion event in |
|
|
407 * MODE_MOUSEMANY, then encode it as a release. */ |
|
|
408 if ((!IS_SET(MODE_MOUSESGR) && e->type == ButtonRelease) || btn == 12) |
|
|
409 code += 3; |
|
|
410 else if (btn >= 8) |
|
|
411 code += 128 + btn - 8; |
|
|
412 else if (btn >= 4) |
|
|
413 code += 64 + btn - 4; |
|
|
414 else |
|
|
415 code += btn - 1; |
|
|
416 |
|
|
417 if (!IS_SET(MODE_MOUSEX10)) { |
|
|
418 code += ((state & ShiftMask ) ? 4 : 0) |
|
|
419 + ((state & Mod1Mask ) ? 8 : 0) /* meta key: alt */ |
|
|
420 + ((state & ControlMask) ? 16 : 0); |
|
|
421 } |
|
|
422 |
|
|
423 if (IS_SET(MODE_MOUSESGR)) { |
|
|
424 len = snprintf(buf, sizeof(buf), "\033[<%d;%d;%d%c", |
|
|
425 code, x+1, y+1, |
|
|
426 e->type == ButtonRelease ? 'm' : 'M'); |
|
|
427 } else if (x < 223 && y < 223) { |
|
|
428 len = snprintf(buf, sizeof(buf), "\033[M%c%c%c", |
|
|
429 32+code, 32+x+1, 32+y+1); |
|
|
430 } else { |
|
|
431 return; |
|
|
432 } |
|
|
433 |
|
|
434 ttywrite(buf, len, 0); |
|
|
435 } |
|
|
436 |
|
|
437 uint |
|
|
438 buttonmask(uint button) |
|
|
439 { |
|
|
440 return button == Button1 ? Button1Mask |
|
|
441 : button == Button2 ? Button2Mask |
|
|
442 : button == Button3 ? Button3Mask |
|
|
443 : button == Button4 ? Button4Mask |
|
|
444 : button == Button5 ? Button5Mask |
|
|
445 : 0; |
|
|
446 } |
|
|
447 |
|
|
448 int |
|
|
449 mouseaction(XEvent *e, uint release) |
|
|
450 { |
|
|
451 MouseShortcut *ms; |
|
|
452 |
|
|
453 /* ignore Button<N>mask for Button<N> - it's set on release */ |
|
|
454 uint state = e->xbutton.state & ~buttonmask(e->xbutton.button); |
|
|
455 |
|
|
456 for (ms = mshortcuts; ms < mshortcuts + LEN(mshortcuts); ms++) { |
|
|
457 if (ms->release == release && |
|
|
458 ms->button == e->xbutton.button && |
|
|
459 (match(ms->mod, state) || /* exact or forced */ |
|
|
460 match(ms->mod, state & ~forcemousemod))) { |
|
|
461 ms->func(&(ms->arg)); |
|
|
462 return 1; |
|
|
463 } |
|
|
464 } |
|
|
465 |
|
|
466 return 0; |
|
|
467 } |
|
|
468 |
|
|
469 void |
|
|
470 bpress(XEvent *e) |
|
|
471 { |
|
|
472 int btn = e->xbutton.button; |
|
|
473 struct timespec now; |
|
|
474 int snap; |
|
|
475 |
|
|
476 if (1 <= btn && btn <= 11) |
|
|
477 buttons |= 1 << (btn-1); |
|
|
478 |
|
|
479 if (IS_SET(MODE_MOUSE) && !(e->xbutton.state & forcemousemod)) { |
|
|
480 mousereport(e); |
|
|
481 return; |
|
|
482 } |
|
|
483 |
|
|
484 if (mouseaction(e, 0)) |
|
|
485 return; |
|
|
486 |
|
|
487 if (btn == Button1) { |
|
|
488 /* |
|
|
489 * If the user clicks below predefined timeouts specific |
|
|
490 * snapping behaviour is exposed. |
|
|
491 */ |
|
|
492 clock_gettime(CLOCK_MONOTONIC, &now); |
|
|
493 if (TIMEDIFF(now, xsel.tclick2) <= tripleclicktimeout) { |
|
|
494 snap = SNAP_LINE; |
|
|
495 } else if (TIMEDIFF(now, xsel.tclick1) <= doubleclicktimeout) { |
|
|
496 snap = SNAP_WORD; |
|
|
497 } else { |
|
|
498 snap = 0; |
|
|
499 } |
|
|
500 xsel.tclick2 = xsel.tclick1; |
|
|
501 xsel.tclick1 = now; |
|
|
502 |
|
|
503 selstart(evcol(e), evrow(e), snap); |
|
|
504 } |
|
|
505 } |
|
|
506 |
|
|
507 void |
|
|
508 propnotify(XEvent *e) |
|
|
509 { |
|
|
510 XPropertyEvent *xpev; |
|
|
511 Atom clipboard = XInternAtom(xw.dpy, "CLIPBOARD", 0); |
|
|
512 |
|
|
513 xpev = &e->xproperty; |
|
|
514 if (xpev->state == PropertyNewValue && |
|
|
515 (xpev->atom == XA_PRIMARY || |
|
|
516 xpev->atom == clipboard)) { |
|
|
517 selnotify(e); |
|
|
518 } |
|
|
519 } |
|
|
520 |
|
|
521 void |
|
|
522 selnotify(XEvent *e) |
|
|
523 { |
|
|
524 ulong nitems, ofs, rem; |
|
|
525 int format; |
|
|
526 uchar *data, *last, *repl; |
|
|
527 Atom type, incratom, property = None; |
|
|
528 |
|
|
529 incratom = XInternAtom(xw.dpy, "INCR", 0); |
|
|
530 |
|
|
531 ofs = 0; |
|
|
532 if (e->type == SelectionNotify) |
|
|
533 property = e->xselection.property; |
|
|
534 else if (e->type == PropertyNotify) |
|
|
535 property = e->xproperty.atom; |
|
|
536 |
|
|
537 if (property == None) |
|
|
538 return; |
|
|
539 |
|
|
540 do { |
|
|
541 if (XGetWindowProperty(xw.dpy, xw.win, property, ofs, |
|
|
542 BUFSIZ/4, False, AnyPropertyType, |
|
|
543 &type, &format, &nitems, &rem, |
|
|
544 &data)) { |
|
|
545 fprintf(stderr, "Clipboard allocation failed\n"); |
|
|
546 return; |
|
|
547 } |
|
|
548 |
|
|
549 if (e->type == PropertyNotify && nitems == 0 && rem == 0) { |
|
|
550 /* |
|
|
551 * If there is some PropertyNotify with no data, then |
|
|
552 * this is the signal of the selection owner that all |
|
|
553 * data has been transferred. We won't need to receive |
|
|
554 * PropertyNotify events anymore. |
|
|
555 */ |
|
|
556 MODBIT(xw.attrs.event_mask, 0, PropertyChangeMask); |
|
|
557 XChangeWindowAttributes(xw.dpy, xw.win, CWEventMask, |
|
|
558 &xw.attrs); |
|
|
559 } |
|
|
560 |
|
|
561 if (type == incratom) { |
|
|
562 /* |
|
|
563 * Activate the PropertyNotify events so we receive |
|
|
564 * when the selection owner does send us the next |
|
|
565 * chunk of data. |
|
|
566 */ |
|
|
567 MODBIT(xw.attrs.event_mask, 1, PropertyChangeMask); |
|
|
568 XChangeWindowAttributes(xw.dpy, xw.win, CWEventMask, |
|
|
569 &xw.attrs); |
|
|
570 |
|
|
571 /* |
|
|
572 * Deleting the property is the transfer start signal. |
|
|
573 */ |
|
|
574 XDeleteProperty(xw.dpy, xw.win, (int)property); |
|
|
575 continue; |
|
|
576 } |
|
|
577 |
|
|
578 /* |
|
|
579 * As seen in getsel: |
|
|
580 * Line endings are inconsistent in the terminal and GUI world |
|
|
581 * copy and pasting. When receiving some selection data, |
|
|
582 * replace all '\n' with '\r'. |
|
|
583 * FIXME: Fix the computer world. |
|
|
584 */ |
|
|
585 repl = data; |
|
|
586 last = data + nitems * format / 8; |
|
|
587 while ((repl = memchr(repl, '\n', last - repl))) { |
|
|
588 *repl++ = '\r'; |
|
|
589 } |
|
|
590 |
|
|
591 if (IS_SET(MODE_BRCKTPASTE) && ofs == 0) |
|
|
592 ttywrite("\033[200~", 6, 0); |
|
|
593 ttywrite((char *)data, nitems * format / 8, 1); |
|
|
594 if (IS_SET(MODE_BRCKTPASTE) && rem == 0) |
|
|
595 ttywrite("\033[201~", 6, 0); |
|
|
596 XFree(data); |
|
|
597 /* number of 32-bit chunks returned */ |
|
|
598 ofs += nitems * format / 32; |
|
|
599 } while (rem > 0); |
|
|
600 |
|
|
601 /* |
|
|
602 * Deleting the property again tells the selection owner to send the |
|
|
603 * next data chunk in the property. |
|
|
604 */ |
|
|
605 XDeleteProperty(xw.dpy, xw.win, (int)property); |
|
|
606 } |
|
|
607 |
|
|
608 void |
|
|
609 xclipcopy(void) |
|
|
610 { |
|
|
611 clipcopy(NULL); |
|
|
612 } |
|
|
613 |
|
|
614 void |
|
|
615 selclear_(XEvent *e) |
|
|
616 { |
|
|
617 selclear(); |
|
|
618 } |
|
|
619 |
|
|
620 void |
|
|
621 selrequest(XEvent *e) |
|
|
622 { |
|
|
623 XSelectionRequestEvent *xsre; |
|
|
624 XSelectionEvent xev; |
|
|
625 Atom xa_targets, string, clipboard; |
|
|
626 char *seltext; |
|
|
627 |
|
|
628 xsre = (XSelectionRequestEvent *) e; |
|
|
629 xev.type = SelectionNotify; |
|
|
630 xev.requestor = xsre->requestor; |
|
|
631 xev.selection = xsre->selection; |
|
|
632 xev.target = xsre->target; |
|
|
633 xev.time = xsre->time; |
|
|
634 if (xsre->property == None) |
|
|
635 xsre->property = xsre->target; |
|
|
636 |
|
|
637 /* reject */ |
|
|
638 xev.property = None; |
|
|
639 |
|
|
640 xa_targets = XInternAtom(xw.dpy, "TARGETS", 0); |
|
|
641 if (xsre->target == xa_targets) { |
|
|
642 /* respond with the supported type */ |
|
|
643 string = xsel.xtarget; |
|
|
644 XChangeProperty(xsre->display, xsre->requestor, xsre->property, |
|
|
645 XA_ATOM, 32, PropModeReplace, |
|
|
646 (uchar *) &string, 1); |
|
|
647 xev.property = xsre->property; |
|
|
648 } else if (xsre->target == xsel.xtarget || xsre->target == XA_STRING) { |
|
|
649 /* |
|
|
650 * xith XA_STRING non ascii characters may be incorrect in the |
|
|
651 * requestor. It is not our problem, use utf8. |
|
|
652 */ |
|
|
653 clipboard = XInternAtom(xw.dpy, "CLIPBOARD", 0); |
|
|
654 if (xsre->selection == XA_PRIMARY) { |
|
|
655 seltext = xsel.primary; |
|
|
656 } else if (xsre->selection == clipboard) { |
|
|
657 seltext = xsel.clipboard; |
|
|
658 } else { |
|
|
659 fprintf(stderr, |
|
|
660 "Unhandled clipboard selection 0x%lx\n", |
|
|
661 xsre->selection); |
|
|
662 return; |
|
|
663 } |
|
|
664 if (seltext != NULL) { |
|
|
665 XChangeProperty(xsre->display, xsre->requestor, |
|
|
666 xsre->property, xsre->target, |
|
|
667 8, PropModeReplace, |
|
|
668 (uchar *)seltext, strlen(seltext)); |
|
|
669 xev.property = xsre->property; |
|
|
670 } |
|
|
671 } |
|
|
672 |
|
|
673 /* all done, send a notification to the listener */ |
|
|
674 if (!XSendEvent(xsre->display, xsre->requestor, 1, 0, (XEvent *) &xev)) |
|
|
675 fprintf(stderr, "Error sending SelectionNotify event\n"); |
|
|
676 } |
|
|
677 |
|
|
678 void |
|
|
679 setsel(char *str, Time t) |
|
|
680 { |
|
|
681 if (!str) |
|
|
682 return; |
|
|
683 |
|
|
684 free(xsel.primary); |
|
|
685 xsel.primary = str; |
|
|
686 |
|
|
687 XSetSelectionOwner(xw.dpy, XA_PRIMARY, xw.win, t); |
|
|
688 if (XGetSelectionOwner(xw.dpy, XA_PRIMARY) != xw.win) |
|
|
689 selclear(); |
|
|
690 } |
|
|
691 |
|
|
692 void |
|
|
693 xsetsel(char *str) |
|
|
694 { |
|
|
695 setsel(str, CurrentTime); |
|
|
696 } |
|
|
697 |
|
|
698 void |
|
|
699 brelease(XEvent *e) |
|
|
700 { |
|
|
701 int btn = e->xbutton.button; |
|
|
702 |
|
|
703 if (1 <= btn && btn <= 11) |
|
|
704 buttons &= ~(1 << (btn-1)); |
|
|
705 |
|
|
706 if (IS_SET(MODE_MOUSE) && !(e->xbutton.state & forcemousemod)) { |
|
|
707 mousereport(e); |
|
|
708 return; |
|
|
709 } |
|
|
710 |
|
|
711 if (mouseaction(e, 1)) |
|
|
712 return; |
|
|
713 if (btn == Button1) |
|
|
714 mousesel(e, 1); |
|
|
715 } |
|
|
716 |
|
|
717 void |
|
|
718 bmotion(XEvent *e) |
|
|
719 { |
|
|
720 if (IS_SET(MODE_MOUSE) && !(e->xbutton.state & forcemousemod)) { |
|
|
721 mousereport(e); |
|
|
722 return; |
|
|
723 } |
|
|
724 |
|
|
725 mousesel(e, 0); |
|
|
726 } |
|
|
727 |
|
|
728 void |
|
|
729 cresize(int width, int height) |
|
|
730 { |
|
|
731 int col, row; |
|
|
732 |
|
|
733 if (width != 0) |
|
|
734 win.w = width; |
|
|
735 if (height != 0) |
|
|
736 win.h = height; |
|
|
737 |
|
|
738 col = (win.w - 2 * borderpx) / win.cw; |
|
|
739 row = (win.h - 2 * borderpx) / win.ch; |
|
|
740 col = MAX(1, col); |
|
|
741 row = MAX(1, row); |
|
|
742 |
|
|
743 tresize(col, row); |
|
|
744 xresize(col, row); |
|
|
745 ttyresize(win.tw, win.th); |
|
|
746 } |
|
|
747 |
|
|
748 void |
|
|
749 xresize(int col, int row) |
|
|
750 { |
|
|
751 win.tw = col * win.cw; |
|
|
752 win.th = row * win.ch; |
|
|
753 |
|
|
754 XFreePixmap(xw.dpy, xw.buf); |
|
|
755 xw.buf = XCreatePixmap(xw.dpy, xw.win, win.w, win.h, |
|
|
756 xw.depth); |
|
|
757 XftDrawChange(xw.draw, xw.buf); |
|
|
758 xclear(0, 0, win.w, win.h); |
|
|
759 |
|
|
760 /* resize to new width */ |
|
|
761 xw.specbuf = xrealloc(xw.specbuf, col * sizeof(GlyphFontSpec)); |
|
|
762 } |
|
|
763 |
|
|
764 ushort |
|
|
765 sixd_to_16bit(int x) |
|
|
766 { |
|
|
767 return x == 0 ? 0 : 0x3737 + 0x2828 * x; |
|
|
768 } |
|
|
769 |
|
|
770 int |
|
|
771 xloadcolor(int i, const char *name, Color *ncolor) |
|
|
772 { |
|
|
773 XRenderColor color = { .alpha = 0xffff }; |
|
|
774 |
|
|
775 if (!name) { |
|
|
776 if (BETWEEN(i, 16, 255)) { /* 256 color */ |
|
|
777 if (i < 6*6*6+16) { /* same colors as xterm */ |
|
|
778 color.red = sixd_to_16bit( ((i-16)/36)%6 ); |
|
|
779 color.green = sixd_to_16bit( ((i-16)/6) %6 ); |
|
|
780 color.blue = sixd_to_16bit( ((i-16)/1) %6 ); |
|
|
781 } else { /* greyscale */ |
|
|
782 color.red = 0x0808 + 0x0a0a * (i - (6*6*6+16)); |
|
|
783 color.green = color.blue = color.red; |
|
|
784 } |
|
|
785 return XftColorAllocValue(xw.dpy, xw.vis, |
|
|
786 xw.cmap, &color, ncolor); |
|
|
787 } else |
|
|
788 name = colorname[i]; |
|
|
789 } |
|
|
790 |
|
|
791 return XftColorAllocName(xw.dpy, xw.vis, xw.cmap, name, ncolor); |
|
|
792 } |
|
|
793 |
|
|
794 void |
|
|
795 xloadcols(void) |
|
|
796 { |
|
|
797 int i; |
|
|
798 static int loaded; |
|
|
799 Color *cp; |
|
|
800 |
|
|
801 if (loaded) { |
|
|
802 for (cp = dc.col; cp < &dc.col[dc.collen]; ++cp) |
|
|
803 XftColorFree(xw.dpy, xw.vis, xw.cmap, cp); |
|
|
804 } else { |
|
|
805 dc.collen = MAX(LEN(colorname), 256); |
|
|
806 dc.col = xmalloc(dc.collen * sizeof(Color)); |
|
|
807 } |
|
|
808 |
|
|
809 for (i = 0; i < dc.collen; i++) |
|
|
810 if (!xloadcolor(i, NULL, &dc.col[i])) { |
|
|
811 if (colorname[i]) |
|
|
812 die("could not allocate color '%s'\n", colorname[i]); |
|
|
813 else |
|
|
814 die("could not allocate color %d\n", i); |
|
|
815 } |
|
|
816 |
|
|
817 dc.col[defaultbg].color.alpha = (unsigned short)(0xffff * alpha); |
|
|
818 dc.col[defaultbg].pixel &= 0x00FFFFFF; |
|
|
819 dc.col[defaultbg].pixel |= (unsigned char)(0xff * alpha) << 24; |
|
|
820 loaded = 1; |
|
|
821 } |
|
|
822 |
|
|
823 int |
|
|
824 xgetcolor(int x, unsigned char *r, unsigned char *g, unsigned char *b) |
|
|
825 { |
|
|
826 if (!BETWEEN(x, 0, dc.collen - 1)) |
|
|
827 return 1; |
|
|
828 |
|
|
829 *r = dc.col[x].color.red >> 8; |
|
|
830 *g = dc.col[x].color.green >> 8; |
|
|
831 *b = dc.col[x].color.blue >> 8; |
|
|
832 |
|
|
833 return 0; |
|
|
834 } |
|
|
835 |
|
|
836 int |
|
|
837 xsetcolorname(int x, const char *name) |
|
|
838 { |
|
|
839 Color ncolor; |
|
|
840 |
|
|
841 if (!BETWEEN(x, 0, dc.collen - 1)) |
|
|
842 return 1; |
|
|
843 |
|
|
844 if (!xloadcolor(x, name, &ncolor)) |
|
|
845 return 1; |
|
|
846 |
|
|
847 XftColorFree(xw.dpy, xw.vis, xw.cmap, &dc.col[x]); |
|
|
848 dc.col[x] = ncolor; |
|
|
849 |
|
|
850 if (x == defaultbg) { |
|
|
851 dc.col[defaultbg].color.alpha = (unsigned short)(0xffff * alpha); |
|
|
852 dc.col[defaultbg].pixel &= 0x00FFFFFF; |
|
|
853 dc.col[defaultbg].pixel |= (unsigned char)(0xff * alpha) << 24; |
|
|
854 } |
|
|
855 |
|
|
856 return 0; |
|
|
857 } |
|
|
858 |
|
|
859 /* |
|
|
860 * Absolute coordinates. |
|
|
861 */ |
|
|
862 void |
|
|
863 xclear(int x1, int y1, int x2, int y2) |
|
|
864 { |
|
|
865 XftDrawRect(xw.draw, |
|
|
866 &dc.col[IS_SET(MODE_REVERSE)? defaultfg : defaultbg], |
|
|
867 x1, y1, x2-x1, y2-y1); |
|
|
868 } |
|
|
869 |
|
|
870 void |
|
|
871 xhints(void) |
|
|
872 { |
|
|
873 XClassHint class = {opt_name ? opt_name : termname, |
|
|
874 opt_class ? opt_class : termname}; |
|
|
875 XWMHints wm = {.flags = InputHint, .input = 1}; |
|
|
876 XSizeHints *sizeh; |
|
|
877 |
|
|
878 sizeh = XAllocSizeHints(); |
|
|
879 |
|
|
880 sizeh->flags = PSize | PResizeInc | PBaseSize | PMinSize; |
|
|
881 sizeh->height = win.h; |
|
|
882 sizeh->width = win.w; |
|
|
883 sizeh->height_inc = win.ch; |
|
|
884 sizeh->width_inc = win.cw; |
|
|
885 sizeh->base_height = 2 * borderpx; |
|
|
886 sizeh->base_width = 2 * borderpx; |
|
|
887 sizeh->min_height = win.ch + 2 * borderpx; |
|
|
888 sizeh->min_width = win.cw + 2 * borderpx; |
|
|
889 if (xw.isfixed) { |
|
|
890 sizeh->flags |= PMaxSize; |
|
|
891 sizeh->min_width = sizeh->max_width = win.w; |
|
|
892 sizeh->min_height = sizeh->max_height = win.h; |
|
|
893 } |
|
|
894 if (xw.gm & (XValue|YValue)) { |
|
|
895 sizeh->flags |= USPosition | PWinGravity; |
|
|
896 sizeh->x = xw.l; |
|
|
897 sizeh->y = xw.t; |
|
|
898 sizeh->win_gravity = xgeommasktogravity(xw.gm); |
|
|
899 } |
|
|
900 |
|
|
901 XSetWMProperties(xw.dpy, xw.win, NULL, NULL, NULL, 0, sizeh, &wm, |
|
|
902 &class); |
|
|
903 XFree(sizeh); |
|
|
904 } |
|
|
905 |
|
|
906 int |
|
|
907 xgeommasktogravity(int mask) |
|
|
908 { |
|
|
909 switch (mask & (XNegative|YNegative)) { |
|
|
910 case 0: |
|
|
911 return NorthWestGravity; |
|
|
912 case XNegative: |
|
|
913 return NorthEastGravity; |
|
|
914 case YNegative: |
|
|
915 return SouthWestGravity; |
|
|
916 } |
|
|
917 |
|
|
918 return SouthEastGravity; |
|
|
919 } |
|
|
920 |
|
|
921 int |
|
|
922 xloadfont(Font *f, FcPattern *pattern) |
|
|
923 { |
|
|
924 FcPattern *configured; |
|
|
925 FcPattern *match; |
|
|
926 FcResult result; |
|
|
927 XGlyphInfo extents; |
|
|
928 int wantattr, haveattr; |
|
|
929 |
|
|
930 /* |
|
|
931 * Manually configure instead of calling XftMatchFont |
|
|
932 * so that we can use the configured pattern for |
|
|
933 * "missing glyph" lookups. |
|
|
934 */ |
|
|
935 configured = FcPatternDuplicate(pattern); |
|
|
936 if (!configured) |
|
|
937 return 1; |
|
|
938 |
|
|
939 FcConfigSubstitute(NULL, configured, FcMatchPattern); |
|
|
940 XftDefaultSubstitute(xw.dpy, xw.scr, configured); |
|
|
941 |
|
|
942 match = FcFontMatch(NULL, configured, &result); |
|
|
943 if (!match) { |
|
|
944 FcPatternDestroy(configured); |
|
|
945 return 1; |
|
|
946 } |
|
|
947 |
|
|
948 if (!(f->match = XftFontOpenPattern(xw.dpy, match))) { |
|
|
949 FcPatternDestroy(configured); |
|
|
950 FcPatternDestroy(match); |
|
|
951 return 1; |
|
|
952 } |
|
|
953 |
|
|
954 if ((XftPatternGetInteger(pattern, "slant", 0, &wantattr) == |
|
|
955 XftResultMatch)) { |
|
|
956 /* |
|
|
957 * Check if xft was unable to find a font with the appropriate |
|
|
958 * slant but gave us one anyway. Try to mitigate. |
|
|
959 */ |
|
|
960 if ((XftPatternGetInteger(f->match->pattern, "slant", 0, |
|
|
961 &haveattr) != XftResultMatch) || haveattr < wantattr) { |
|
|
962 f->badslant = 1; |
|
|
963 fputs("font slant does not match\n", stderr); |
|
|
964 } |
|
|
965 } |
|
|
966 |
|
|
967 if ((XftPatternGetInteger(pattern, "weight", 0, &wantattr) == |
|
|
968 XftResultMatch)) { |
|
|
969 if ((XftPatternGetInteger(f->match->pattern, "weight", 0, |
|
|
970 &haveattr) != XftResultMatch) || haveattr != wantattr) { |
|
|
971 f->badweight = 1; |
|
|
972 fputs("font weight does not match\n", stderr); |
|
|
973 } |
|
|
974 } |
|
|
975 |
|
|
976 XftTextExtentsUtf8(xw.dpy, f->match, |
|
|
977 (const FcChar8 *) ascii_printable, |
|
|
978 strlen(ascii_printable), &extents); |
|
|
979 |
|
|
980 f->set = NULL; |
|
|
981 f->pattern = configured; |
|
|
982 |
|
|
983 f->ascent = f->match->ascent; |
|
|
984 f->descent = f->match->descent; |
|
|
985 f->lbearing = 0; |
|
|
986 f->rbearing = f->match->max_advance_width; |
|
|
987 |
|
|
988 f->height = f->ascent + f->descent; |
|
|
989 f->width = DIVCEIL(extents.xOff, strlen(ascii_printable)); |
|
|
990 |
|
|
991 return 0; |
|
|
992 } |
|
|
993 |
|
|
994 void |
|
|
995 xloadfonts(const char *fontstr, double fontsize) |
|
|
996 { |
|
|
997 FcPattern *pattern; |
|
|
998 double fontval; |
|
|
999 |
|
|
1000 if (fontstr[0] == '-') |
|
|
1001 pattern = XftXlfdParse(fontstr, False, False); |
|
|
1002 else |
|
|
1003 pattern = FcNameParse((const FcChar8 *)fontstr); |
|
|
1004 |
|
|
1005 if (!pattern) |
|
|
1006 die("can't open font %s\n", fontstr); |
|
|
1007 |
|
|
1008 if (fontsize > 1) { |
|
|
1009 FcPatternDel(pattern, FC_PIXEL_SIZE); |
|
|
1010 FcPatternDel(pattern, FC_SIZE); |
|
|
1011 FcPatternAddDouble(pattern, FC_PIXEL_SIZE, (double)fontsize); |
|
|
1012 usedfontsize = fontsize; |
|
|
1013 } else { |
|
|
1014 if (FcPatternGetDouble(pattern, FC_PIXEL_SIZE, 0, &fontval) == |
|
|
1015 FcResultMatch) { |
|
|
1016 usedfontsize = fontval; |
|
|
1017 } else if (FcPatternGetDouble(pattern, FC_SIZE, 0, &fontval) == |
|
|
1018 FcResultMatch) { |
|
|
1019 usedfontsize = -1; |
|
|
1020 } else { |
|
|
1021 /* |
|
|
1022 * Default font size is 12, if none given. This is to |
|
|
1023 * have a known usedfontsize value. |
|
|
1024 */ |
|
|
1025 FcPatternAddDouble(pattern, FC_PIXEL_SIZE, 12); |
|
|
1026 usedfontsize = 12; |
|
|
1027 } |
|
|
1028 defaultfontsize = usedfontsize; |
|
|
1029 } |
|
|
1030 |
|
|
1031 if (xloadfont(&dc.font, pattern)) |
|
|
1032 die("can't open font %s\n", fontstr); |
|
|
1033 |
|
|
1034 if (usedfontsize < 0) { |
|
|
1035 FcPatternGetDouble(dc.font.match->pattern, |
|
|
1036 FC_PIXEL_SIZE, 0, &fontval); |
|
|
1037 usedfontsize = fontval; |
|
|
1038 if (fontsize == 0) |
|
|
1039 defaultfontsize = fontval; |
|
|
1040 } |
|
|
1041 |
|
|
1042 /* Setting character width and height. */ |
|
|
1043 win.cw = ceilf(dc.font.width * cwscale); |
|
|
1044 win.ch = ceilf(dc.font.height * chscale); |
|
|
1045 |
|
|
1046 FcPatternDel(pattern, FC_SLANT); |
|
|
1047 FcPatternAddInteger(pattern, FC_SLANT, FC_SLANT_ITALIC); |
|
|
1048 if (xloadfont(&dc.ifont, pattern)) |
|
|
1049 die("can't open font %s\n", fontstr); |
|
|
1050 |
|
|
1051 FcPatternDel(pattern, FC_WEIGHT); |
|
|
1052 FcPatternAddInteger(pattern, FC_WEIGHT, FC_WEIGHT_BOLD); |
|
|
1053 if (xloadfont(&dc.ibfont, pattern)) |
|
|
1054 die("can't open font %s\n", fontstr); |
|
|
1055 |
|
|
1056 FcPatternDel(pattern, FC_SLANT); |
|
|
1057 FcPatternAddInteger(pattern, FC_SLANT, FC_SLANT_ROMAN); |
|
|
1058 if (xloadfont(&dc.bfont, pattern)) |
|
|
1059 die("can't open font %s\n", fontstr); |
|
|
1060 |
|
|
1061 FcPatternDestroy(pattern); |
|
|
1062 } |
|
|
1063 |
|
|
1064 void |
|
|
1065 xunloadfont(Font *f) |
|
|
1066 { |
|
|
1067 XftFontClose(xw.dpy, f->match); |
|
|
1068 FcPatternDestroy(f->pattern); |
|
|
1069 if (f->set) |
|
|
1070 FcFontSetDestroy(f->set); |
|
|
1071 } |
|
|
1072 |
|
|
1073 void |
|
|
1074 xunloadfonts(void) |
|
|
1075 { |
|
|
1076 /* Free the loaded fonts in the font cache. */ |
|
|
1077 while (frclen > 0) |
|
|
1078 XftFontClose(xw.dpy, frc[--frclen].font); |
|
|
1079 |
|
|
1080 xunloadfont(&dc.font); |
|
|
1081 xunloadfont(&dc.bfont); |
|
|
1082 xunloadfont(&dc.ifont); |
|
|
1083 xunloadfont(&dc.ibfont); |
|
|
1084 } |
|
|
1085 |
|
|
1086 int |
|
|
1087 ximopen(Display *dpy) |
|
|
1088 { |
|
|
1089 XIMCallback imdestroy = { .client_data = NULL, .callback = ximdestroy }; |
|
|
1090 XICCallback icdestroy = { .client_data = NULL, .callback = xicdestroy }; |
|
|
1091 |
|
|
1092 xw.ime.xim = XOpenIM(xw.dpy, NULL, NULL, NULL); |
|
|
1093 if (xw.ime.xim == NULL) |
|
|
1094 return 0; |
|
|
1095 |
|
|
1096 if (XSetIMValues(xw.ime.xim, XNDestroyCallback, &imdestroy, NULL)) |
|
|
1097 fprintf(stderr, "XSetIMValues: " |
|
|
1098 "Could not set XNDestroyCallback.\n"); |
|
|
1099 |
|
|
1100 xw.ime.spotlist = XVaCreateNestedList(0, XNSpotLocation, &xw.ime.spot, |
|
|
1101 NULL); |
|
|
1102 |
|
|
1103 if (xw.ime.xic == NULL) { |
|
|
1104 xw.ime.xic = XCreateIC(xw.ime.xim, XNInputStyle, |
|
|
1105 XIMPreeditNothing | XIMStatusNothing, |
|
|
1106 XNClientWindow, xw.win, |
|
|
1107 XNDestroyCallback, &icdestroy, |
|
|
1108 NULL); |
|
|
1109 } |
|
|
1110 if (xw.ime.xic == NULL) |
|
|
1111 fprintf(stderr, "XCreateIC: Could not create input context.\n"); |
|
|
1112 |
|
|
1113 return 1; |
|
|
1114 } |
|
|
1115 |
|
|
1116 void |
|
|
1117 ximinstantiate(Display *dpy, XPointer client, XPointer call) |
|
|
1118 { |
|
|
1119 if (ximopen(dpy)) |
|
|
1120 XUnregisterIMInstantiateCallback(xw.dpy, NULL, NULL, NULL, |
|
|
1121 ximinstantiate, NULL); |
|
|
1122 } |
|
|
1123 |
|
|
1124 void |
|
|
1125 ximdestroy(XIM xim, XPointer client, XPointer call) |
|
|
1126 { |
|
|
1127 xw.ime.xim = NULL; |
|
|
1128 XRegisterIMInstantiateCallback(xw.dpy, NULL, NULL, NULL, |
|
|
1129 ximinstantiate, NULL); |
|
|
1130 XFree(xw.ime.spotlist); |
|
|
1131 } |
|
|
1132 |
|
|
1133 int |
|
|
1134 xicdestroy(XIC xim, XPointer client, XPointer call) |
|
|
1135 { |
|
|
1136 xw.ime.xic = NULL; |
|
|
1137 return 1; |
|
|
1138 } |
|
|
1139 |
|
|
1140 void |
|
|
1141 xinit(int cols, int rows) |
|
|
1142 { |
|
|
1143 XGCValues gcvalues; |
|
|
1144 Cursor cursor; |
|
|
1145 Window parent, root; |
|
|
1146 pid_t thispid = getpid(); |
|
|
1147 XColor xmousefg, xmousebg; |
|
|
1148 XWindowAttributes attr; |
|
|
1149 XVisualInfo vis; |
|
|
1150 |
|
|
1151 if (!(xw.dpy = XOpenDisplay(NULL))) |
|
|
1152 die("can't open display\n"); |
|
|
1153 xw.scr = XDefaultScreen(xw.dpy); |
|
|
1154 |
|
|
1155 root = XRootWindow(xw.dpy, xw.scr); |
|
|
1156 if (!(opt_embed && (parent = strtol(opt_embed, NULL, 0)))) |
|
|
1157 parent = root; |
|
|
1158 |
|
|
1159 if (XMatchVisualInfo(xw.dpy, xw.scr, 32, TrueColor, &vis) != 0) { |
|
|
1160 xw.vis = vis.visual; |
|
|
1161 xw.depth = vis.depth; |
|
|
1162 } else { |
|
|
1163 XGetWindowAttributes(xw.dpy, parent, &attr); |
|
|
1164 xw.vis = attr.visual; |
|
|
1165 xw.depth = attr.depth; |
|
|
1166 } |
|
|
1167 |
|
|
1168 /* font */ |
|
|
1169 if (!FcInit()) |
|
|
1170 die("could not init fontconfig.\n"); |
|
|
1171 |
|
|
1172 usedfont = (opt_font == NULL)? font : opt_font; |
|
|
1173 xloadfonts(usedfont, 0); |
|
|
1174 |
|
|
1175 /* colors */ |
|
|
1176 xw.cmap = XCreateColormap(xw.dpy, parent, xw.vis, None); |
|
|
1177 xloadcols(); |
|
|
1178 |
|
|
1179 /* adjust fixed window geometry */ |
|
|
1180 win.w = 2 * borderpx + cols * win.cw; |
|
|
1181 win.h = 2 * borderpx + rows * win.ch; |
|
|
1182 if (xw.gm & XNegative) |
|
|
1183 xw.l += DisplayWidth(xw.dpy, xw.scr) - win.w - 2; |
|
|
1184 if (xw.gm & YNegative) |
|
|
1185 xw.t += DisplayHeight(xw.dpy, xw.scr) - win.h - 2; |
|
|
1186 |
|
|
1187 /* Events */ |
|
|
1188 xw.attrs.background_pixel = dc.col[defaultbg].pixel; |
|
|
1189 xw.attrs.border_pixel = dc.col[defaultbg].pixel; |
|
|
1190 xw.attrs.bit_gravity = NorthWestGravity; |
|
|
1191 xw.attrs.event_mask = FocusChangeMask | KeyPressMask | KeyReleaseMask |
|
|
1192 | ExposureMask | VisibilityChangeMask | StructureNotifyMask |
|
|
1193 | ButtonMotionMask | ButtonPressMask | ButtonReleaseMask; |
|
|
1194 xw.attrs.colormap = xw.cmap; |
|
|
1195 |
|
|
1196 xw.win = XCreateWindow(xw.dpy, parent, xw.l, xw.t, |
|
|
1197 win.w, win.h, 0, xw.depth, InputOutput, |
|
|
1198 xw.vis, CWBackPixel | CWBorderPixel | CWBitGravity |
|
|
1199 | CWEventMask | CWColormap, &xw.attrs); |
|
|
1200 if (parent != root) |
|
|
1201 XReparentWindow(xw.dpy, xw.win, parent, xw.l, xw.t); |
|
|
1202 |
|
|
1203 memset(&gcvalues, 0, sizeof(gcvalues)); |
|
|
1204 gcvalues.graphics_exposures = False; |
|
|
1205 dc.gc = XCreateGC(xw.dpy, xw.win, GCGraphicsExposures, |
|
|
1206 &gcvalues); |
|
|
1207 xw.buf = XCreatePixmap(xw.dpy, xw.win, win.w, win.h, |
|
|
1208 xw.depth); |
|
|
1209 XSetForeground(xw.dpy, dc.gc, dc.col[defaultbg].pixel); |
|
|
1210 XFillRectangle(xw.dpy, xw.buf, dc.gc, 0, 0, win.w, win.h); |
|
|
1211 |
|
|
1212 /* font spec buffer */ |
|
|
1213 xw.specbuf = xmalloc(cols * sizeof(GlyphFontSpec)); |
|
|
1214 |
|
|
1215 /* Xft rendering context */ |
|
|
1216 xw.draw = XftDrawCreate(xw.dpy, xw.buf, xw.vis, xw.cmap); |
|
|
1217 |
|
|
1218 /* input methods */ |
|
|
1219 if (!ximopen(xw.dpy)) { |
|
|
1220 XRegisterIMInstantiateCallback(xw.dpy, NULL, NULL, NULL, |
|
|
1221 ximinstantiate, NULL); |
|
|
1222 } |
|
|
1223 |
|
|
1224 /* white cursor, black outline */ |
|
|
1225 cursor = XCreateFontCursor(xw.dpy, mouseshape); |
|
|
1226 XDefineCursor(xw.dpy, xw.win, cursor); |
|
|
1227 |
|
|
1228 if (XParseColor(xw.dpy, xw.cmap, colorname[mousefg], &xmousefg) == 0) { |
|
|
1229 xmousefg.red = 0xffff; |
|
|
1230 xmousefg.green = 0xffff; |
|
|
1231 xmousefg.blue = 0xffff; |
|
|
1232 } |
|
|
1233 |
|
|
1234 if (XParseColor(xw.dpy, xw.cmap, colorname[mousebg], &xmousebg) == 0) { |
|
|
1235 xmousebg.red = 0x0000; |
|
|
1236 xmousebg.green = 0x0000; |
|
|
1237 xmousebg.blue = 0x0000; |
|
|
1238 } |
|
|
1239 |
|
|
1240 XRecolorCursor(xw.dpy, cursor, &xmousefg, &xmousebg); |
|
|
1241 |
|
|
1242 xw.xembed = XInternAtom(xw.dpy, "_XEMBED", False); |
|
|
1243 xw.wmdeletewin = XInternAtom(xw.dpy, "WM_DELETE_WINDOW", False); |
|
|
1244 xw.netwmname = XInternAtom(xw.dpy, "_NET_WM_NAME", False); |
|
|
1245 xw.netwmiconname = XInternAtom(xw.dpy, "_NET_WM_ICON_NAME", False); |
|
|
1246 XSetWMProtocols(xw.dpy, xw.win, &xw.wmdeletewin, 1); |
|
|
1247 |
|
|
1248 xw.netwmpid = XInternAtom(xw.dpy, "_NET_WM_PID", False); |
|
|
1249 XChangeProperty(xw.dpy, xw.win, xw.netwmpid, XA_CARDINAL, 32, |
|
|
1250 PropModeReplace, (uchar *)&thispid, 1); |
|
|
1251 |
|
|
1252 win.mode = MODE_NUMLOCK; |
|
|
1253 resettitle(); |
|
|
1254 xhints(); |
|
|
1255 XMapWindow(xw.dpy, xw.win); |
|
|
1256 XSync(xw.dpy, False); |
|
|
1257 |
|
|
1258 clock_gettime(CLOCK_MONOTONIC, &xsel.tclick1); |
|
|
1259 clock_gettime(CLOCK_MONOTONIC, &xsel.tclick2); |
|
|
1260 xsel.primary = NULL; |
|
|
1261 xsel.clipboard = NULL; |
|
|
1262 xsel.xtarget = XInternAtom(xw.dpy, "UTF8_STRING", 0); |
|
|
1263 if (xsel.xtarget == None) |
|
|
1264 xsel.xtarget = XA_STRING; |
|
|
1265 } |
|
|
1266 |
|
|
1267 int |
|
|
1268 xmakeglyphfontspecs(XftGlyphFontSpec *specs, const Glyph *glyphs, int len, int x, int y) |
|
|
1269 { |
|
|
1270 float winx = borderpx + x * win.cw, winy = borderpx + y * win.ch, xp, yp; |
|
|
1271 ushort mode, prevmode = USHRT_MAX; |
|
|
1272 Font *font = &dc.font; |
|
|
1273 int frcflags = FRC_NORMAL; |
|
|
1274 float runewidth = win.cw; |
|
|
1275 Rune rune; |
|
|
1276 FT_UInt glyphidx; |
|
|
1277 FcResult fcres; |
|
|
1278 FcPattern *fcpattern, *fontpattern; |
|
|
1279 FcFontSet *fcsets[] = { NULL }; |
|
|
1280 FcCharSet *fccharset; |
|
|
1281 int i, f, numspecs = 0; |
|
|
1282 |
|
|
1283 for (i = 0, xp = winx, yp = winy + font->ascent; i < len; ++i) { |
|
|
1284 /* Fetch rune and mode for current glyph. */ |
|
|
1285 rune = glyphs[i].u; |
|
|
1286 mode = glyphs[i].mode; |
|
|
1287 |
|
|
1288 /* Skip dummy wide-character spacing. */ |
|
|
1289 if (mode == ATTR_WDUMMY) |
|
|
1290 continue; |
|
|
1291 |
|
|
1292 /* Determine font for glyph if different from previous glyph. */ |
|
|
1293 if (prevmode != mode) { |
|
|
1294 prevmode = mode; |
|
|
1295 font = &dc.font; |
|
|
1296 frcflags = FRC_NORMAL; |
|
|
1297 runewidth = win.cw * ((mode & ATTR_WIDE) ? 2.0f : 1.0f); |
|
|
1298 if ((mode & ATTR_ITALIC) && (mode & ATTR_BOLD)) { |
|
|
1299 font = &dc.ibfont; |
|
|
1300 frcflags = FRC_ITALICBOLD; |
|
|
1301 } else if (mode & ATTR_ITALIC) { |
|
|
1302 font = &dc.ifont; |
|
|
1303 frcflags = FRC_ITALIC; |
|
|
1304 } else if (mode & ATTR_BOLD) { |
|
|
1305 font = &dc.bfont; |
|
|
1306 frcflags = FRC_BOLD; |
|
|
1307 } |
|
|
1308 yp = winy + font->ascent; |
|
|
1309 } |
|
|
1310 |
|
|
1311 /* Lookup character index with default font. */ |
|
|
1312 glyphidx = XftCharIndex(xw.dpy, font->match, rune); |
|
|
1313 if (glyphidx) { |
|
|
1314 specs[numspecs].font = font->match; |
|
|
1315 specs[numspecs].glyph = glyphidx; |
|
|
1316 specs[numspecs].x = (short)xp; |
|
|
1317 specs[numspecs].y = (short)yp; |
|
|
1318 xp += runewidth; |
|
|
1319 numspecs++; |
|
|
1320 continue; |
|
|
1321 } |
|
|
1322 |
|
|
1323 /* Fallback on font cache, search the font cache for match. */ |
|
|
1324 for (f = 0; f < frclen; f++) { |
|
|
1325 glyphidx = XftCharIndex(xw.dpy, frc[f].font, rune); |
|
|
1326 /* Everything correct. */ |
|
|
1327 if (glyphidx && frc[f].flags == frcflags) |
|
|
1328 break; |
|
|
1329 /* We got a default font for a not found glyph. */ |
|
|
1330 if (!glyphidx && frc[f].flags == frcflags |
|
|
1331 && frc[f].unicodep == rune) { |
|
|
1332 break; |
|
|
1333 } |
|
|
1334 } |
|
|
1335 |
|
|
1336 /* Nothing was found. Use fontconfig to find matching font. */ |
|
|
1337 if (f >= frclen) { |
|
|
1338 if (!font->set) |
|
|
1339 font->set = FcFontSort(0, font->pattern, |
|
|
1340 1, 0, &fcres); |
|
|
1341 fcsets[0] = font->set; |
|
|
1342 |
|
|
1343 /* |
|
|
1344 * Nothing was found in the cache. Now use |
|
|
1345 * some dozen of Fontconfig calls to get the |
|
|
1346 * font for one single character. |
|
|
1347 * |
|
|
1348 * Xft and fontconfig are design failures. |
|
|
1349 */ |
|
|
1350 fcpattern = FcPatternDuplicate(font->pattern); |
|
|
1351 fccharset = FcCharSetCreate(); |
|
|
1352 |
|
|
1353 FcCharSetAddChar(fccharset, rune); |
|
|
1354 FcPatternAddCharSet(fcpattern, FC_CHARSET, |
|
|
1355 fccharset); |
|
|
1356 FcPatternAddBool(fcpattern, FC_SCALABLE, 1); |
|
|
1357 |
|
|
1358 FcConfigSubstitute(0, fcpattern, |
|
|
1359 FcMatchPattern); |
|
|
1360 FcDefaultSubstitute(fcpattern); |
|
|
1361 |
|
|
1362 fontpattern = FcFontSetMatch(0, fcsets, 1, |
|
|
1363 fcpattern, &fcres); |
|
|
1364 |
|
|
1365 /* Allocate memory for the new cache entry. */ |
|
|
1366 if (frclen >= frccap) { |
|
|
1367 frccap += 16; |
|
|
1368 frc = xrealloc(frc, frccap * sizeof(Fontcache)); |
|
|
1369 } |
|
|
1370 |
|
|
1371 frc[frclen].font = XftFontOpenPattern(xw.dpy, |
|
|
1372 fontpattern); |
|
|
1373 if (!frc[frclen].font) |
|
|
1374 die("XftFontOpenPattern failed seeking fallback font: %s\n", |
|
|
1375 strerror(errno)); |
|
|
1376 frc[frclen].flags = frcflags; |
|
|
1377 frc[frclen].unicodep = rune; |
|
|
1378 |
|
|
1379 glyphidx = XftCharIndex(xw.dpy, frc[frclen].font, rune); |
|
|
1380 |
|
|
1381 f = frclen; |
|
|
1382 frclen++; |
|
|
1383 |
|
|
1384 FcPatternDestroy(fcpattern); |
|
|
1385 FcCharSetDestroy(fccharset); |
|
|
1386 } |
|
|
1387 |
|
|
1388 specs[numspecs].font = frc[f].font; |
|
|
1389 specs[numspecs].glyph = glyphidx; |
|
|
1390 specs[numspecs].x = (short)xp; |
|
|
1391 specs[numspecs].y = (short)yp; |
|
|
1392 xp += runewidth; |
|
|
1393 numspecs++; |
|
|
1394 } |
|
|
1395 |
|
|
1396 return numspecs; |
|
|
1397 } |
|
|
1398 |
|
|
1399 void |
|
|
1400 xdrawglyphfontspecs(const XftGlyphFontSpec *specs, Glyph base, int len, int x, int y) |
|
|
1401 { |
|
|
1402 int charlen = len * ((base.mode & ATTR_WIDE) ? 2 : 1); |
|
|
1403 int winx = borderpx + x * win.cw, winy = borderpx + y * win.ch, |
|
|
1404 width = charlen * win.cw; |
|
|
1405 Color *fg, *bg, *temp, revfg, revbg, truefg, truebg; |
|
|
1406 XRenderColor colfg, colbg; |
|
|
1407 XRectangle r; |
|
|
1408 |
|
|
1409 /* Fallback on color display for attributes not supported by the font */ |
|
|
1410 if (base.mode & ATTR_ITALIC && base.mode & ATTR_BOLD) { |
|
|
1411 if (dc.ibfont.badslant || dc.ibfont.badweight) |
|
|
1412 base.fg = defaultattr; |
|
|
1413 } else if ((base.mode & ATTR_ITALIC && dc.ifont.badslant) || |
|
|
1414 (base.mode & ATTR_BOLD && dc.bfont.badweight)) { |
|
|
1415 base.fg = defaultattr; |
|
|
1416 } |
|
|
1417 |
|
|
1418 if (IS_TRUECOL(base.fg)) { |
|
|
1419 colfg.alpha = 0xffff; |
|
|
1420 colfg.red = TRUERED(base.fg); |
|
|
1421 colfg.green = TRUEGREEN(base.fg); |
|
|
1422 colfg.blue = TRUEBLUE(base.fg); |
|
|
1423 XftColorAllocValue(xw.dpy, xw.vis, xw.cmap, &colfg, &truefg); |
|
|
1424 fg = &truefg; |
|
|
1425 } else { |
|
|
1426 fg = &dc.col[base.fg]; |
|
|
1427 } |
|
|
1428 |
|
|
1429 if (IS_TRUECOL(base.bg)) { |
|
|
1430 colbg.alpha = 0xffff; |
|
|
1431 colbg.green = TRUEGREEN(base.bg); |
|
|
1432 colbg.red = TRUERED(base.bg); |
|
|
1433 colbg.blue = TRUEBLUE(base.bg); |
|
|
1434 XftColorAllocValue(xw.dpy, xw.vis, xw.cmap, &colbg, &truebg); |
|
|
1435 bg = &truebg; |
|
|
1436 } else { |
|
|
1437 bg = &dc.col[base.bg]; |
|
|
1438 } |
|
|
1439 |
|
|
1440 /* Change basic system colors [0-7] to bright system colors [8-15] */ |
|
|
1441 if ((base.mode & ATTR_BOLD_FAINT) == ATTR_BOLD && BETWEEN(base.fg, 0, 7)) |
|
|
1442 fg = &dc.col[base.fg + 8]; |
|
|
1443 |
|
|
1444 if (IS_SET(MODE_REVERSE)) { |
|
|
1445 if (fg == &dc.col[defaultfg]) { |
|
|
1446 fg = &dc.col[defaultbg]; |
|
|
1447 } else { |
|
|
1448 colfg.red = ~fg->color.red; |
|
|
1449 colfg.green = ~fg->color.green; |
|
|
1450 colfg.blue = ~fg->color.blue; |
|
|
1451 colfg.alpha = fg->color.alpha; |
|
|
1452 XftColorAllocValue(xw.dpy, xw.vis, xw.cmap, &colfg, |
|
|
1453 &revfg); |
|
|
1454 fg = &revfg; |
|
|
1455 } |
|
|
1456 |
|
|
1457 if (bg == &dc.col[defaultbg]) { |
|
|
1458 bg = &dc.col[defaultfg]; |
|
|
1459 } else { |
|
|
1460 colbg.red = ~bg->color.red; |
|
|
1461 colbg.green = ~bg->color.green; |
|
|
1462 colbg.blue = ~bg->color.blue; |
|
|
1463 colbg.alpha = bg->color.alpha; |
|
|
1464 XftColorAllocValue(xw.dpy, xw.vis, xw.cmap, &colbg, |
|
|
1465 &revbg); |
|
|
1466 bg = &revbg; |
|
|
1467 } |
|
|
1468 } |
|
|
1469 |
|
|
1470 if ((base.mode & ATTR_BOLD_FAINT) == ATTR_FAINT) { |
|
|
1471 colfg.red = fg->color.red / 2; |
|
|
1472 colfg.green = fg->color.green / 2; |
|
|
1473 colfg.blue = fg->color.blue / 2; |
|
|
1474 colfg.alpha = fg->color.alpha; |
|
|
1475 XftColorAllocValue(xw.dpy, xw.vis, xw.cmap, &colfg, &revfg); |
|
|
1476 fg = &revfg; |
|
|
1477 } |
|
|
1478 |
|
|
1479 if (base.mode & ATTR_REVERSE) { |
|
|
1480 temp = fg; |
|
|
1481 fg = bg; |
|
|
1482 bg = temp; |
|
|
1483 } |
|
|
1484 |
|
|
1485 if (base.mode & ATTR_BLINK && win.mode & MODE_BLINK) |
|
|
1486 fg = bg; |
|
|
1487 |
|
|
1488 if (base.mode & ATTR_INVISIBLE) |
|
|
1489 fg = bg; |
|
|
1490 |
|
|
1491 /* Intelligent cleaning up of the borders. */ |
|
|
1492 if (x == 0) { |
|
|
1493 xclear(0, (y == 0)? 0 : winy, borderpx, |
|
|
1494 winy + win.ch + |
|
|
1495 ((winy + win.ch >= borderpx + win.th)? win.h : 0)); |
|
|
1496 } |
|
|
1497 if (winx + width >= borderpx + win.tw) { |
|
|
1498 xclear(winx + width, (y == 0)? 0 : winy, win.w, |
|
|
1499 ((winy + win.ch >= borderpx + win.th)? win.h : (winy + win.ch))); |
|
|
1500 } |
|
|
1501 if (y == 0) |
|
|
1502 xclear(winx, 0, winx + width, borderpx); |
|
|
1503 if (winy + win.ch >= borderpx + win.th) |
|
|
1504 xclear(winx, winy + win.ch, winx + width, win.h); |
|
|
1505 |
|
|
1506 /* Clean up the region we want to draw to. */ |
|
|
1507 XftDrawRect(xw.draw, bg, winx, winy, width, win.ch); |
|
|
1508 |
|
|
1509 /* Set the clip region because Xft is sometimes dirty. */ |
|
|
1510 r.x = 0; |
|
|
1511 r.y = 0; |
|
|
1512 r.height = win.ch; |
|
|
1513 r.width = width; |
|
|
1514 XftDrawSetClipRectangles(xw.draw, winx, winy, &r, 1); |
|
|
1515 |
|
|
1516 /* Render the glyphs. */ |
|
|
1517 XftDrawGlyphFontSpec(xw.draw, fg, specs, len); |
|
|
1518 |
|
|
1519 /* Render underline and strikethrough. */ |
|
|
1520 if (base.mode & ATTR_UNDERLINE) { |
|
|
1521 XftDrawRect(xw.draw, fg, winx, winy + dc.font.ascent * chscale + 1, |
|
|
1522 width, 1); |
|
|
1523 } |
|
|
1524 |
|
|
1525 if (base.mode & ATTR_STRUCK) { |
|
|
1526 XftDrawRect(xw.draw, fg, winx, winy + 2 * dc.font.ascent * chscale / 3, |
|
|
1527 width, 1); |
|
|
1528 } |
|
|
1529 |
|
|
1530 /* Reset clip to none. */ |
|
|
1531 XftDrawSetClip(xw.draw, 0); |
|
|
1532 } |
|
|
1533 |
|
|
1534 void |
|
|
1535 xdrawglyph(Glyph g, int x, int y) |
|
|
1536 { |
|
|
1537 int numspecs; |
|
|
1538 XftGlyphFontSpec spec; |
|
|
1539 |
|
|
1540 numspecs = xmakeglyphfontspecs(&spec, &g, 1, x, y); |
|
|
1541 xdrawglyphfontspecs(&spec, g, numspecs, x, y); |
|
|
1542 } |
|
|
1543 |
|
|
1544 void |
|
|
1545 xdrawcursor(int cx, int cy, Glyph g, int ox, int oy, Glyph og) |
|
|
1546 { |
|
|
1547 Color drawcol; |
|
|
1548 |
|
|
1549 /* remove the old cursor */ |
|
|
1550 if (selected(ox, oy)) |
|
|
1551 og.mode ^= ATTR_REVERSE; |
|
|
1552 xdrawglyph(og, ox, oy); |
|
|
1553 |
|
|
1554 if (IS_SET(MODE_HIDE)) |
|
|
1555 return; |
|
|
1556 |
|
|
1557 /* |
|
|
1558 * Select the right color for the right mode. |
|
|
1559 */ |
|
|
1560 g.mode &= ATTR_BOLD|ATTR_ITALIC|ATTR_UNDERLINE|ATTR_STRUCK|ATTR_WIDE; |
|
|
1561 |
|
|
1562 if (IS_SET(MODE_REVERSE)) { |
|
|
1563 g.mode |= ATTR_REVERSE; |
|
|
1564 g.bg = defaultfg; |
|
|
1565 if (selected(cx, cy)) { |
|
|
1566 drawcol = dc.col[defaultcs]; |
|
|
1567 g.fg = defaultrcs; |
|
|
1568 } else { |
|
|
1569 drawcol = dc.col[defaultrcs]; |
|
|
1570 g.fg = defaultcs; |
|
|
1571 } |
|
|
1572 } else { |
|
|
1573 if (selected(cx, cy)) { |
|
|
1574 g.fg = defaultfg; |
|
|
1575 g.bg = defaultrcs; |
|
|
1576 } else { |
|
|
1577 g.fg = defaultbg; |
|
|
1578 g.bg = defaultcs; |
|
|
1579 } |
|
|
1580 drawcol = dc.col[g.bg]; |
|
|
1581 } |
|
|
1582 |
|
|
1583 /* draw the new one */ |
|
|
1584 if (IS_SET(MODE_FOCUSED)) { |
|
|
1585 switch (win.cursor) { |
|
|
1586 case 7: /* st extension */ |
|
|
1587 g.u = 0x2603; /* snowman (U+2603) */ |
|
|
1588 /* FALLTHROUGH */ |
|
|
1589 case 0: /* Blinking Block */ |
|
|
1590 case 1: /* Blinking Block (Default) */ |
|
|
1591 case 2: /* Steady Block */ |
|
|
1592 xdrawglyph(g, cx, cy); |
|
|
1593 break; |
|
|
1594 case 3: /* Blinking Underline */ |
|
|
1595 case 4: /* Steady Underline */ |
|
|
1596 XftDrawRect(xw.draw, &drawcol, |
|
|
1597 borderpx + cx * win.cw, |
|
|
1598 borderpx + (cy + 1) * win.ch - \ |
|
|
1599 cursorthickness, |
|
|
1600 win.cw, cursorthickness); |
|
|
1601 break; |
|
|
1602 case 5: /* Blinking bar */ |
|
|
1603 case 6: /* Steady bar */ |
|
|
1604 XftDrawRect(xw.draw, &drawcol, |
|
|
1605 borderpx + cx * win.cw, |
|
|
1606 borderpx + cy * win.ch, |
|
|
1607 cursorthickness, win.ch); |
|
|
1608 break; |
|
|
1609 } |
|
|
1610 } else { |
|
|
1611 XftDrawRect(xw.draw, &drawcol, |
|
|
1612 borderpx + cx * win.cw, |
|
|
1613 borderpx + cy * win.ch, |
|
|
1614 win.cw - 1, 1); |
|
|
1615 XftDrawRect(xw.draw, &drawcol, |
|
|
1616 borderpx + cx * win.cw, |
|
|
1617 borderpx + cy * win.ch, |
|
|
1618 1, win.ch - 1); |
|
|
1619 XftDrawRect(xw.draw, &drawcol, |
|
|
1620 borderpx + (cx + 1) * win.cw - 1, |
|
|
1621 borderpx + cy * win.ch, |
|
|
1622 1, win.ch - 1); |
|
|
1623 XftDrawRect(xw.draw, &drawcol, |
|
|
1624 borderpx + cx * win.cw, |
|
|
1625 borderpx + (cy + 1) * win.ch - 1, |
|
|
1626 win.cw, 1); |
|
|
1627 } |
|
|
1628 } |
|
|
1629 |
|
|
1630 void |
|
|
1631 xsetenv(void) |
|
|
1632 { |
|
|
1633 char buf[sizeof(long) * 8 + 1]; |
|
|
1634 |
|
|
1635 snprintf(buf, sizeof(buf), "%lu", xw.win); |
|
|
1636 setenv("WINDOWID", buf, 1); |
|
|
1637 } |
|
|
1638 |
|
|
1639 void |
|
|
1640 xseticontitle(char *p) |
|
|
1641 { |
|
|
1642 XTextProperty prop; |
|
|
1643 DEFAULT(p, opt_title); |
|
|
1644 |
|
|
1645 if (p[0] == '\0') |
|
|
1646 p = opt_title; |
|
|
1647 |
|
|
1648 if (Xutf8TextListToTextProperty(xw.dpy, &p, 1, XUTF8StringStyle, |
|
|
1649 &prop) != Success) |
|
|
1650 return; |
|
|
1651 XSetWMIconName(xw.dpy, xw.win, &prop); |
|
|
1652 XSetTextProperty(xw.dpy, xw.win, &prop, xw.netwmiconname); |
|
|
1653 XFree(prop.value); |
|
|
1654 } |
|
|
1655 |
|
|
1656 void |
|
|
1657 xsettitle(char *p) |
|
|
1658 { |
|
|
1659 XTextProperty prop; |
|
|
1660 DEFAULT(p, opt_title); |
|
|
1661 |
|
|
1662 if (p[0] == '\0') |
|
|
1663 p = opt_title; |
|
|
1664 |
|
|
1665 if (Xutf8TextListToTextProperty(xw.dpy, &p, 1, XUTF8StringStyle, |
|
|
1666 &prop) != Success) |
|
|
1667 return; |
|
|
1668 XSetWMName(xw.dpy, xw.win, &prop); |
|
|
1669 XSetTextProperty(xw.dpy, xw.win, &prop, xw.netwmname); |
|
|
1670 XFree(prop.value); |
|
|
1671 } |
|
|
1672 |
|
|
1673 int |
|
|
1674 xstartdraw(void) |
|
|
1675 { |
|
|
1676 return IS_SET(MODE_VISIBLE); |
|
|
1677 } |
|
|
1678 |
|
|
1679 void |
|
|
1680 xdrawline(Line line, int x1, int y1, int x2) |
|
|
1681 { |
|
|
1682 int i, x, ox, numspecs; |
|
|
1683 Glyph base, new; |
|
|
1684 XftGlyphFontSpec *specs = xw.specbuf; |
|
|
1685 |
|
|
1686 numspecs = xmakeglyphfontspecs(specs, &line[x1], x2 - x1, x1, y1); |
|
|
1687 i = ox = 0; |
|
|
1688 for (x = x1; x < x2 && i < numspecs; x++) { |
|
|
1689 new = line[x]; |
|
|
1690 if (new.mode == ATTR_WDUMMY) |
|
|
1691 continue; |
|
|
1692 if (selected(x, y1)) |
|
|
1693 new.mode ^= ATTR_REVERSE; |
|
|
1694 if (i > 0 && ATTRCMP(base, new)) { |
|
|
1695 xdrawglyphfontspecs(specs, base, i, ox, y1); |
|
|
1696 specs += i; |
|
|
1697 numspecs -= i; |
|
|
1698 i = 0; |
|
|
1699 } |
|
|
1700 if (i == 0) { |
|
|
1701 ox = x; |
|
|
1702 base = new; |
|
|
1703 } |
|
|
1704 i++; |
|
|
1705 } |
|
|
1706 if (i > 0) |
|
|
1707 xdrawglyphfontspecs(specs, base, i, ox, y1); |
|
|
1708 } |
|
|
1709 |
|
|
1710 void |
|
|
1711 xfinishdraw(void) |
|
|
1712 { |
|
|
1713 XCopyArea(xw.dpy, xw.buf, xw.win, dc.gc, 0, 0, win.w, |
|
|
1714 win.h, 0, 0); |
|
|
1715 XSetForeground(xw.dpy, dc.gc, |
|
|
1716 dc.col[IS_SET(MODE_REVERSE)? |
|
|
1717 defaultfg : defaultbg].pixel); |
|
|
1718 } |
|
|
1719 |
|
|
1720 void |
|
|
1721 xximspot(int x, int y) |
|
|
1722 { |
|
|
1723 if (xw.ime.xic == NULL) |
|
|
1724 return; |
|
|
1725 |
|
|
1726 xw.ime.spot.x = borderpx + x * win.cw; |
|
|
1727 xw.ime.spot.y = borderpx + (y + 1) * win.ch; |
|
|
1728 |
|
|
1729 XSetICValues(xw.ime.xic, XNPreeditAttributes, xw.ime.spotlist, NULL); |
|
|
1730 } |
|
|
1731 |
|
|
1732 void |
|
|
1733 expose(XEvent *ev) |
|
|
1734 { |
|
|
1735 redraw(); |
|
|
1736 } |
|
|
1737 |
|
|
1738 void |
|
|
1739 visibility(XEvent *ev) |
|
|
1740 { |
|
|
1741 XVisibilityEvent *e = &ev->xvisibility; |
|
|
1742 |
|
|
1743 MODBIT(win.mode, e->state != VisibilityFullyObscured, MODE_VISIBLE); |
|
|
1744 } |
|
|
1745 |
|
|
1746 void |
|
|
1747 unmap(XEvent *ev) |
|
|
1748 { |
|
|
1749 win.mode &= ~MODE_VISIBLE; |
|
|
1750 } |
|
|
1751 |
|
|
1752 void |
|
|
1753 xsetpointermotion(int set) |
|
|
1754 { |
|
|
1755 MODBIT(xw.attrs.event_mask, set, PointerMotionMask); |
|
|
1756 XChangeWindowAttributes(xw.dpy, xw.win, CWEventMask, &xw.attrs); |
|
|
1757 } |
|
|
1758 |
|
|
1759 void |
|
|
1760 xsetmode(int set, unsigned int flags) |
|
|
1761 { |
|
|
1762 int mode = win.mode; |
|
|
1763 MODBIT(win.mode, set, flags); |
|
|
1764 if ((win.mode & MODE_REVERSE) != (mode & MODE_REVERSE)) |
|
|
1765 redraw(); |
|
|
1766 } |
|
|
1767 |
|
|
1768 int |
|
|
1769 xsetcursor(int cursor) |
|
|
1770 { |
|
|
1771 if (!BETWEEN(cursor, 0, 7)) /* 7: st extension */ |
|
|
1772 return 1; |
|
|
1773 win.cursor = cursor; |
|
|
1774 return 0; |
|
|
1775 } |
|
|
1776 |
|
|
1777 void |
|
|
1778 xseturgency(int add) |
|
|
1779 { |
|
|
1780 XWMHints *h = XGetWMHints(xw.dpy, xw.win); |
|
|
1781 |
|
|
1782 MODBIT(h->flags, add, XUrgencyHint); |
|
|
1783 XSetWMHints(xw.dpy, xw.win, h); |
|
|
1784 XFree(h); |
|
|
1785 } |
|
|
1786 |
|
|
1787 void |
|
|
1788 xbell(void) |
|
|
1789 { |
|
|
1790 if (!(IS_SET(MODE_FOCUSED))) |
|
|
1791 xseturgency(1); |
|
|
1792 if (bellvolume) |
|
|
1793 XkbBell(xw.dpy, xw.win, bellvolume, (Atom)NULL); |
|
|
1794 } |
|
|
1795 |
|
|
1796 void |
|
|
1797 focus(XEvent *ev) |
|
|
1798 { |
|
|
1799 XFocusChangeEvent *e = &ev->xfocus; |
|
|
1800 |
|
|
1801 if (e->mode == NotifyGrab) |
|
|
1802 return; |
|
|
1803 |
|
|
1804 if (ev->type == FocusIn) { |
|
|
1805 if (xw.ime.xic) |
|
|
1806 XSetICFocus(xw.ime.xic); |
|
|
1807 win.mode |= MODE_FOCUSED; |
|
|
1808 xseturgency(0); |
|
|
1809 if (IS_SET(MODE_FOCUS)) |
|
|
1810 ttywrite("\033[I", 3, 0); |
|
|
1811 } else { |
|
|
1812 if (xw.ime.xic) |
|
|
1813 XUnsetICFocus(xw.ime.xic); |
|
|
1814 win.mode &= ~MODE_FOCUSED; |
|
|
1815 if (IS_SET(MODE_FOCUS)) |
|
|
1816 ttywrite("\033[O", 3, 0); |
|
|
1817 } |
|
|
1818 } |
|
|
1819 |
|
|
1820 int |
|
|
1821 match(uint mask, uint state) |
|
|
1822 { |
|
|
1823 return mask == XK_ANY_MOD || mask == (state & ~ignoremod); |
|
|
1824 } |
|
|
1825 |
|
|
1826 char* |
|
|
1827 kmap(KeySym k, uint state) |
|
|
1828 { |
|
|
1829 Key *kp; |
|
|
1830 int i; |
|
|
1831 |
|
|
1832 /* Check for mapped keys out of X11 function keys. */ |
|
|
1833 for (i = 0; i < LEN(mappedkeys); i++) { |
|
|
1834 if (mappedkeys[i] == k) |
|
|
1835 break; |
|
|
1836 } |
|
|
1837 if (i == LEN(mappedkeys)) { |
|
|
1838 if ((k & 0xFFFF) < 0xFD00) |
|
|
1839 return NULL; |
|
|
1840 } |
|
|
1841 |
|
|
1842 for (kp = key; kp < key + LEN(key); kp++) { |
|
|
1843 if (kp->k != k) |
|
|
1844 continue; |
|
|
1845 |
|
|
1846 if (!match(kp->mask, state)) |
|
|
1847 continue; |
|
|
1848 |
|
|
1849 if (IS_SET(MODE_APPKEYPAD) ? kp->appkey < 0 : kp->appkey > 0) |
|
|
1850 continue; |
|
|
1851 if (IS_SET(MODE_NUMLOCK) && kp->appkey == 2) |
|
|
1852 continue; |
|
|
1853 |
|
|
1854 if (IS_SET(MODE_APPCURSOR) ? kp->appcursor < 0 : kp->appcursor > 0) |
|
|
1855 continue; |
|
|
1856 |
|
|
1857 return kp->s; |
|
|
1858 } |
|
|
1859 |
|
|
1860 return NULL; |
|
|
1861 } |
|
|
1862 |
|
|
1863 void |
|
|
1864 kpress(XEvent *ev) |
|
|
1865 { |
|
|
1866 XKeyEvent *e = &ev->xkey; |
|
|
1867 KeySym ksym = NoSymbol; |
|
|
1868 char buf[64], *customkey; |
|
|
1869 int len; |
|
|
1870 Rune c; |
|
|
1871 Status status; |
|
|
1872 Shortcut *bp; |
|
|
1873 |
|
|
1874 if (IS_SET(MODE_KBDLOCK)) |
|
|
1875 return; |
|
|
1876 |
|
|
1877 if (xw.ime.xic) { |
|
|
1878 len = XmbLookupString(xw.ime.xic, e, buf, sizeof buf, &ksym, &status); |
|
|
1879 if (status == XBufferOverflow) |
|
|
1880 return; |
|
|
1881 } else { |
|
|
1882 len = XLookupString(e, buf, sizeof buf, &ksym, NULL); |
|
|
1883 } |
|
|
1884 /* 1. shortcuts */ |
|
|
1885 for (bp = shortcuts; bp < shortcuts + LEN(shortcuts); bp++) { |
|
|
1886 if (ksym == bp->keysym && match(bp->mod, e->state)) { |
|
|
1887 bp->func(&(bp->arg)); |
|
|
1888 return; |
|
|
1889 } |
|
|
1890 } |
|
|
1891 |
|
|
1892 /* 2. custom keys from config.h */ |
|
|
1893 if ((customkey = kmap(ksym, e->state))) { |
|
|
1894 ttywrite(customkey, strlen(customkey), 1); |
|
|
1895 return; |
|
|
1896 } |
|
|
1897 |
|
|
1898 /* 3. composed string from input method */ |
|
|
1899 if (len == 0) |
|
|
1900 return; |
|
|
1901 if (len == 1 && e->state & Mod1Mask) { |
|
|
1902 if (IS_SET(MODE_8BIT)) { |
|
|
1903 if (*buf < 0177) { |
|
|
1904 c = *buf | 0x80; |
|
|
1905 len = utf8encode(c, buf); |
|
|
1906 } |
|
|
1907 } else { |
|
|
1908 buf[1] = buf[0]; |
|
|
1909 buf[0] = '\033'; |
|
|
1910 len = 2; |
|
|
1911 } |
|
|
1912 } |
|
|
1913 ttywrite(buf, len, 1); |
|
|
1914 } |
|
|
1915 |
|
|
1916 void |
|
|
1917 cmessage(XEvent *e) |
|
|
1918 { |
|
|
1919 /* |
|
|
1920 * See xembed specs |
|
|
1921 * http://standards.freedesktop.org/xembed-spec/xembed-spec-latest.html |
|
|
1922 */ |
|
|
1923 if (e->xclient.message_type == xw.xembed && e->xclient.format == 32) { |
|
|
1924 if (e->xclient.data.l[1] == XEMBED_FOCUS_IN) { |
|
|
1925 win.mode |= MODE_FOCUSED; |
|
|
1926 xseturgency(0); |
|
|
1927 } else if (e->xclient.data.l[1] == XEMBED_FOCUS_OUT) { |
|
|
1928 win.mode &= ~MODE_FOCUSED; |
|
|
1929 } |
|
|
1930 } else if (e->xclient.data.l[0] == xw.wmdeletewin) { |
|
|
1931 ttyhangup(); |
|
|
1932 exit(0); |
|
|
1933 } |
|
|
1934 } |
|
|
1935 |
|
|
1936 void |
|
|
1937 resize(XEvent *e) |
|
|
1938 { |
|
|
1939 if (e->xconfigure.width == win.w && e->xconfigure.height == win.h) |
|
|
1940 return; |
|
|
1941 |
|
|
1942 cresize(e->xconfigure.width, e->xconfigure.height); |
|
|
1943 } |
|
|
1944 |
|
|
1945 void |
|
|
1946 run(void) |
|
|
1947 { |
|
|
1948 XEvent ev; |
|
|
1949 int w = win.w, h = win.h; |
|
|
1950 fd_set rfd; |
|
|
1951 int xfd = XConnectionNumber(xw.dpy), ttyfd, xev, drawing; |
|
|
1952 struct timespec seltv, *tv, now, lastblink, trigger; |
|
|
1953 double timeout; |
|
|
1954 |
|
|
1955 /* Waiting for window mapping */ |
|
|
1956 do { |
|
|
1957 XNextEvent(xw.dpy, &ev); |
|
|
1958 /* |
|
|
1959 * This XFilterEvent call is required because of XOpenIM. It |
|
|
1960 * does filter out the key event and some client message for |
|
|
1961 * the input method too. |
|
|
1962 */ |
|
|
1963 if (XFilterEvent(&ev, None)) |
|
|
1964 continue; |
|
|
1965 if (ev.type == ConfigureNotify) { |
|
|
1966 w = ev.xconfigure.width; |
|
|
1967 h = ev.xconfigure.height; |
|
|
1968 } |
|
|
1969 } while (ev.type != MapNotify); |
|
|
1970 |
|
|
1971 ttyfd = ttynew(opt_line, shell, opt_io, opt_cmd); |
|
|
1972 cresize(w, h); |
|
|
1973 |
|
|
1974 for (timeout = -1, drawing = 0, lastblink = (struct timespec){0};;) { |
|
|
1975 FD_ZERO(&rfd); |
|
|
1976 FD_SET(ttyfd, &rfd); |
|
|
1977 FD_SET(xfd, &rfd); |
|
|
1978 |
|
|
1979 if (XPending(xw.dpy)) |
|
|
1980 timeout = 0; /* existing events might not set xfd */ |
|
|
1981 |
|
|
1982 seltv.tv_sec = timeout / 1E3; |
|
|
1983 seltv.tv_nsec = 1E6 * (timeout - 1E3 * seltv.tv_sec); |
|
|
1984 tv = timeout >= 0 ? &seltv : NULL; |
|
|
1985 |
|
|
1986 if (pselect(MAX(xfd, ttyfd)+1, &rfd, NULL, NULL, tv, NULL) < 0) { |
|
|
1987 if (errno == EINTR) |
|
|
1988 continue; |
|
|
1989 die("select failed: %s\n", strerror(errno)); |
|
|
1990 } |
|
|
1991 clock_gettime(CLOCK_MONOTONIC, &now); |
|
|
1992 |
|
|
1993 if (FD_ISSET(ttyfd, &rfd)) |
|
|
1994 ttyread(); |
|
|
1995 |
|
|
1996 xev = 0; |
|
|
1997 while (XPending(xw.dpy)) { |
|
|
1998 xev = 1; |
|
|
1999 XNextEvent(xw.dpy, &ev); |
|
|
2000 if (XFilterEvent(&ev, None)) |
|
|
2001 continue; |
|
|
2002 if (handler[ev.type]) |
|
|
2003 (handler[ev.type])(&ev); |
|
|
2004 } |
|
|
2005 |
|
|
2006 /* |
|
|
2007 * To reduce flicker and tearing, when new content or event |
|
|
2008 * triggers drawing, we first wait a bit to ensure we got |
|
|
2009 * everything, and if nothing new arrives - we draw. |
|
|
2010 * We start with trying to wait minlatency ms. If more content |
|
|
2011 * arrives sooner, we retry with shorter and shorter periods, |
|
|
2012 * and eventually draw even without idle after maxlatency ms. |
|
|
2013 * Typically this results in low latency while interacting, |
|
|
2014 * maximum latency intervals during `cat huge.txt`, and perfect |
|
|
2015 * sync with periodic updates from animations/key-repeats/etc. |
|
|
2016 */ |
|
|
2017 if (FD_ISSET(ttyfd, &rfd) || xev) { |
|
|
2018 if (!drawing) { |
|
|
2019 trigger = now; |
|
|
2020 drawing = 1; |
|
|
2021 } |
|
|
2022 timeout = (maxlatency - TIMEDIFF(now, trigger)) \ |
|
|
2023 / maxlatency * minlatency; |
|
|
2024 if (timeout > 0) |
|
|
2025 continue; /* we have time, try to find idle */ |
|
|
2026 } |
|
|
2027 |
|
|
2028 /* idle detected or maxlatency exhausted -> draw */ |
|
|
2029 timeout = -1; |
|
|
2030 if (blinktimeout && tattrset(ATTR_BLINK)) { |
|
|
2031 timeout = blinktimeout - TIMEDIFF(now, lastblink); |
|
|
2032 if (timeout <= 0) { |
|
|
2033 if (-timeout > blinktimeout) /* start visible */ |
|
|
2034 win.mode |= MODE_BLINK; |
|
|
2035 win.mode ^= MODE_BLINK; |
|
|
2036 tsetdirtattr(ATTR_BLINK); |
|
|
2037 lastblink = now; |
|
|
2038 timeout = blinktimeout; |
|
|
2039 } |
|
|
2040 } |
|
|
2041 |
|
|
2042 draw(); |
|
|
2043 XFlush(xw.dpy); |
|
|
2044 drawing = 0; |
|
|
2045 } |
|
|
2046 } |
|
|
2047 |
|
|
2048 void |
|
|
2049 usage(void) |
|
|
2050 { |
|
|
2051 die("usage: %s [-aiv] [-c class] [-f font] [-g geometry]" |
|
|
2052 " [-n name] [-o file]\n" |
|
|
2053 " [-T title] [-t title] [-w windowid]" |
|
|
2054 " [[-e] command [args ...]]\n" |
|
|
2055 " %s [-aiv] [-c class] [-f font] [-g geometry]" |
|
|
2056 " [-n name] [-o file]\n" |
|
|
2057 " [-T title] [-t title] [-w windowid] -l line" |
|
|
2058 " [stty_args ...]\n", argv0, argv0); |
|
|
2059 } |
|
|
2060 |
|
|
2061 int |
|
|
2062 main(int argc, char *argv[]) |
|
|
2063 { |
|
|
2064 xw.l = xw.t = 0; |
|
|
2065 xw.isfixed = False; |
|
|
2066 xsetcursor(cursorshape); |
|
|
2067 |
|
|
2068 ARGBEGIN { |
|
|
2069 case 'a': |
|
|
2070 allowaltscreen = 0; |
|
|
2071 break; |
|
|
2072 case 'A': |
|
|
2073 alpha = strtof(EARGF(usage()), NULL); |
|
|
2074 LIMIT(alpha, 0.0, 1.0); |
|
|
2075 break; |
|
|
2076 case 'c': |
|
|
2077 opt_class = EARGF(usage()); |
|
|
2078 break; |
|
|
2079 case 'e': |
|
|
2080 if (argc > 0) |
|
|
2081 --argc, ++argv; |
|
|
2082 goto run; |
|
|
2083 case 'f': |
|
|
2084 opt_font = EARGF(usage()); |
|
|
2085 break; |
|
|
2086 case 'g': |
|
|
2087 xw.gm = XParseGeometry(EARGF(usage()), |
|
|
2088 &xw.l, &xw.t, &cols, &rows); |
|
|
2089 break; |
|
|
2090 case 'i': |
|
|
2091 xw.isfixed = 1; |
|
|
2092 break; |
|
|
2093 case 'o': |
|
|
2094 opt_io = EARGF(usage()); |
|
|
2095 break; |
|
|
2096 case 'l': |
|
|
2097 opt_line = EARGF(usage()); |
|
|
2098 break; |
|
|
2099 case 'n': |
|
|
2100 opt_name = EARGF(usage()); |
|
|
2101 break; |
|
|
2102 case 't': |
|
|
2103 case 'T': |
|
|
2104 opt_title = EARGF(usage()); |
|
|
2105 break; |
|
|
2106 case 'w': |
|
|
2107 opt_embed = EARGF(usage()); |
|
|
2108 break; |
|
|
2109 case 'v': |
|
|
2110 die("%s " VERSION "\n", argv0); |
|
|
2111 break; |
|
|
2112 default: |
|
|
2113 usage(); |
|
|
2114 } ARGEND; |
|
|
2115 |
|
|
2116 run: |
|
|
2117 if (argc > 0) /* eat all remaining arguments */ |
|
|
2118 opt_cmd = argv; |
|
|
2119 |
|
|
2120 if (!opt_title) |
|
|
2121 opt_title = (opt_line || !opt_cmd) ? "st" : opt_cmd[0]; |
|
|
2122 |
|
|
2123 setlocale(LC_CTYPE, ""); |
|
|
2124 XSetLocaleModifiers(""); |
|
|
2125 cols = MAX(cols, 1); |
|
|
2126 rows = MAX(rows, 1); |
|
|
2127 tnew(cols, rows); |
|
|
2128 xinit(cols, rows); |
|
|
2129 xsetenv(); |
|
|
2130 selinit(); |
|
|
2131 run(); |
|
|
2132 |
|
|
2133 return 0; |
|
|
2134 } |