diff options
| -rw-r--r-- | LICENSE | 34 | ||||
| -rw-r--r-- | Makefile | 62 | ||||
| -rw-r--r-- | README.md | 39 | ||||
| -rw-r--r-- | Xdefaults | 128 | ||||
| -rw-r--r-- | arg.h | 50 | ||||
| -rw-r--r-- | boxdraw.c | 194 | ||||
| -rw-r--r-- | boxdraw_data.h | 214 | ||||
| -rw-r--r-- | config.h | 557 | ||||
| -rw-r--r-- | config.mk | 37 | ||||
| -rw-r--r-- | hb.c | 154 | ||||
| -rw-r--r-- | hb.h | 7 | ||||
| -rwxr-xr-x | st-copyout | 13 | ||||
| -rwxr-xr-x | st-urlhandler | 19 | ||||
| -rw-r--r-- | st.1 | 193 | ||||
| -rw-r--r-- | st.c | 2816 | ||||
| -rw-r--r-- | st.h | 146 | ||||
| -rw-r--r-- | st.info | 239 | ||||
| -rw-r--r-- | win.h | 42 | ||||
| -rw-r--r-- | x.c | 2366 |
19 files changed, 7310 insertions, 0 deletions
| @@ -0,0 +1,34 @@ | |||
| 1 | MIT/X Consortium License | ||
| 2 | |||
| 3 | © 2014-2022 Hiltjo Posthuma <hiltjo at codemadness dot org> | ||
| 4 | © 2018 Devin J. Pohly <djpohly at gmail dot com> | ||
| 5 | © 2014-2017 Quentin Rameau <quinq at fifth dot space> | ||
| 6 | © 2009-2012 Aurélien APTEL <aurelien dot aptel at gmail dot com> | ||
| 7 | © 2008-2017 Anselm R Garbe <garbeam at gmail dot com> | ||
| 8 | © 2012-2017 Roberto E. Vargas Caballero <k0ga at shike2 dot com> | ||
| 9 | © 2012-2016 Christoph Lohmann <20h at r-36 dot net> | ||
| 10 | © 2013 Eon S. Jeon <esjeon at hyunmu dot am> | ||
| 11 | © 2013 Alexander Sedov <alex0player at gmail dot com> | ||
| 12 | © 2013 Mark Edgar <medgar123 at gmail dot com> | ||
| 13 | © 2013-2014 Eric Pruitt <eric.pruitt at gmail dot com> | ||
| 14 | © 2013 Michael Forney <mforney at mforney dot org> | ||
| 15 | © 2013-2014 Markus Teich <markus dot teich at stusta dot mhn dot de> | ||
| 16 | © 2014-2015 Laslo Hunhold <dev at frign dot de> | ||
| 17 | |||
| 18 | Permission is hereby granted, free of charge, to any person obtaining a | ||
| 19 | copy of this software and associated documentation files (the "Software"), | ||
| 20 | to deal in the Software without restriction, including without limitation | ||
| 21 | the rights to use, copy, modify, merge, publish, distribute, sublicense, | ||
| 22 | and/or sell copies of the Software, and to permit persons to whom the | ||
| 23 | Software is furnished to do so, subject to the following conditions: | ||
| 24 | |||
| 25 | The above copyright notice and this permission notice shall be included in | ||
| 26 | all copies or substantial portions of the Software. | ||
| 27 | |||
| 28 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | ||
| 29 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | ||
| 30 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL | ||
| 31 | THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | ||
| 32 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING | ||
| 33 | FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER | ||
| 34 | DEALINGS IN THE SOFTWARE. | ||
diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..02045f0 --- /dev/null +++ b/Makefile | |||
| @@ -0,0 +1,62 @@ | |||
| 1 | # st - simple terminal | ||
| 2 | # See LICENSE file for copyright and license details. | ||
| 3 | .POSIX: | ||
| 4 | |||
| 5 | include config.mk | ||
| 6 | |||
| 7 | SRC = st.c x.c boxdraw.c hb.c | ||
| 8 | OBJ = $(SRC:.c=.o) | ||
| 9 | |||
| 10 | all: options st | ||
| 11 | |||
| 12 | options: | ||
| 13 | @echo st build options: | ||
| 14 | @echo "CFLAGS = $(STCFLAGS)" | ||
| 15 | @echo "LDFLAGS = $(STLDFLAGS)" | ||
| 16 | @echo "CC = $(CC)" | ||
| 17 | |||
| 18 | .c.o: | ||
| 19 | $(CC) $(STCFLAGS) -c $< | ||
| 20 | |||
| 21 | st.o: config.h st.h win.h | ||
| 22 | x.o: arg.h config.h st.h win.h hb.h | ||
| 23 | hb.o: st.h | ||
| 24 | boxdraw.o: config.h st.h boxdraw_data.h | ||
| 25 | |||
| 26 | $(OBJ): config.h config.mk | ||
| 27 | |||
| 28 | st: $(OBJ) | ||
| 29 | $(CC) -o $@ $(OBJ) $(STLDFLAGS) | ||
| 30 | |||
| 31 | clean: | ||
| 32 | rm -f st $(OBJ) st-$(VERSION).tar.gz *.rej *.orig *.o | ||
| 33 | |||
| 34 | dist: clean | ||
| 35 | mkdir -p st-$(VERSION) | ||
| 36 | cp -R FAQ LEGACY TODO LICENSE Makefile README config.mk\ | ||
| 37 | config.h st.info st.1 arg.h st.h win.h $(SRC)\ | ||
| 38 | st-$(VERSION) | ||
| 39 | tar -cf - st-$(VERSION) | gzip > st-$(VERSION).tar.gz | ||
| 40 | rm -rf st-$(VERSION) | ||
| 41 | |||
| 42 | install: st | ||
| 43 | mkdir -p $(DESTDIR)$(PREFIX)/bin | ||
| 44 | cp -f st $(DESTDIR)$(PREFIX)/bin | ||
| 45 | cp -f st-copyout $(DESTDIR)$(PREFIX)/bin | ||
| 46 | cp -f st-urlhandler $(DESTDIR)$(PREFIX)/bin | ||
| 47 | chmod 755 $(DESTDIR)$(PREFIX)/bin/st | ||
| 48 | chmod 755 $(DESTDIR)$(PREFIX)/bin/st-copyout | ||
| 49 | chmod 755 $(DESTDIR)$(PREFIX)/bin/st-urlhandler | ||
| 50 | mkdir -p $(DESTDIR)$(MANPREFIX)/man1 | ||
| 51 | sed "s/VERSION/$(VERSION)/g" < st.1 > $(DESTDIR)$(MANPREFIX)/man1/st.1 | ||
| 52 | chmod 644 $(DESTDIR)$(MANPREFIX)/man1/st.1 | ||
| 53 | tic -sx st.info | ||
| 54 | @echo Please see the README file regarding the terminfo entry of st. | ||
| 55 | |||
| 56 | uninstall: | ||
| 57 | rm -f $(DESTDIR)$(PREFIX)/bin/st | ||
| 58 | rm -f $(DESTDIR)$(PREFIX)/bin/st-copyout | ||
| 59 | rm -f $(DESTDIR)$(PREFIX)/bin/st-urlhandler | ||
| 60 | rm -f $(DESTDIR)$(MANPREFIX)/man1/st.1 | ||
| 61 | |||
| 62 | .PHONY: all options clean dist install uninstall | ||
diff --git a/README.md b/README.md new file mode 100644 index 0000000..72fe8e5 --- /dev/null +++ b/README.md | |||
| @@ -0,0 +1,39 @@ | |||
| 1 | # st | ||
| 2 | |||
| 3 | Forked from [https://github.com/Lukesmithxyz/st][https://github.com/Lukesmithxyz/st] | ||
| 4 | |||
| 5 | ## Unique features (using dmenu) | ||
| 6 | |||
| 7 | + **follow urls** by pressing `alt-l` | ||
| 8 | + **copy urls** in the same way with `alt-y` | ||
| 9 | + **copy the output of commands** with `alt-o` | ||
| 10 | |||
| 11 | ## Bindings for | ||
| 12 | |||
| 13 | + **scrollback** with `alt-↑/↓` or `alt-pageup/down` or `shift` while scrolling the | ||
| 14 | mouse. | ||
| 15 | + OR **vim-bindings**: scroll up/down in history with `alt-k` and `alt-j`. | ||
| 16 | Faster with `alt-u`/`alt-d`. | ||
| 17 | + **zoom/change font size**: same bindings as above, but holding down shift as | ||
| 18 | well. `alt-home` returns to default | ||
| 19 | + **copy text** with `alt-c`, **paste** is `alt-v` or `shift-insert` | ||
| 20 | |||
| 21 | |||
| 22 | ## st patches | ||
| 23 | |||
| 24 | - alpha | ||
| 25 | - xresources | ||
| 26 | - boxdraw | ||
| 27 | - ligatures | ||
| 28 | - font2 | ||
| 29 | - scrollback | ||
| 30 | - externalpipe | ||
| 31 | |||
| 32 | ## Installation | ||
| 33 | |||
| 34 | ``` | ||
| 35 | apt install fontconfig libx11-dev libxft-dev libharfbuzz-dev build-essential | ||
| 36 | git clone https://gitea.chudnick.com/sam/st | ||
| 37 | cd st | ||
| 38 | sudo make install | ||
| 39 | ``` | ||
diff --git a/Xdefaults b/Xdefaults new file mode 100644 index 0000000..040c772 --- /dev/null +++ b/Xdefaults | |||
| @@ -0,0 +1,128 @@ | |||
| 1 | !! Transparency (0-1): | ||
| 2 | st.alpha: 0.92 | ||
| 3 | st.alphaOffset: 0.3 | ||
| 4 | |||
| 5 | !! Set a default font and font size as below: | ||
| 6 | st.font: Monospace-11; | ||
| 7 | |||
| 8 | ! st.termname: st-256color | ||
| 9 | ! st.borderpx: 2 | ||
| 10 | |||
| 11 | !! Set the background, foreground and cursor colors as below: | ||
| 12 | |||
| 13 | !! gruvbox: | ||
| 14 | *.color0: #1d2021 | ||
| 15 | *.color1: #cc241d | ||
| 16 | *.color2: #98971a | ||
| 17 | *.color3: #d79921 | ||
| 18 | *.color4: #458588 | ||
| 19 | *.color5: #b16286 | ||
| 20 | *.color6: #689d6a | ||
| 21 | *.color7: #a89984 | ||
| 22 | *.color8: #928374 | ||
| 23 | *.color9: #fb4934 | ||
| 24 | *.color10: #b8bb26 | ||
| 25 | *.color11: #fabd2f | ||
| 26 | *.color12: #83a598 | ||
| 27 | *.color13: #d3869b | ||
| 28 | *.color14: #8ec07c | ||
| 29 | *.color15: #ebdbb2 | ||
| 30 | *.background: #282828 | ||
| 31 | *.foreground: white | ||
| 32 | *.cursorColor: white | ||
| 33 | |||
| 34 | /* /1* !! gruvbox light: *1/ */ | ||
| 35 | /* *.color0: #fbf1c7 */ | ||
| 36 | /* *.color1: #cc241d */ | ||
| 37 | /* *.color2: #98971a */ | ||
| 38 | /* *.color3: #d79921 */ | ||
| 39 | /* *.color4: #458588 */ | ||
| 40 | /* *.color5: #b16286 */ | ||
| 41 | /* *.color6: #689d6a */ | ||
| 42 | /* *.color7: #7c6f64 */ | ||
| 43 | /* *.color8: #928374 */ | ||
| 44 | /* *.color9: #9d0006 */ | ||
| 45 | /* *.color10: #79740e */ | ||
| 46 | /* *.color11: #b57614 */ | ||
| 47 | /* *.color12: #076678 */ | ||
| 48 | /* *.color13: #8f3f71 */ | ||
| 49 | /* *.color14: #427b58 */ | ||
| 50 | /* *.color15: #3c3836 */ | ||
| 51 | /* *.background: #fbf1c7 */ | ||
| 52 | /* *.foreground: #282828 */ | ||
| 53 | /* *.cursorColor: #282828 */ | ||
| 54 | |||
| 55 | /* !! brogrammer: */ | ||
| 56 | /* *.foreground: #d6dbe5 */ | ||
| 57 | /* *.background: #131313 */ | ||
| 58 | /* *.color0: #1f1f1f */ | ||
| 59 | /* *.color8: #d6dbe5 */ | ||
| 60 | /* *.color1: #f81118 */ | ||
| 61 | /* *.color9: #de352e */ | ||
| 62 | /* *.color2: #2dc55e */ | ||
| 63 | /* *.color10: #1dd361 */ | ||
| 64 | /* *.color3: #ecba0f */ | ||
| 65 | /* *.color11: #f3bd09 */ | ||
| 66 | /* *.color4: #2a84d2 */ | ||
| 67 | /* *.color12: #1081d6 */ | ||
| 68 | /* *.color5: #4e5ab7 */ | ||
| 69 | /* *.color13: #5350b9 */ | ||
| 70 | /* *.color6: #1081d6 */ | ||
| 71 | /* *.color14: #0f7ddb */ | ||
| 72 | /* *.color7: #d6dbe5 */ | ||
| 73 | /* *.color15: #ffffff */ | ||
| 74 | /* *.colorBD: #d6dbe5 */ | ||
| 75 | |||
| 76 | /* ! base16 */ | ||
| 77 | /* *.color0: #181818 */ | ||
| 78 | /* *.color1: #ab4642 */ | ||
| 79 | /* *.color2: #a1b56c */ | ||
| 80 | /* *.color3: #f7ca88 */ | ||
| 81 | /* *.color4: #7cafc2 */ | ||
| 82 | /* *.color5: #ba8baf */ | ||
| 83 | /* *.color6: #86c1b9 */ | ||
| 84 | /* *.color7: #d8d8d8 */ | ||
| 85 | /* *.color8: #585858 */ | ||
| 86 | /* *.color9: #ab4642 */ | ||
| 87 | /* *.color10: #a1b56c */ | ||
| 88 | /* *.color11: #f7ca88 */ | ||
| 89 | /* *.color12: #7cafc2 */ | ||
| 90 | /* *.color13: #ba8baf */ | ||
| 91 | /* *.color14: #86c1b9 */ | ||
| 92 | /* *.color15: #f8f8f8 */ | ||
| 93 | |||
| 94 | /* !! solarized */ | ||
| 95 | /* *.color0: #073642 */ | ||
| 96 | /* *.color1: #dc322f */ | ||
| 97 | /* *.color2: #859900 */ | ||
| 98 | /* *.color3: #b58900 */ | ||
| 99 | /* *.color4: #268bd2 */ | ||
| 100 | /* *.color5: #d33682 */ | ||
| 101 | /* *.color6: #2aa198 */ | ||
| 102 | /* *.color7: #eee8d5 */ | ||
| 103 | /* *.color9: #cb4b16 */ | ||
| 104 | /* *.color8: #fdf6e3 */ | ||
| 105 | /* *.color10: #586e75 */ | ||
| 106 | /* *.color11: #657b83 */ | ||
| 107 | /* *.color12: #839496 */ | ||
| 108 | /* *.color13: #6c71c4 */ | ||
| 109 | /* *.color14: #93a1a1 */ | ||
| 110 | /* *.color15: #fdf6e3 */ | ||
| 111 | |||
| 112 | /* !! xterm */ | ||
| 113 | /* *.color0: #000000 */ | ||
| 114 | /* *.color1: #cd0000 */ | ||
| 115 | /* *.color2: #00cd00 */ | ||
| 116 | /* *.color3: #cdcd00 */ | ||
| 117 | /* *.color4: #0000cd */ | ||
| 118 | /* *.color5: #cd00cd */ | ||
| 119 | /* *.color6: #00cdcd */ | ||
| 120 | /* *.color7: #e5e5e5 */ | ||
| 121 | /* *.color8: #4d4d4d */ | ||
| 122 | /* *.color9: #ff0000 */ | ||
| 123 | /* *.color10: #00ff00 */ | ||
| 124 | /* *.color11: #ffff00 */ | ||
| 125 | /* *.color12: #0000ff */ | ||
| 126 | /* *.color13: #ff00ff */ | ||
| 127 | /* *.color14: #00ffff */ | ||
| 128 | /* *.color15: #aabac8 */ | ||
| @@ -0,0 +1,50 @@ | |||
| 1 | /* | ||
| 2 | * Copy me if you can. | ||
| 3 | * by 20h | ||
| 4 | */ | ||
| 5 | |||
| 6 | #ifndef ARG_H__ | ||
| 7 | #define ARG_H__ | ||
| 8 | |||
| 9 | extern char *argv0; | ||
| 10 | |||
| 11 | /* use main(int argc, char *argv[]) */ | ||
| 12 | #define ARGBEGIN for (argv0 = *argv, argv++, argc--;\ | ||
| 13 | argv[0] && argv[0][0] == '-'\ | ||
| 14 | && argv[0][1];\ | ||
| 15 | argc--, argv++) {\ | ||
| 16 | char argc_;\ | ||
| 17 | char **argv_;\ | ||
| 18 | int brk_;\ | ||
| 19 | if (argv[0][1] == '-' && argv[0][2] == '\0') {\ | ||
| 20 | argv++;\ | ||
| 21 | argc--;\ | ||
| 22 | break;\ | ||
| 23 | }\ | ||
| 24 | int i_;\ | ||
| 25 | for (i_ = 1, brk_ = 0, argv_ = argv;\ | ||
| 26 | argv[0][i_] && !brk_;\ | ||
| 27 | i_++) {\ | ||
| 28 | if (argv_ != argv)\ | ||
| 29 | break;\ | ||
| 30 | argc_ = argv[0][i_];\ | ||
| 31 | switch (argc_) | ||
| 32 | |||
| 33 | #define ARGEND }\ | ||
| 34 | } | ||
| 35 | |||
| 36 | #define ARGC() argc_ | ||
| 37 | |||
| 38 | #define EARGF(x) ((argv[0][i_+1] == '\0' && argv[1] == NULL)?\ | ||
| 39 | ((x), abort(), (char *)0) :\ | ||
| 40 | (brk_ = 1, (argv[0][i_+1] != '\0')?\ | ||
| 41 | (&argv[0][i_+1]) :\ | ||
| 42 | (argc--, argv++, argv[0]))) | ||
| 43 | |||
| 44 | #define ARGF() ((argv[0][i_+1] == '\0' && argv[1] == NULL)?\ | ||
| 45 | (char *)0 :\ | ||
| 46 | (brk_ = 1, (argv[0][i_+1] != '\0')?\ | ||
| 47 | (&argv[0][i_+1]) :\ | ||
| 48 | (argc--, argv++, argv[0]))) | ||
| 49 | |||
| 50 | #endif | ||
diff --git a/boxdraw.c b/boxdraw.c new file mode 100644 index 0000000..28a92d0 --- /dev/null +++ b/boxdraw.c | |||
| @@ -0,0 +1,194 @@ | |||
| 1 | /* | ||
| 2 | * Copyright 2018 Avi Halachmi (:avih) avihpit@yahoo.com https://github.com/avih | ||
| 3 | * MIT/X Consortium License | ||
| 4 | */ | ||
| 5 | |||
| 6 | #include <X11/Xft/Xft.h> | ||
| 7 | #include "st.h" | ||
| 8 | #include "boxdraw_data.h" | ||
| 9 | |||
| 10 | /* Rounded non-negative integers division of n / d */ | ||
| 11 | #define DIV(n, d) (((n) + (d) / 2) / (d)) | ||
| 12 | |||
| 13 | static Display *xdpy; | ||
| 14 | static Colormap xcmap; | ||
| 15 | static XftDraw *xd; | ||
| 16 | static Visual *xvis; | ||
| 17 | |||
| 18 | static void drawbox(int, int, int, int, XftColor *, XftColor *, ushort); | ||
| 19 | static void drawboxlines(int, int, int, int, XftColor *, ushort); | ||
| 20 | |||
| 21 | /* public API */ | ||
| 22 | |||
| 23 | void | ||
| 24 | boxdraw_xinit(Display *dpy, Colormap cmap, XftDraw *draw, Visual *vis) | ||
| 25 | { | ||
| 26 | xdpy = dpy; xcmap = cmap; xd = draw, xvis = vis; | ||
| 27 | } | ||
| 28 | |||
| 29 | int | ||
| 30 | isboxdraw(Rune u) | ||
| 31 | { | ||
| 32 | Rune block = u & ~0xff; | ||
| 33 | return (boxdraw && block == 0x2500 && boxdata[(uint8_t)u]) || | ||
| 34 | (boxdraw_braille && block == 0x2800); | ||
| 35 | } | ||
| 36 | |||
| 37 | /* the "index" is actually the entire shape data encoded as ushort */ | ||
| 38 | ushort | ||
| 39 | boxdrawindex(const Glyph *g) | ||
| 40 | { | ||
| 41 | if (boxdraw_braille && (g->u & ~0xff) == 0x2800) | ||
| 42 | return BRL | (uint8_t)g->u; | ||
| 43 | if (boxdraw_bold && (g->mode & ATTR_BOLD)) | ||
| 44 | return BDB | boxdata[(uint8_t)g->u]; | ||
| 45 | return boxdata[(uint8_t)g->u]; | ||
| 46 | } | ||
| 47 | |||
| 48 | void | ||
| 49 | drawboxes(int x, int y, int cw, int ch, XftColor *fg, XftColor *bg, | ||
| 50 | const XftGlyphFontSpec *specs, int len) | ||
| 51 | { | ||
| 52 | for ( ; len-- > 0; x += cw, specs++) | ||
| 53 | drawbox(x, y, cw, ch, fg, bg, (ushort)specs->glyph); | ||
| 54 | } | ||
| 55 | |||
| 56 | /* implementation */ | ||
| 57 | |||
| 58 | void | ||
| 59 | drawbox(int x, int y, int w, int h, XftColor *fg, XftColor *bg, ushort bd) | ||
| 60 | { | ||
| 61 | ushort cat = bd & ~(BDB | 0xff); /* mask out bold and data */ | ||
| 62 | if (bd & (BDL | BDA)) { | ||
| 63 | /* lines (light/double/heavy/arcs) */ | ||
| 64 | drawboxlines(x, y, w, h, fg, bd); | ||
| 65 | |||
| 66 | } else if (cat == BBD) { | ||
| 67 | /* lower (8-X)/8 block */ | ||
| 68 | int d = DIV((uint8_t)bd * h, 8); | ||
| 69 | XftDrawRect(xd, fg, x, y + d, w, h - d); | ||
| 70 | |||
| 71 | } else if (cat == BBU) { | ||
| 72 | /* upper X/8 block */ | ||
| 73 | XftDrawRect(xd, fg, x, y, w, DIV((uint8_t)bd * h, 8)); | ||
| 74 | |||
| 75 | } else if (cat == BBL) { | ||
| 76 | /* left X/8 block */ | ||
| 77 | XftDrawRect(xd, fg, x, y, DIV((uint8_t)bd * w, 8), h); | ||
| 78 | |||
| 79 | } else if (cat == BBR) { | ||
| 80 | /* right (8-X)/8 block */ | ||
| 81 | int d = DIV((uint8_t)bd * w, 8); | ||
| 82 | XftDrawRect(xd, fg, x + d, y, w - d, h); | ||
| 83 | |||
| 84 | } else if (cat == BBQ) { | ||
| 85 | /* Quadrants */ | ||
| 86 | int w2 = DIV(w, 2), h2 = DIV(h, 2); | ||
| 87 | if (bd & TL) | ||
| 88 | XftDrawRect(xd, fg, x, y, w2, h2); | ||
| 89 | if (bd & TR) | ||
| 90 | XftDrawRect(xd, fg, x + w2, y, w - w2, h2); | ||
| 91 | if (bd & BL) | ||
| 92 | XftDrawRect(xd, fg, x, y + h2, w2, h - h2); | ||
| 93 | if (bd & BR) | ||
| 94 | XftDrawRect(xd, fg, x + w2, y + h2, w - w2, h - h2); | ||
| 95 | |||
| 96 | } else if (bd & BBS) { | ||
| 97 | /* Shades - data is 1/2/3 for 25%/50%/75% alpha, respectively */ | ||
| 98 | int d = (uint8_t)bd; | ||
| 99 | XftColor xfc; | ||
| 100 | XRenderColor xrc = { .alpha = 0xffff }; | ||
| 101 | |||
| 102 | xrc.red = DIV(fg->color.red * d + bg->color.red * (4 - d), 4); | ||
| 103 | xrc.green = DIV(fg->color.green * d + bg->color.green * (4 - d), 4); | ||
| 104 | xrc.blue = DIV(fg->color.blue * d + bg->color.blue * (4 - d), 4); | ||
| 105 | |||
| 106 | XftColorAllocValue(xdpy, xvis, xcmap, &xrc, &xfc); | ||
| 107 | XftDrawRect(xd, &xfc, x, y, w, h); | ||
| 108 | XftColorFree(xdpy, xvis, xcmap, &xfc); | ||
| 109 | |||
| 110 | } else if (cat == BRL) { | ||
| 111 | /* braille, each data bit corresponds to one dot at 2x4 grid */ | ||
| 112 | int w1 = DIV(w, 2); | ||
| 113 | int h1 = DIV(h, 4), h2 = DIV(h, 2), h3 = DIV(3 * h, 4); | ||
| 114 | |||
| 115 | if (bd & 1) XftDrawRect(xd, fg, x, y, w1, h1); | ||
| 116 | if (bd & 2) XftDrawRect(xd, fg, x, y + h1, w1, h2 - h1); | ||
| 117 | if (bd & 4) XftDrawRect(xd, fg, x, y + h2, w1, h3 - h2); | ||
| 118 | if (bd & 8) XftDrawRect(xd, fg, x + w1, y, w - w1, h1); | ||
| 119 | if (bd & 16) XftDrawRect(xd, fg, x + w1, y + h1, w - w1, h2 - h1); | ||
| 120 | if (bd & 32) XftDrawRect(xd, fg, x + w1, y + h2, w - w1, h3 - h2); | ||
| 121 | if (bd & 64) XftDrawRect(xd, fg, x, y + h3, w1, h - h3); | ||
| 122 | if (bd & 128) XftDrawRect(xd, fg, x + w1, y + h3, w - w1, h - h3); | ||
| 123 | |||
| 124 | } | ||
| 125 | } | ||
| 126 | |||
| 127 | void | ||
| 128 | drawboxlines(int x, int y, int w, int h, XftColor *fg, ushort bd) | ||
| 129 | { | ||
| 130 | /* s: stem thickness. width/8 roughly matches underscore thickness. */ | ||
| 131 | /* We draw bold as 1.5 * normal-stem and at least 1px thicker. */ | ||
| 132 | /* doubles draw at least 3px, even when w or h < 3. bold needs 6px. */ | ||
| 133 | int mwh = MIN(w, h); | ||
| 134 | int base_s = MAX(1, DIV(mwh, 8)); | ||
| 135 | int bold = (bd & BDB) && mwh >= 6; /* possibly ignore boldness */ | ||
| 136 | int s = bold ? MAX(base_s + 1, DIV(3 * base_s, 2)) : base_s; | ||
| 137 | int w2 = DIV(w - s, 2), h2 = DIV(h - s, 2); | ||
| 138 | /* the s-by-s square (x + w2, y + h2, s, s) is the center texel. */ | ||
| 139 | /* The base length (per direction till edge) includes this square. */ | ||
| 140 | |||
| 141 | int light = bd & (LL | LU | LR | LD); | ||
| 142 | int double_ = bd & (DL | DU | DR | DD); | ||
| 143 | |||
| 144 | if (light) { | ||
| 145 | /* d: additional (negative) length to not-draw the center */ | ||
| 146 | /* texel - at arcs and avoid drawing inside (some) doubles */ | ||
| 147 | int arc = bd & BDA; | ||
| 148 | int multi_light = light & (light - 1); | ||
| 149 | int multi_double = double_ & (double_ - 1); | ||
| 150 | /* light crosses double only at DH+LV, DV+LH (ref. shapes) */ | ||
| 151 | int d = arc || (multi_double && !multi_light) ? -s : 0; | ||
| 152 | |||
| 153 | if (bd & LL) | ||
| 154 | XftDrawRect(xd, fg, x, y + h2, w2 + s + d, s); | ||
| 155 | if (bd & LU) | ||
| 156 | XftDrawRect(xd, fg, x + w2, y, s, h2 + s + d); | ||
| 157 | if (bd & LR) | ||
| 158 | XftDrawRect(xd, fg, x + w2 - d, y + h2, w - w2 + d, s); | ||
| 159 | if (bd & LD) | ||
| 160 | XftDrawRect(xd, fg, x + w2, y + h2 - d, s, h - h2 + d); | ||
| 161 | } | ||
| 162 | |||
| 163 | /* double lines - also align with light to form heavy when combined */ | ||
| 164 | if (double_) { | ||
| 165 | /* | ||
| 166 | * going clockwise, for each double-ray: p is additional length | ||
| 167 | * to the single-ray nearer to the previous direction, and n to | ||
| 168 | * the next. p and n adjust from the base length to lengths | ||
| 169 | * which consider other doubles - shorter to avoid intersections | ||
| 170 | * (p, n), or longer to draw the far-corner texel (n). | ||
| 171 | */ | ||
| 172 | int dl = bd & DL, du = bd & DU, dr = bd & DR, dd = bd & DD; | ||
| 173 | if (dl) { | ||
| 174 | int p = dd ? -s : 0, n = du ? -s : dd ? s : 0; | ||
| 175 | XftDrawRect(xd, fg, x, y + h2 + s, w2 + s + p, s); | ||
| 176 | XftDrawRect(xd, fg, x, y + h2 - s, w2 + s + n, s); | ||
| 177 | } | ||
| 178 | if (du) { | ||
| 179 | int p = dl ? -s : 0, n = dr ? -s : dl ? s : 0; | ||
| 180 | XftDrawRect(xd, fg, x + w2 - s, y, s, h2 + s + p); | ||
| 181 | XftDrawRect(xd, fg, x + w2 + s, y, s, h2 + s + n); | ||
| 182 | } | ||
| 183 | if (dr) { | ||
| 184 | int p = du ? -s : 0, n = dd ? -s : du ? s : 0; | ||
| 185 | XftDrawRect(xd, fg, x + w2 - p, y + h2 - s, w - w2 + p, s); | ||
| 186 | XftDrawRect(xd, fg, x + w2 - n, y + h2 + s, w - w2 + n, s); | ||
| 187 | } | ||
| 188 | if (dd) { | ||
| 189 | int p = dr ? -s : 0, n = dl ? -s : dr ? s : 0; | ||
| 190 | XftDrawRect(xd, fg, x + w2 + s, y + h2 - p, s, h - h2 + p); | ||
| 191 | XftDrawRect(xd, fg, x + w2 - s, y + h2 - n, s, h - h2 + n); | ||
| 192 | } | ||
| 193 | } | ||
| 194 | } | ||
diff --git a/boxdraw_data.h b/boxdraw_data.h new file mode 100644 index 0000000..7890500 --- /dev/null +++ b/boxdraw_data.h | |||
| @@ -0,0 +1,214 @@ | |||
| 1 | /* | ||
| 2 | * Copyright 2018 Avi Halachmi (:avih) avihpit@yahoo.com https://github.com/avih | ||
| 3 | * MIT/X Consortium License | ||
| 4 | */ | ||
| 5 | |||
| 6 | /* | ||
| 7 | * U+25XX codepoints data | ||
| 8 | * | ||
| 9 | * References: | ||
| 10 | * http://www.unicode.org/charts/PDF/U2500.pdf | ||
| 11 | * http://www.unicode.org/charts/PDF/U2580.pdf | ||
| 12 | * | ||
| 13 | * Test page: | ||
| 14 | * https://github.com/GNOME/vte/blob/master/doc/boxes.txt | ||
| 15 | */ | ||
| 16 | |||
| 17 | /* Each shape is encoded as 16-bits. Higher bits are category, lower are data */ | ||
| 18 | /* Categories (mutually exclusive except BDB): */ | ||
| 19 | /* For convenience, BDL/BDA/BBS/BDB are 1 bit each, the rest are enums */ | ||
| 20 | #define BDL (1<<8) /* Box Draw Lines (light/double/heavy) */ | ||
| 21 | #define BDA (1<<9) /* Box Draw Arc (light) */ | ||
| 22 | |||
| 23 | #define BBD (1<<10) /* Box Block Down (lower) X/8 */ | ||
| 24 | #define BBL (2<<10) /* Box Block Left X/8 */ | ||
| 25 | #define BBU (3<<10) /* Box Block Upper X/8 */ | ||
| 26 | #define BBR (4<<10) /* Box Block Right X/8 */ | ||
| 27 | #define BBQ (5<<10) /* Box Block Quadrants */ | ||
| 28 | #define BRL (6<<10) /* Box Braille (data is lower byte of U28XX) */ | ||
| 29 | |||
| 30 | #define BBS (1<<14) /* Box Block Shades */ | ||
| 31 | #define BDB (1<<15) /* Box Draw is Bold */ | ||
| 32 | |||
| 33 | /* (BDL/BDA) Light/Double/Heavy x Left/Up/Right/Down/Horizontal/Vertical */ | ||
| 34 | /* Heavy is light+double (literally drawing light+double align to form heavy) */ | ||
| 35 | #define LL (1<<0) | ||
| 36 | #define LU (1<<1) | ||
| 37 | #define LR (1<<2) | ||
| 38 | #define LD (1<<3) | ||
| 39 | #define LH (LL+LR) | ||
| 40 | #define LV (LU+LD) | ||
| 41 | |||
| 42 | #define DL (1<<4) | ||
| 43 | #define DU (1<<5) | ||
| 44 | #define DR (1<<6) | ||
| 45 | #define DD (1<<7) | ||
| 46 | #define DH (DL+DR) | ||
| 47 | #define DV (DU+DD) | ||
| 48 | |||
| 49 | #define HL (LL+DL) | ||
| 50 | #define HU (LU+DU) | ||
| 51 | #define HR (LR+DR) | ||
| 52 | #define HD (LD+DD) | ||
| 53 | #define HH (HL+HR) | ||
| 54 | #define HV (HU+HD) | ||
| 55 | |||
| 56 | /* (BBQ) Quadrants Top/Bottom x Left/Right */ | ||
| 57 | #define TL (1<<0) | ||
| 58 | #define TR (1<<1) | ||
| 59 | #define BL (1<<2) | ||
| 60 | #define BR (1<<3) | ||
| 61 | |||
| 62 | /* Data for U+2500 - U+259F except dashes/diagonals */ | ||
| 63 | static const unsigned short boxdata[256] = { | ||
| 64 | /* light lines */ | ||
| 65 | [0x00] = BDL + LH, /* light horizontal */ | ||
| 66 | [0x02] = BDL + LV, /* light vertical */ | ||
| 67 | [0x0c] = BDL + LD + LR, /* light down and right */ | ||
| 68 | [0x10] = BDL + LD + LL, /* light down and left */ | ||
| 69 | [0x14] = BDL + LU + LR, /* light up and right */ | ||
| 70 | [0x18] = BDL + LU + LL, /* light up and left */ | ||
| 71 | [0x1c] = BDL + LV + LR, /* light vertical and right */ | ||
| 72 | [0x24] = BDL + LV + LL, /* light vertical and left */ | ||
| 73 | [0x2c] = BDL + LH + LD, /* light horizontal and down */ | ||
| 74 | [0x34] = BDL + LH + LU, /* light horizontal and up */ | ||
| 75 | [0x3c] = BDL + LV + LH, /* light vertical and horizontal */ | ||
| 76 | [0x74] = BDL + LL, /* light left */ | ||
| 77 | [0x75] = BDL + LU, /* light up */ | ||
| 78 | [0x76] = BDL + LR, /* light right */ | ||
| 79 | [0x77] = BDL + LD, /* light down */ | ||
| 80 | |||
| 81 | /* heavy [+light] lines */ | ||
| 82 | [0x01] = BDL + HH, | ||
| 83 | [0x03] = BDL + HV, | ||
| 84 | [0x0d] = BDL + HR + LD, | ||
| 85 | [0x0e] = BDL + HD + LR, | ||
| 86 | [0x0f] = BDL + HD + HR, | ||
| 87 | [0x11] = BDL + HL + LD, | ||
| 88 | [0x12] = BDL + HD + LL, | ||
| 89 | [0x13] = BDL + HD + HL, | ||
| 90 | [0x15] = BDL + HR + LU, | ||
| 91 | [0x16] = BDL + HU + LR, | ||
| 92 | [0x17] = BDL + HU + HR, | ||
| 93 | [0x19] = BDL + HL + LU, | ||
| 94 | [0x1a] = BDL + HU + LL, | ||
| 95 | [0x1b] = BDL + HU + HL, | ||
| 96 | [0x1d] = BDL + HR + LV, | ||
| 97 | [0x1e] = BDL + HU + LD + LR, | ||
| 98 | [0x1f] = BDL + HD + LR + LU, | ||
| 99 | [0x20] = BDL + HV + LR, | ||
| 100 | [0x21] = BDL + HU + HR + LD, | ||
| 101 | [0x22] = BDL + HD + HR + LU, | ||
| 102 | [0x23] = BDL + HV + HR, | ||
| 103 | [0x25] = BDL + HL + LV, | ||
| 104 | [0x26] = BDL + HU + LD + LL, | ||
| 105 | [0x27] = BDL + HD + LU + LL, | ||
| 106 | [0x28] = BDL + HV + LL, | ||
| 107 | [0x29] = BDL + HU + HL + LD, | ||
| 108 | [0x2a] = BDL + HD + HL + LU, | ||
| 109 | [0x2b] = BDL + HV + HL, | ||
| 110 | [0x2d] = BDL + HL + LD + LR, | ||
| 111 | [0x2e] = BDL + HR + LL + LD, | ||
| 112 | [0x2f] = BDL + HH + LD, | ||
| 113 | [0x30] = BDL + HD + LH, | ||
| 114 | [0x31] = BDL + HD + HL + LR, | ||
| 115 | [0x32] = BDL + HR + HD + LL, | ||
| 116 | [0x33] = BDL + HH + HD, | ||
| 117 | [0x35] = BDL + HL + LU + LR, | ||
| 118 | [0x36] = BDL + HR + LU + LL, | ||
| 119 | [0x37] = BDL + HH + LU, | ||
| 120 | [0x38] = BDL + HU + LH, | ||
| 121 | [0x39] = BDL + HU + HL + LR, | ||
| 122 | [0x3a] = BDL + HU + HR + LL, | ||
| 123 | [0x3b] = BDL + HH + HU, | ||
| 124 | [0x3d] = BDL + HL + LV + LR, | ||
| 125 | [0x3e] = BDL + HR + LV + LL, | ||
| 126 | [0x3f] = BDL + HH + LV, | ||
| 127 | [0x40] = BDL + HU + LH + LD, | ||
| 128 | [0x41] = BDL + HD + LH + LU, | ||
| 129 | [0x42] = BDL + HV + LH, | ||
| 130 | [0x43] = BDL + HU + HL + LD + LR, | ||
| 131 | [0x44] = BDL + HU + HR + LD + LL, | ||
| 132 | [0x45] = BDL + HD + HL + LU + LR, | ||
| 133 | [0x46] = BDL + HD + HR + LU + LL, | ||
| 134 | [0x47] = BDL + HH + HU + LD, | ||
| 135 | [0x48] = BDL + HH + HD + LU, | ||
| 136 | [0x49] = BDL + HV + HL + LR, | ||
| 137 | [0x4a] = BDL + HV + HR + LL, | ||
| 138 | [0x4b] = BDL + HV + HH, | ||
| 139 | [0x78] = BDL + HL, | ||
| 140 | [0x79] = BDL + HU, | ||
| 141 | [0x7a] = BDL + HR, | ||
| 142 | [0x7b] = BDL + HD, | ||
| 143 | [0x7c] = BDL + HR + LL, | ||
| 144 | [0x7d] = BDL + HD + LU, | ||
| 145 | [0x7e] = BDL + HL + LR, | ||
| 146 | [0x7f] = BDL + HU + LD, | ||
| 147 | |||
| 148 | /* double [+light] lines */ | ||
| 149 | [0x50] = BDL + DH, | ||
| 150 | [0x51] = BDL + DV, | ||
| 151 | [0x52] = BDL + DR + LD, | ||
| 152 | [0x53] = BDL + DD + LR, | ||
| 153 | [0x54] = BDL + DR + DD, | ||
| 154 | [0x55] = BDL + DL + LD, | ||
| 155 | [0x56] = BDL + DD + LL, | ||
| 156 | [0x57] = BDL + DL + DD, | ||
| 157 | [0x58] = BDL + DR + LU, | ||
| 158 | [0x59] = BDL + DU + LR, | ||
| 159 | [0x5a] = BDL + DU + DR, | ||
| 160 | [0x5b] = BDL + DL + LU, | ||
| 161 | [0x5c] = BDL + DU + LL, | ||
| 162 | [0x5d] = BDL + DL + DU, | ||
| 163 | [0x5e] = BDL + DR + LV, | ||
| 164 | [0x5f] = BDL + DV + LR, | ||
| 165 | [0x60] = BDL + DV + DR, | ||
| 166 | [0x61] = BDL + DL + LV, | ||
| 167 | [0x62] = BDL + DV + LL, | ||
| 168 | [0x63] = BDL + DV + DL, | ||
| 169 | [0x64] = BDL + DH + LD, | ||
| 170 | [0x65] = BDL + DD + LH, | ||
| 171 | [0x66] = BDL + DD + DH, | ||
| 172 | [0x67] = BDL + DH + LU, | ||
| 173 | [0x68] = BDL + DU + LH, | ||
| 174 | [0x69] = BDL + DH + DU, | ||
| 175 | [0x6a] = BDL + DH + LV, | ||
| 176 | [0x6b] = BDL + DV + LH, | ||
| 177 | [0x6c] = BDL + DH + DV, | ||
| 178 | |||
| 179 | /* (light) arcs */ | ||
| 180 | [0x6d] = BDA + LD + LR, | ||
| 181 | [0x6e] = BDA + LD + LL, | ||
| 182 | [0x6f] = BDA + LU + LL, | ||
| 183 | [0x70] = BDA + LU + LR, | ||
| 184 | |||
| 185 | /* Lower (Down) X/8 block (data is 8 - X) */ | ||
| 186 | [0x81] = BBD + 7, [0x82] = BBD + 6, [0x83] = BBD + 5, [0x84] = BBD + 4, | ||
| 187 | [0x85] = BBD + 3, [0x86] = BBD + 2, [0x87] = BBD + 1, [0x88] = BBD + 0, | ||
| 188 | |||
| 189 | /* Left X/8 block (data is X) */ | ||
| 190 | [0x89] = BBL + 7, [0x8a] = BBL + 6, [0x8b] = BBL + 5, [0x8c] = BBL + 4, | ||
| 191 | [0x8d] = BBL + 3, [0x8e] = BBL + 2, [0x8f] = BBL + 1, | ||
| 192 | |||
| 193 | /* upper 1/2 (4/8), 1/8 block (X), right 1/2, 1/8 block (8-X) */ | ||
| 194 | [0x80] = BBU + 4, [0x94] = BBU + 1, | ||
| 195 | [0x90] = BBR + 4, [0x95] = BBR + 7, | ||
| 196 | |||
| 197 | /* Quadrants */ | ||
| 198 | [0x96] = BBQ + BL, | ||
| 199 | [0x97] = BBQ + BR, | ||
| 200 | [0x98] = BBQ + TL, | ||
| 201 | [0x99] = BBQ + TL + BL + BR, | ||
| 202 | [0x9a] = BBQ + TL + BR, | ||
| 203 | [0x9b] = BBQ + TL + TR + BL, | ||
| 204 | [0x9c] = BBQ + TL + TR + BR, | ||
| 205 | [0x9d] = BBQ + TR, | ||
| 206 | [0x9e] = BBQ + BL + TR, | ||
| 207 | [0x9f] = BBQ + BL + TR + BR, | ||
| 208 | |||
| 209 | /* Shades, data is an alpha value in 25% units (1/4, 1/2, 3/4) */ | ||
| 210 | [0x91] = BBS + 1, [0x92] = BBS + 2, [0x93] = BBS + 3, | ||
| 211 | |||
| 212 | /* U+2504 - U+250B, U+254C - U+254F: unsupported (dashes) */ | ||
| 213 | /* U+2571 - U+2573: unsupported (diagonals) */ | ||
| 214 | }; | ||
diff --git a/config.h b/config.h new file mode 100644 index 0000000..8f79881 --- /dev/null +++ b/config.h | |||
| @@ -0,0 +1,557 @@ | |||
| 1 | /* See LICENSE file for copyright and license details. */ | ||
| 2 | |||
| 3 | /* | ||
| 4 | * appearance | ||
| 5 | * | ||
| 6 | * font: see http://freedesktop.org/software/fontconfig/fontconfig-user.html | ||
| 7 | */ | ||
| 8 | static char *font = "mono:pixelsize=16:antialias=true:autohint=true"; | ||
| 9 | static char *font2[] = { "NotoColorEmoji:pixelsize=10:antialias=true:autohint=true" }; | ||
| 10 | static int borderpx = 2; | ||
| 11 | |||
| 12 | /* | ||
| 13 | * What program is execed by st depends of these precedence rules: | ||
| 14 | * 1: program passed with -e | ||
| 15 | * 2: scroll and/or utmp | ||
| 16 | * 3: SHELL environment variable | ||
| 17 | * 4: value of shell in /etc/passwd | ||
| 18 | * 5: value of shell in config.h | ||
| 19 | */ | ||
| 20 | static char *shell = "/bin/sh"; | ||
| 21 | char *utmp = NULL; | ||
| 22 | /* scroll program: to enable use a string like "scroll" */ | ||
| 23 | char *scroll = NULL; | ||
| 24 | char *stty_args = "stty raw pass8 nl -echo -iexten -cstopb 38400"; | ||
| 25 | |||
| 26 | /* identification sequence returned in DA and DECID */ | ||
| 27 | char *vtiden = "\033[?6c"; | ||
| 28 | |||
| 29 | /* Kerning / character bounding-box multipliers */ | ||
| 30 | static float cwscale = 1.0; | ||
| 31 | static float chscale = 1.0; | ||
| 32 | |||
| 33 | /* | ||
| 34 | * word delimiter string | ||
| 35 | * | ||
| 36 | * More advanced example: L" `'\"()[]{}" | ||
| 37 | */ | ||
| 38 | wchar_t *worddelimiters = L" "; | ||
| 39 | |||
| 40 | /* selection timeouts (in milliseconds) */ | ||
| 41 | static unsigned int doubleclicktimeout = 300; | ||
| 42 | static unsigned int tripleclicktimeout = 600; | ||
| 43 | |||
| 44 | /* alt screens */ | ||
| 45 | int allowaltscreen = 1; | ||
| 46 | |||
| 47 | /* allow certain non-interactive (insecure) window operations such as: | ||
| 48 | setting the clipboard text */ | ||
| 49 | int allowwindowops = 0; | ||
| 50 | |||
| 51 | /* | ||
| 52 | * draw latency range in ms - from new content/keypress/etc until drawing. | ||
| 53 | * within this range, st draws when content stops arriving (idle). mostly it's | ||
| 54 | * near minlatency, but it waits longer for slow updates to avoid partial draw. | ||
| 55 | * low minlatency will tear/flicker more, as it can "detect" idle too early. | ||
| 56 | */ | ||
| 57 | static double minlatency = 8; | ||
| 58 | static double maxlatency = 33; | ||
| 59 | |||
| 60 | /* | ||
| 61 | * blinking timeout (set to 0 to disable blinking) for the terminal blinking | ||
| 62 | * attribute. | ||
| 63 | */ | ||
| 64 | static unsigned int blinktimeout = 800; | ||
| 65 | |||
| 66 | /* | ||
| 67 | * thickness of underline and bar cursors | ||
| 68 | */ | ||
| 69 | static unsigned int cursorthickness = 2; | ||
| 70 | |||
| 71 | /* | ||
| 72 | * 1: render most of the lines/blocks characters without using the font for | ||
| 73 | * perfect alignment between cells (U2500 - U259F except dashes/diagonals). | ||
| 74 | * Bold affects lines thickness if boxdraw_bold is not 0. Italic is ignored. | ||
| 75 | * 0: disable (render all U25XX glyphs normally from the font). | ||
| 76 | */ | ||
| 77 | const int boxdraw = 1; | ||
| 78 | const int boxdraw_bold = 0; | ||
| 79 | |||
| 80 | /* braille (U28XX): 1: render as adjacent "pixels", 0: use font */ | ||
| 81 | const int boxdraw_braille = 0; | ||
| 82 | |||
| 83 | /* | ||
| 84 | * bell volume. It must be a value between -100 and 100. Use 0 for disabling | ||
| 85 | * it | ||
| 86 | */ | ||
| 87 | static int bellvolume = 0; | ||
| 88 | |||
| 89 | /* default TERM value */ | ||
| 90 | char *termname = "st-256color"; | ||
| 91 | |||
| 92 | /* | ||
| 93 | * spaces per tab | ||
| 94 | * | ||
| 95 | * When you are changing this value, don't forget to adapt the »it« value in | ||
| 96 | * the st.info and appropriately install the st.info in the environment where | ||
| 97 | * you use this st version. | ||
| 98 | * | ||
| 99 | * it#$tabspaces, | ||
| 100 | * | ||
| 101 | * Secondly make sure your kernel is not expanding tabs. When running `stty | ||
| 102 | * -a` »tab0« should appear. You can tell the terminal to not expand tabs by | ||
| 103 | * running following command: | ||
| 104 | * | ||
| 105 | * stty tabs | ||
| 106 | */ | ||
| 107 | unsigned int tabspaces = 8; | ||
| 108 | |||
| 109 | /* bg opacity */ | ||
| 110 | float alpha = 0.8; | ||
| 111 | float alphaOffset = 0.0; | ||
| 112 | float alphaUnfocus; | ||
| 113 | |||
| 114 | /* Terminal colors (16 first used in escape sequence) */ | ||
| 115 | static const char *colorname[] = { | ||
| 116 | "#282828", /* hard contrast: #1d2021 / soft contrast: #32302f */ | ||
| 117 | "#cc241d", | ||
| 118 | "#98971a", | ||
| 119 | "#d79921", | ||
| 120 | "#458588", | ||
| 121 | "#b16286", | ||
| 122 | "#689d6a", | ||
| 123 | "#a89984", | ||
| 124 | "#928374", | ||
| 125 | "#fb4934", | ||
| 126 | "#b8bb26", | ||
| 127 | "#fabd2f", | ||
| 128 | "#83a598", | ||
| 129 | "#d3869b", | ||
| 130 | "#8ec07c", | ||
| 131 | "#ebdbb2", | ||
| 132 | [255] = 0, | ||
| 133 | /* more colors can be added after 255 to use with DefaultXX */ | ||
| 134 | "#add8e6", /* 256 -> cursor */ | ||
| 135 | "#555555", /* 257 -> rev cursor*/ | ||
| 136 | "#282828", /* 258 -> bg */ | ||
| 137 | "#ebdbb2", /* 259 -> fg */ | ||
| 138 | }; | ||
| 139 | |||
| 140 | |||
| 141 | /* | ||
| 142 | * Default colors (colorname index) | ||
| 143 | * foreground, background, cursor, reverse cursor | ||
| 144 | */ | ||
| 145 | unsigned int defaultfg = 259; | ||
| 146 | unsigned int defaultbg = 258; | ||
| 147 | unsigned int defaultcs = 256; | ||
| 148 | unsigned int defaultrcs = 257; | ||
| 149 | unsigned int background = 258; | ||
| 150 | |||
| 151 | /* | ||
| 152 | * Default shape of cursor | ||
| 153 | * 2: Block ("█") | ||
| 154 | * 4: Underline ("_") | ||
| 155 | * 6: Bar ("|") | ||
| 156 | * 7: Snowman ("☃") | ||
| 157 | */ | ||
| 158 | static unsigned int cursorshape = 2; | ||
| 159 | |||
| 160 | /* | ||
| 161 | * Default columns and rows numbers | ||
| 162 | */ | ||
| 163 | |||
| 164 | static unsigned int cols = 80; | ||
| 165 | static unsigned int rows = 24; | ||
| 166 | |||
| 167 | /* | ||
| 168 | * Default colour and shape of the mouse cursor | ||
| 169 | */ | ||
| 170 | static unsigned int mouseshape = XC_xterm; | ||
| 171 | static unsigned int mousefg = 7; | ||
| 172 | static unsigned int mousebg = 0; | ||
| 173 | |||
| 174 | /* | ||
| 175 | * Color used to display font attributes when fontconfig selected a font which | ||
| 176 | * doesn't match the ones requested. | ||
| 177 | */ | ||
| 178 | static unsigned int defaultattr = 11; | ||
| 179 | |||
| 180 | /* | ||
| 181 | * Force mouse select/shortcuts while mask is active (when MODE_MOUSE is set). | ||
| 182 | * Note that if you want to use ShiftMask with selmasks, set this to an other | ||
| 183 | * modifier, set to 0 to not use it. | ||
| 184 | */ | ||
| 185 | static uint forcemousemod = ShiftMask; | ||
| 186 | |||
| 187 | /* | ||
| 188 | * Xresources preferences to load at startup | ||
| 189 | */ | ||
| 190 | ResourcePref resources[] = { | ||
| 191 | { "font", STRING, &font }, | ||
| 192 | { "fontalt0", STRING, &font2[0] }, | ||
| 193 | { "color0", STRING, &colorname[0] }, | ||
| 194 | { "color1", STRING, &colorname[1] }, | ||
| 195 | { "color2", STRING, &colorname[2] }, | ||
| 196 | { "color3", STRING, &colorname[3] }, | ||
| 197 | { "color4", STRING, &colorname[4] }, | ||
| 198 | { "color5", STRING, &colorname[5] }, | ||
| 199 | { "color6", STRING, &colorname[6] }, | ||
| 200 | { "color7", STRING, &colorname[7] }, | ||
| 201 | { "color8", STRING, &colorname[8] }, | ||
| 202 | { "color9", STRING, &colorname[9] }, | ||
| 203 | { "color10", STRING, &colorname[10] }, | ||
| 204 | { "color11", STRING, &colorname[11] }, | ||
| 205 | { "color12", STRING, &colorname[12] }, | ||
| 206 | { "color13", STRING, &colorname[13] }, | ||
| 207 | { "color14", STRING, &colorname[14] }, | ||
| 208 | { "color15", STRING, &colorname[15] }, | ||
| 209 | { "background", STRING, &colorname[258] }, | ||
| 210 | { "foreground", STRING, &colorname[259] }, | ||
| 211 | { "cursorColor", STRING, &colorname[256] }, | ||
| 212 | { "termname", STRING, &termname }, | ||
| 213 | { "shell", STRING, &shell }, | ||
| 214 | { "minlatency", INTEGER, &minlatency }, | ||
| 215 | { "maxlatency", INTEGER, &maxlatency }, | ||
| 216 | { "blinktimeout", INTEGER, &blinktimeout }, | ||
| 217 | { "bellvolume", INTEGER, &bellvolume }, | ||
| 218 | { "tabspaces", INTEGER, &tabspaces }, | ||
| 219 | { "borderpx", INTEGER, &borderpx }, | ||
| 220 | { "cwscale", FLOAT, &cwscale }, | ||
| 221 | { "chscale", FLOAT, &chscale }, | ||
| 222 | { "alpha", FLOAT, &alpha }, | ||
| 223 | { "alphaOffset", FLOAT, &alphaOffset }, | ||
| 224 | }; | ||
| 225 | |||
| 226 | /* | ||
| 227 | * Internal mouse shortcuts. | ||
| 228 | * Beware that overloading Button1 will disable the selection. | ||
| 229 | */ | ||
| 230 | static MouseShortcut mshortcuts[] = { | ||
| 231 | /* mask button function argument release */ | ||
| 232 | { XK_NO_MOD, Button4, kscrollup, {.i = 1} }, | ||
| 233 | { XK_NO_MOD, Button5, kscrolldown, {.i = 1} }, | ||
| 234 | { XK_ANY_MOD, Button2, selpaste, {.i = 0}, 1 }, | ||
| 235 | { ShiftMask, Button4, ttysend, {.s = "\033[5;2~"} }, | ||
| 236 | { XK_ANY_MOD, Button4, ttysend, {.s = "\031"} }, | ||
| 237 | { ShiftMask, Button5, ttysend, {.s = "\033[6;2~"} }, | ||
| 238 | { XK_ANY_MOD, Button5, ttysend, {.s = "\005"} }, | ||
| 239 | }; | ||
| 240 | |||
| 241 | /* Internal keyboard shortcuts. */ | ||
| 242 | #define MODKEY Mod1Mask | ||
| 243 | #define TERMMOD (Mod1Mask|ShiftMask) | ||
| 244 | |||
| 245 | static char *openurlcmd[] = { "/bin/sh", "-c", "st-urlhandler -o", "externalpipe", NULL }; | ||
| 246 | static char *copyurlcmd[] = { "/bin/sh", "-c", "st-urlhandler -c", "externalpipe", NULL }; | ||
| 247 | static char *copyoutput[] = { "/bin/sh", "-c", "st-copyout", "externalpipe", NULL }; | ||
| 248 | |||
| 249 | static Shortcut shortcuts[] = { | ||
| 250 | /* mask keysym function argument */ | ||
| 251 | { XK_ANY_MOD, XK_Break, sendbreak, {.i = 0} }, | ||
| 252 | { ControlMask, XK_Print, toggleprinter, {.i = 0} }, | ||
| 253 | { ShiftMask, XK_Print, printscreen, {.i = 0} }, | ||
| 254 | { XK_ANY_MOD, XK_Print, printsel, {.i = 0} }, | ||
| 255 | { TERMMOD, XK_Prior, zoom, {.f = +1} }, | ||
| 256 | { TERMMOD, XK_Next, zoom, {.f = -1} }, | ||
| 257 | { TERMMOD, XK_Home, zoomreset, {.f = 0} }, | ||
| 258 | { TERMMOD, XK_C, clipcopy, {.i = 0} }, | ||
| 259 | { TERMMOD, XK_V, clippaste, {.i = 0} }, | ||
| 260 | { MODKEY, XK_c, clipcopy, {.i = 0} }, | ||
| 261 | { ShiftMask, XK_Insert, clippaste, {.i = 0} }, | ||
| 262 | { MODKEY, XK_v, clippaste, {.i = 0} }, | ||
| 263 | { ShiftMask, XK_Insert, selpaste, {.i = 0} }, | ||
| 264 | { TERMMOD, XK_Num_Lock, numlock, {.i = 0} }, | ||
| 265 | { ShiftMask, XK_Page_Up, kscrollup, {.i = -1} }, | ||
| 266 | { ShiftMask, XK_Page_Down, kscrolldown, {.i = -1} }, | ||
| 267 | { MODKEY, XK_Page_Up, kscrollup, {.i = -1} }, | ||
| 268 | { MODKEY, XK_Page_Down, kscrolldown, {.i = -1} }, | ||
| 269 | { MODKEY, XK_k, kscrollup, {.i = 1} }, | ||
| 270 | { MODKEY, XK_j, kscrolldown, {.i = 1} }, | ||
| 271 | { MODKEY, XK_Up, kscrollup, {.i = 1} }, | ||
| 272 | { MODKEY, XK_Down, kscrolldown, {.i = 1} }, | ||
| 273 | { MODKEY, XK_u, kscrollup, {.i = -1} }, | ||
| 274 | { MODKEY, XK_d, kscrolldown, {.i = -1} }, | ||
| 275 | { MODKEY, XK_s, changealpha, {.f = -0.05} }, | ||
| 276 | { MODKEY, XK_a, changealpha, {.f = +0.05} }, | ||
| 277 | { TERMMOD, XK_Up, zoom, {.f = +1} }, | ||
| 278 | { TERMMOD, XK_Down, zoom, {.f = -1} }, | ||
| 279 | { TERMMOD, XK_K, zoom, {.f = +1} }, | ||
| 280 | { TERMMOD, XK_J, zoom, {.f = -1} }, | ||
| 281 | { TERMMOD, XK_U, zoom, {.f = +2} }, | ||
| 282 | { TERMMOD, XK_D, zoom, {.f = -2} }, | ||
| 283 | { MODKEY, XK_l, externalpipe, {.v = openurlcmd } }, | ||
| 284 | { MODKEY, XK_y, externalpipe, {.v = copyurlcmd } }, | ||
| 285 | { MODKEY, XK_o, externalpipe, {.v = copyoutput } }, | ||
| 286 | }; | ||
| 287 | |||
| 288 | /* | ||
| 289 | * Special keys (change & recompile st.info accordingly) | ||
| 290 | * | ||
| 291 | * Mask value: | ||
| 292 | * * Use XK_ANY_MOD to match the key no matter modifiers state | ||
| 293 | * * Use XK_NO_MOD to match the key alone (no modifiers) | ||
| 294 | * appkey value: | ||
| 295 | * * 0: no value | ||
| 296 | * * > 0: keypad application mode enabled | ||
| 297 | * * = 2: term.numlock = 1 | ||
| 298 | * * < 0: keypad application mode disabled | ||
| 299 | * appcursor value: | ||
| 300 | * * 0: no value | ||
| 301 | * * > 0: cursor application mode enabled | ||
| 302 | * * < 0: cursor application mode disabled | ||
| 303 | * | ||
| 304 | * Be careful with the order of the definitions because st searches in | ||
| 305 | * this table sequentially, so any XK_ANY_MOD must be in the last | ||
| 306 | * position for a key. | ||
| 307 | */ | ||
| 308 | |||
| 309 | /* | ||
| 310 | * If you want keys other than the X11 function keys (0xFD00 - 0xFFFF) | ||
| 311 | * to be mapped below, add them to this array. | ||
| 312 | */ | ||
| 313 | static KeySym mappedkeys[] = { -1 }; | ||
| 314 | |||
| 315 | /* | ||
| 316 | * State bits to ignore when matching key or button events. By default, | ||
| 317 | * numlock (Mod2Mask) and keyboard layout (XK_SWITCH_MOD) are ignored. | ||
| 318 | */ | ||
| 319 | static uint ignoremod = Mod2Mask|XK_SWITCH_MOD; | ||
| 320 | |||
| 321 | /* | ||
| 322 | * This is the huge key array which defines all compatibility to the Linux | ||
| 323 | * world. Please decide about changes wisely. | ||
| 324 | */ | ||
| 325 | static Key key[] = { | ||
| 326 | /* keysym mask string appkey appcursor */ | ||
| 327 | { XK_KP_Home, ShiftMask, "\033[2J", 0, -1}, | ||
| 328 | { XK_KP_Home, ShiftMask, "\033[1;2H", 0, +1}, | ||
| 329 | { XK_KP_Home, XK_ANY_MOD, "\033[H", 0, -1}, | ||
| 330 | { XK_KP_Home, XK_ANY_MOD, "\033[1~", 0, +1}, | ||
| 331 | { XK_KP_Up, XK_ANY_MOD, "\033Ox", +1, 0}, | ||
| 332 | { XK_KP_Up, XK_ANY_MOD, "\033[A", 0, -1}, | ||
| 333 | { XK_KP_Up, XK_ANY_MOD, "\033OA", 0, +1}, | ||
| 334 | { XK_KP_Down, XK_ANY_MOD, "\033Or", +1, 0}, | ||
| 335 | { XK_KP_Down, XK_ANY_MOD, "\033[B", 0, -1}, | ||
| 336 | { XK_KP_Down, XK_ANY_MOD, "\033OB", 0, +1}, | ||
| 337 | { XK_KP_Left, XK_ANY_MOD, "\033Ot", +1, 0}, | ||
| 338 | { XK_KP_Left, XK_ANY_MOD, "\033[D", 0, -1}, | ||
| 339 | { XK_KP_Left, XK_ANY_MOD, "\033OD", 0, +1}, | ||
| 340 | { XK_KP_Right, XK_ANY_MOD, "\033Ov", +1, 0}, | ||
| 341 | { XK_KP_Right, XK_ANY_MOD, "\033[C", 0, -1}, | ||
| 342 | { XK_KP_Right, XK_ANY_MOD, "\033OC", 0, +1}, | ||
| 343 | { XK_KP_Prior, ShiftMask, "\033[5;2~", 0, 0}, | ||
| 344 | { XK_KP_Prior, XK_ANY_MOD, "\033[5~", 0, 0}, | ||
| 345 | { XK_KP_Begin, XK_ANY_MOD, "\033[E", 0, 0}, | ||
| 346 | { XK_KP_End, ControlMask, "\033[J", -1, 0}, | ||
| 347 | { XK_KP_End, ControlMask, "\033[1;5F", +1, 0}, | ||
| 348 | { XK_KP_End, ShiftMask, "\033[K", -1, 0}, | ||
| 349 | { XK_KP_End, ShiftMask, "\033[1;2F", +1, 0}, | ||
| 350 | { XK_KP_End, XK_ANY_MOD, "\033[4~", 0, 0}, | ||
| 351 | { XK_KP_Next, ShiftMask, "\033[6;2~", 0, 0}, | ||
| 352 | { XK_KP_Next, XK_ANY_MOD, "\033[6~", 0, 0}, | ||
| 353 | { XK_KP_Insert, ShiftMask, "\033[2;2~", +1, 0}, | ||
| 354 | { XK_KP_Insert, ShiftMask, "\033[4l", -1, 0}, | ||
| 355 | { XK_KP_Insert, ControlMask, "\033[L", -1, 0}, | ||
| 356 | { XK_KP_Insert, ControlMask, "\033[2;5~", +1, 0}, | ||
| 357 | { XK_KP_Insert, XK_ANY_MOD, "\033[4h", -1, 0}, | ||
| 358 | { XK_KP_Insert, XK_ANY_MOD, "\033[2~", +1, 0}, | ||
| 359 | { XK_KP_Delete, ControlMask, "\033[M", -1, 0}, | ||
| 360 | { XK_KP_Delete, ControlMask, "\033[3;5~", +1, 0}, | ||
| 361 | { XK_KP_Delete, ShiftMask, "\033[2K", -1, 0}, | ||
| 362 | { XK_KP_Delete, ShiftMask, "\033[3;2~", +1, 0}, | ||
| 363 | { XK_KP_Delete, XK_ANY_MOD, "\033[P", -1, 0}, | ||
| 364 | { XK_KP_Delete, XK_ANY_MOD, "\033[3~", +1, 0}, | ||
| 365 | { XK_KP_Multiply, XK_ANY_MOD, "\033Oj", +2, 0}, | ||
| 366 | { XK_KP_Add, XK_ANY_MOD, "\033Ok", +2, 0}, | ||
| 367 | { XK_KP_Enter, XK_ANY_MOD, "\033OM", +2, 0}, | ||
| 368 | { XK_KP_Enter, XK_ANY_MOD, "\r", -1, 0}, | ||
| 369 | { XK_KP_Subtract, XK_ANY_MOD, "\033Om", +2, 0}, | ||
| 370 | { XK_KP_Decimal, XK_ANY_MOD, "\033On", +2, 0}, | ||
| 371 | { XK_KP_Divide, XK_ANY_MOD, "\033Oo", +2, 0}, | ||
| 372 | { XK_KP_0, XK_ANY_MOD, "\033Op", +2, 0}, | ||
| 373 | { XK_KP_1, XK_ANY_MOD, "\033Oq", +2, 0}, | ||
| 374 | { XK_KP_2, XK_ANY_MOD, "\033Or", +2, 0}, | ||
| 375 | { XK_KP_3, XK_ANY_MOD, "\033Os", +2, 0}, | ||
| 376 | { XK_KP_4, XK_ANY_MOD, "\033Ot", +2, 0}, | ||
| 377 | { XK_KP_5, XK_ANY_MOD, "\033Ou", +2, 0}, | ||
| 378 | { XK_KP_6, XK_ANY_MOD, "\033Ov", +2, 0}, | ||
| 379 | { XK_KP_7, XK_ANY_MOD, "\033Ow", +2, 0}, | ||
| 380 | { XK_KP_8, XK_ANY_MOD, "\033Ox", +2, 0}, | ||
| 381 | { XK_KP_9, XK_ANY_MOD, "\033Oy", +2, 0}, | ||
| 382 | { XK_Up, ShiftMask, "\033[1;2A", 0, 0}, | ||
| 383 | { XK_Up, Mod1Mask, "\033[1;3A", 0, 0}, | ||
| 384 | { XK_Up, ShiftMask|Mod1Mask,"\033[1;4A", 0, 0}, | ||
| 385 | { XK_Up, ControlMask, "\033[1;5A", 0, 0}, | ||
| 386 | { XK_Up, ShiftMask|ControlMask,"\033[1;6A", 0, 0}, | ||
| 387 | { XK_Up, ControlMask|Mod1Mask,"\033[1;7A", 0, 0}, | ||
| 388 | { XK_Up,ShiftMask|ControlMask|Mod1Mask,"\033[1;8A", 0, 0}, | ||
| 389 | { XK_Up, XK_ANY_MOD, "\033[A", 0, -1}, | ||
| 390 | { XK_Up, XK_ANY_MOD, "\033OA", 0, +1}, | ||
| 391 | { XK_Down, ShiftMask, "\033[1;2B", 0, 0}, | ||
| 392 | { XK_Down, Mod1Mask, "\033[1;3B", 0, 0}, | ||
| 393 | { XK_Down, ShiftMask|Mod1Mask,"\033[1;4B", 0, 0}, | ||
| 394 | { XK_Down, ControlMask, "\033[1;5B", 0, 0}, | ||
| 395 | { XK_Down, ShiftMask|ControlMask,"\033[1;6B", 0, 0}, | ||
| 396 | { XK_Down, ControlMask|Mod1Mask,"\033[1;7B", 0, 0}, | ||
| 397 | { XK_Down,ShiftMask|ControlMask|Mod1Mask,"\033[1;8B",0, 0}, | ||
| 398 | { XK_Down, XK_ANY_MOD, "\033[B", 0, -1}, | ||
| 399 | { XK_Down, XK_ANY_MOD, "\033OB", 0, +1}, | ||
| 400 | { XK_Left, ShiftMask, "\033[1;2D", 0, 0}, | ||
| 401 | { XK_Left, Mod1Mask, "\033[1;3D", 0, 0}, | ||
| 402 | { XK_Left, ShiftMask|Mod1Mask,"\033[1;4D", 0, 0}, | ||
| 403 | { XK_Left, ControlMask, "\033[1;5D", 0, 0}, | ||
| 404 | { XK_Left, ShiftMask|ControlMask,"\033[1;6D", 0, 0}, | ||
| 405 | { XK_Left, ControlMask|Mod1Mask,"\033[1;7D", 0, 0}, | ||
| 406 | { XK_Left,ShiftMask|ControlMask|Mod1Mask,"\033[1;8D",0, 0}, | ||
| 407 | { XK_Left, XK_ANY_MOD, "\033[D", 0, -1}, | ||
| 408 | { XK_Left, XK_ANY_MOD, "\033OD", 0, +1}, | ||
| 409 | { XK_Right, ShiftMask, "\033[1;2C", 0, 0}, | ||
| 410 | { XK_Right, Mod1Mask, "\033[1;3C", 0, 0}, | ||
| 411 | { XK_Right, ShiftMask|Mod1Mask,"\033[1;4C", 0, 0}, | ||
| 412 | { XK_Right, ControlMask, "\033[1;5C", 0, 0}, | ||
| 413 | { XK_Right, ShiftMask|ControlMask,"\033[1;6C", 0, 0}, | ||
| 414 | { XK_Right, ControlMask|Mod1Mask,"\033[1;7C", 0, 0}, | ||
| 415 | { XK_Right,ShiftMask|ControlMask|Mod1Mask,"\033[1;8C",0, 0}, | ||
| 416 | { XK_Right, XK_ANY_MOD, "\033[C", 0, -1}, | ||
| 417 | { XK_Right, XK_ANY_MOD, "\033OC", 0, +1}, | ||
| 418 | { XK_ISO_Left_Tab, ShiftMask, "\033[Z", 0, 0}, | ||
| 419 | { XK_Return, Mod1Mask, "\033\r", 0, 0}, | ||
| 420 | { XK_Return, XK_ANY_MOD, "\r", 0, 0}, | ||
| 421 | { XK_Insert, ShiftMask, "\033[4l", -1, 0}, | ||
| 422 | { XK_Insert, ShiftMask, "\033[2;2~", +1, 0}, | ||
| 423 | { XK_Insert, ControlMask, "\033[L", -1, 0}, | ||
| 424 | { XK_Insert, ControlMask, "\033[2;5~", +1, 0}, | ||
| 425 | { XK_Insert, XK_ANY_MOD, "\033[4h", -1, 0}, | ||
| 426 | { XK_Insert, XK_ANY_MOD, "\033[2~", +1, 0}, | ||
| 427 | { XK_Delete, ControlMask, "\033[M", -1, 0}, | ||
| 428 | { XK_Delete, ControlMask, "\033[3;5~", +1, 0}, | ||
| 429 | { XK_Delete, ShiftMask, "\033[2K", -1, 0}, | ||
| 430 | { XK_Delete, ShiftMask, "\033[3;2~", +1, 0}, | ||
| 431 | { XK_Delete, XK_ANY_MOD, "\033[P", -1, 0}, | ||
| 432 | { XK_Delete, XK_ANY_MOD, "\033[3~", +1, 0}, | ||
| 433 | { XK_BackSpace, XK_NO_MOD, "\177", 0, 0}, | ||
| 434 | { XK_BackSpace, Mod1Mask, "\033\177", 0, 0}, | ||
| 435 | { XK_Home, ShiftMask, "\033[2J", 0, -1}, | ||
| 436 | { XK_Home, ShiftMask, "\033[1;2H", 0, +1}, | ||
| 437 | { XK_Home, XK_ANY_MOD, "\033[H", 0, -1}, | ||
| 438 | { XK_Home, XK_ANY_MOD, "\033[1~", 0, +1}, | ||
| 439 | { XK_End, ControlMask, "\033[J", -1, 0}, | ||
| 440 | { XK_End, ControlMask, "\033[1;5F", +1, 0}, | ||
| 441 | { XK_End, ShiftMask, "\033[K", -1, 0}, | ||
| 442 | { XK_End, ShiftMask, "\033[1;2F", +1, 0}, | ||
| 443 | { XK_End, XK_ANY_MOD, "\033[4~", 0, 0}, | ||
| 444 | { XK_Prior, ControlMask, "\033[5;5~", 0, 0}, | ||
| 445 | { XK_Prior, ShiftMask, "\033[5;2~", 0, 0}, | ||
| 446 | { XK_Prior, XK_ANY_MOD, "\033[5~", 0, 0}, | ||
| 447 | { XK_Next, ControlMask, "\033[6;5~", 0, 0}, | ||
| 448 | { XK_Next, ShiftMask, "\033[6;2~", 0, 0}, | ||
| 449 | { XK_Next, XK_ANY_MOD, "\033[6~", 0, 0}, | ||
| 450 | { XK_F1, XK_NO_MOD, "\033OP" , 0, 0}, | ||
| 451 | { XK_F1, /* F13 */ ShiftMask, "\033[1;2P", 0, 0}, | ||
| 452 | { XK_F1, /* F25 */ ControlMask, "\033[1;5P", 0, 0}, | ||
| 453 | { XK_F1, /* F37 */ Mod4Mask, "\033[1;6P", 0, 0}, | ||
| 454 | { XK_F1, /* F49 */ Mod1Mask, "\033[1;3P", 0, 0}, | ||
| 455 | { XK_F1, /* F61 */ Mod3Mask, "\033[1;4P", 0, 0}, | ||
| 456 | { XK_F2, XK_NO_MOD, "\033OQ" , 0, 0}, | ||
| 457 | { XK_F2, /* F14 */ ShiftMask, "\033[1;2Q", 0, 0}, | ||
| 458 | { XK_F2, /* F26 */ ControlMask, "\033[1;5Q", 0, 0}, | ||
| 459 | { XK_F2, /* F38 */ Mod4Mask, "\033[1;6Q", 0, 0}, | ||
| 460 | { XK_F2, /* F50 */ Mod1Mask, "\033[1;3Q", 0, 0}, | ||
| 461 | { XK_F2, /* F62 */ Mod3Mask, "\033[1;4Q", 0, 0}, | ||
| 462 | { XK_F3, XK_NO_MOD, "\033OR" , 0, 0}, | ||
| 463 | { XK_F3, /* F15 */ ShiftMask, "\033[1;2R", 0, 0}, | ||
| 464 | { XK_F3, /* F27 */ ControlMask, "\033[1;5R", 0, 0}, | ||
| 465 | { XK_F3, /* F39 */ Mod4Mask, "\033[1;6R", 0, 0}, | ||
| 466 | { XK_F3, /* F51 */ Mod1Mask, "\033[1;3R", 0, 0}, | ||
| 467 | { XK_F3, /* F63 */ Mod3Mask, "\033[1;4R", 0, 0}, | ||
| 468 | { XK_F4, XK_NO_MOD, "\033OS" , 0, 0}, | ||
| 469 | { XK_F4, /* F16 */ ShiftMask, "\033[1;2S", 0, 0}, | ||
| 470 | { XK_F4, /* F28 */ ControlMask, "\033[1;5S", 0, 0}, | ||
| 471 | { XK_F4, /* F40 */ Mod4Mask, "\033[1;6S", 0, 0}, | ||
| 472 | { XK_F4, /* F52 */ Mod1Mask, "\033[1;3S", 0, 0}, | ||
| 473 | { XK_F5, XK_NO_MOD, "\033[15~", 0, 0}, | ||
| 474 | { XK_F5, /* F17 */ ShiftMask, "\033[15;2~", 0, 0}, | ||
| 475 | { XK_F5, /* F29 */ ControlMask, "\033[15;5~", 0, 0}, | ||
| 476 | { XK_F5, /* F41 */ Mod4Mask, "\033[15;6~", 0, 0}, | ||
| 477 | { XK_F5, /* F53 */ Mod1Mask, "\033[15;3~", 0, 0}, | ||
| 478 | { XK_F6, XK_NO_MOD, "\033[17~", 0, 0}, | ||
| 479 | { XK_F6, /* F18 */ ShiftMask, "\033[17;2~", 0, 0}, | ||
| 480 | { XK_F6, /* F30 */ ControlMask, "\033[17;5~", 0, 0}, | ||
| 481 | { XK_F6, /* F42 */ Mod4Mask, "\033[17;6~", 0, 0}, | ||
| 482 | { XK_F6, /* F54 */ Mod1Mask, "\033[17;3~", 0, 0}, | ||
| 483 | { XK_F7, XK_NO_MOD, "\033[18~", 0, 0}, | ||
| 484 | { XK_F7, /* F19 */ ShiftMask, "\033[18;2~", 0, 0}, | ||
| 485 | { XK_F7, /* F31 */ ControlMask, "\033[18;5~", 0, 0}, | ||
| 486 | { XK_F7, /* F43 */ Mod4Mask, "\033[18;6~", 0, 0}, | ||
| 487 | { XK_F7, /* F55 */ Mod1Mask, "\033[18;3~", 0, 0}, | ||
| 488 | { XK_F8, XK_NO_MOD, "\033[19~", 0, 0}, | ||
| 489 | { XK_F8, /* F20 */ ShiftMask, "\033[19;2~", 0, 0}, | ||
| 490 | { XK_F8, /* F32 */ ControlMask, "\033[19;5~", 0, 0}, | ||
| 491 | { XK_F8, /* F44 */ Mod4Mask, "\033[19;6~", 0, 0}, | ||
| 492 | { XK_F8, /* F56 */ Mod1Mask, "\033[19;3~", 0, 0}, | ||
| 493 | { XK_F9, XK_NO_MOD, "\033[20~", 0, 0}, | ||
| 494 | { XK_F9, /* F21 */ ShiftMask, "\033[20;2~", 0, 0}, | ||
| 495 | { XK_F9, /* F33 */ ControlMask, "\033[20;5~", 0, 0}, | ||
| 496 | { XK_F9, /* F45 */ Mod4Mask, "\033[20;6~", 0, 0}, | ||
| 497 | { XK_F9, /* F57 */ Mod1Mask, "\033[20;3~", 0, 0}, | ||
| 498 | { XK_F10, XK_NO_MOD, "\033[21~", 0, 0}, | ||
| 499 | { XK_F10, /* F22 */ ShiftMask, "\033[21;2~", 0, 0}, | ||
| 500 | { XK_F10, /* F34 */ ControlMask, "\033[21;5~", 0, 0}, | ||
| 501 | { XK_F10, /* F46 */ Mod4Mask, "\033[21;6~", 0, 0}, | ||
| 502 | { XK_F10, /* F58 */ Mod1Mask, "\033[21;3~", 0, 0}, | ||
| 503 | { XK_F11, XK_NO_MOD, "\033[23~", 0, 0}, | ||
| 504 | { XK_F11, /* F23 */ ShiftMask, "\033[23;2~", 0, 0}, | ||
| 505 | { XK_F11, /* F35 */ ControlMask, "\033[23;5~", 0, 0}, | ||
| 506 | { XK_F11, /* F47 */ Mod4Mask, "\033[23;6~", 0, 0}, | ||
| 507 | { XK_F11, /* F59 */ Mod1Mask, "\033[23;3~", 0, 0}, | ||
| 508 | { XK_F12, XK_NO_MOD, "\033[24~", 0, 0}, | ||
| 509 | { XK_F12, /* F24 */ ShiftMask, "\033[24;2~", 0, 0}, | ||
| 510 | { XK_F12, /* F36 */ ControlMask, "\033[24;5~", 0, 0}, | ||
| 511 | { XK_F12, /* F48 */ Mod4Mask, "\033[24;6~", 0, 0}, | ||
| 512 | { XK_F12, /* F60 */ Mod1Mask, "\033[24;3~", 0, 0}, | ||
| 513 | { XK_F13, XK_NO_MOD, "\033[1;2P", 0, 0}, | ||
| 514 | { XK_F14, XK_NO_MOD, "\033[1;2Q", 0, 0}, | ||
| 515 | { XK_F15, XK_NO_MOD, "\033[1;2R", 0, 0}, | ||
| 516 | { XK_F16, XK_NO_MOD, "\033[1;2S", 0, 0}, | ||
| 517 | { XK_F17, XK_NO_MOD, "\033[15;2~", 0, 0}, | ||
| 518 | { XK_F18, XK_NO_MOD, "\033[17;2~", 0, 0}, | ||
| 519 | { XK_F19, XK_NO_MOD, "\033[18;2~", 0, 0}, | ||
| 520 | { XK_F20, XK_NO_MOD, "\033[19;2~", 0, 0}, | ||
| 521 | { XK_F21, XK_NO_MOD, "\033[20;2~", 0, 0}, | ||
| 522 | { XK_F22, XK_NO_MOD, "\033[21;2~", 0, 0}, | ||
| 523 | { XK_F23, XK_NO_MOD, "\033[23;2~", 0, 0}, | ||
| 524 | { XK_F24, XK_NO_MOD, "\033[24;2~", 0, 0}, | ||
| 525 | { XK_F25, XK_NO_MOD, "\033[1;5P", 0, 0}, | ||
| 526 | { XK_F26, XK_NO_MOD, "\033[1;5Q", 0, 0}, | ||
| 527 | { XK_F27, XK_NO_MOD, "\033[1;5R", 0, 0}, | ||
| 528 | { XK_F28, XK_NO_MOD, "\033[1;5S", 0, 0}, | ||
| 529 | { XK_F29, XK_NO_MOD, "\033[15;5~", 0, 0}, | ||
| 530 | { XK_F30, XK_NO_MOD, "\033[17;5~", 0, 0}, | ||
| 531 | { XK_F31, XK_NO_MOD, "\033[18;5~", 0, 0}, | ||
| 532 | { XK_F32, XK_NO_MOD, "\033[19;5~", 0, 0}, | ||
| 533 | { XK_F33, XK_NO_MOD, "\033[20;5~", 0, 0}, | ||
| 534 | { XK_F34, XK_NO_MOD, "\033[21;5~", 0, 0}, | ||
| 535 | { XK_F35, XK_NO_MOD, "\033[23;5~", 0, 0}, | ||
| 536 | }; | ||
| 537 | |||
| 538 | /* | ||
| 539 | * Selection types' masks. | ||
| 540 | * Use the same masks as usual. | ||
| 541 | * Button1Mask is always unset, to make masks match between ButtonPress. | ||
| 542 | * ButtonRelease and MotionNotify. | ||
| 543 | * If no match is found, regular selection is used. | ||
| 544 | */ | ||
| 545 | static uint selmasks[] = { | ||
| 546 | [SEL_RECTANGULAR] = Mod1Mask, | ||
| 547 | }; | ||
| 548 | |||
| 549 | /* | ||
| 550 | * Printable characters in ASCII, used to estimate the advance width | ||
| 551 | * of single wide characters. | ||
| 552 | */ | ||
| 553 | static char ascii_printable[] = | ||
| 554 | " !\"#$%&'()*+,-./0123456789:;<=>?" | ||
| 555 | "@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_" | ||
| 556 | "`abcdefghijklmnopqrstuvwxyz{|}~"; | ||
| 557 | |||
diff --git a/config.mk b/config.mk new file mode 100644 index 0000000..ef6de39 --- /dev/null +++ b/config.mk | |||
| @@ -0,0 +1,37 @@ | |||
| 1 | # st version | ||
| 2 | VERSION = 0.8.5 | ||
| 3 | |||
| 4 | # Customize below to fit your system | ||
| 5 | |||
| 6 | # paths | ||
| 7 | PREFIX = /usr/local | ||
| 8 | MANPREFIX = $(PREFIX)/share/man | ||
| 9 | |||
| 10 | X11INC = /usr/X11R6/include | ||
| 11 | X11LIB = /usr/X11R6/lib | ||
| 12 | |||
| 13 | PKG_CONFIG = pkg-config | ||
| 14 | |||
| 15 | # includes and libs | ||
| 16 | INCS = -I$(X11INC) \ | ||
| 17 | `$(PKG_CONFIG) --cflags fontconfig` \ | ||
| 18 | `$(PKG_CONFIG) --cflags freetype2` \ | ||
| 19 | `$(PKG_CONFIG) --cflags harfbuzz` | ||
| 20 | LIBS = -L$(X11LIB) -lm -lrt -lX11 -lutil -lXft -lXrender\ | ||
| 21 | `$(PKG_CONFIG) --libs fontconfig` \ | ||
| 22 | `$(PKG_CONFIG) --libs freetype2` \ | ||
| 23 | `$(PKG_CONFIG) --libs harfbuzz` | ||
| 24 | |||
| 25 | # flags | ||
| 26 | STCPPFLAGS = -DVERSION=\"$(VERSION)\" -D_XOPEN_SOURCE=600 | ||
| 27 | STCFLAGS = $(INCS) $(STCPPFLAGS) $(CPPFLAGS) $(CFLAGS) | ||
| 28 | STLDFLAGS = $(LIBS) $(LDFLAGS) | ||
| 29 | |||
| 30 | # OpenBSD: | ||
| 31 | #CPPFLAGS = -DVERSION=\"$(VERSION)\" -D_XOPEN_SOURCE=600 -D_BSD_SOURCE | ||
| 32 | #LIBS = -L$(X11LIB) -lm -lX11 -lutil -lXft \ | ||
| 33 | # `$(PKG_CONFIG) --libs fontconfig` \ | ||
| 34 | # `$(PKG_CONFIG) --libs freetype2` | ||
| 35 | |||
| 36 | # compiler and linker | ||
| 37 | # CC = c99 | ||
| @@ -0,0 +1,154 @@ | |||
| 1 | #include <stdlib.h> | ||
| 2 | #include <stdio.h> | ||
| 3 | #include <math.h> | ||
| 4 | #include <X11/Xft/Xft.h> | ||
| 5 | #include <X11/cursorfont.h> | ||
| 6 | #include <hb.h> | ||
| 7 | #include <hb-ft.h> | ||
| 8 | |||
| 9 | #include "st.h" | ||
| 10 | |||
| 11 | #define FEATURE(c1,c2,c3,c4) { .tag = HB_TAG(c1,c2,c3,c4), .value = 1, .start = HB_FEATURE_GLOBAL_START, .end = HB_FEATURE_GLOBAL_END } | ||
| 12 | |||
| 13 | /* | ||
| 14 | * Replace 0 with a list of font features, wrapped in FEATURE macro, e.g. | ||
| 15 | * FEATURE('c', 'a', 'l', 't'), FEATURE('d', 'l', 'i', 'g') | ||
| 16 | * | ||
| 17 | * Uncomment either one of the 2 lines below. Uncomment the prior to disable (any) font features. Uncomment the | ||
| 18 | * latter to enable the (selected) font features. | ||
| 19 | */ | ||
| 20 | |||
| 21 | hb_feature_t features[] = { 0 }; | ||
| 22 | //hb_feature_t features[] = { FEATURE('s','s','0','1'), FEATURE('s','s','0','2'), FEATURE('s','s','0','3'), FEATURE('s','s','0','5'), FEATURE('s','s','0','6'), FEATURE('s','s','0','7'), FEATURE('s','s','0','8'), FEATURE('z','e','r','o') }; | ||
| 23 | |||
| 24 | void hbtransformsegment(XftFont *xfont, const Glyph *string, hb_codepoint_t *codepoints, int start, int length); | ||
| 25 | hb_font_t *hbfindfont(XftFont *match); | ||
| 26 | |||
| 27 | typedef struct { | ||
| 28 | XftFont *match; | ||
| 29 | hb_font_t *font; | ||
| 30 | } HbFontMatch; | ||
| 31 | |||
| 32 | static int hbfontslen = 0; | ||
| 33 | static HbFontMatch *hbfontcache = NULL; | ||
| 34 | |||
| 35 | void | ||
| 36 | hbunloadfonts() | ||
| 37 | { | ||
| 38 | for (int i = 0; i < hbfontslen; i++) { | ||
| 39 | hb_font_destroy(hbfontcache[i].font); | ||
| 40 | XftUnlockFace(hbfontcache[i].match); | ||
| 41 | } | ||
| 42 | |||
| 43 | if (hbfontcache != NULL) { | ||
| 44 | free(hbfontcache); | ||
| 45 | hbfontcache = NULL; | ||
| 46 | } | ||
| 47 | hbfontslen = 0; | ||
| 48 | } | ||
| 49 | |||
| 50 | hb_font_t * | ||
| 51 | hbfindfont(XftFont *match) | ||
| 52 | { | ||
| 53 | for (int i = 0; i < hbfontslen; i++) { | ||
| 54 | if (hbfontcache[i].match == match) | ||
| 55 | return hbfontcache[i].font; | ||
| 56 | } | ||
| 57 | |||
| 58 | /* Font not found in cache, caching it now. */ | ||
| 59 | hbfontcache = realloc(hbfontcache, sizeof(HbFontMatch) * (hbfontslen + 1)); | ||
| 60 | FT_Face face = XftLockFace(match); | ||
| 61 | hb_font_t *font = hb_ft_font_create(face, NULL); | ||
| 62 | if (font == NULL) | ||
| 63 | die("Failed to load Harfbuzz font."); | ||
| 64 | |||
| 65 | hbfontcache[hbfontslen].match = match; | ||
| 66 | hbfontcache[hbfontslen].font = font; | ||
| 67 | hbfontslen += 1; | ||
| 68 | |||
| 69 | return font; | ||
| 70 | } | ||
| 71 | |||
| 72 | void | ||
| 73 | hbtransform(XftGlyphFontSpec *specs, const Glyph *glyphs, size_t len, int x, int y) | ||
| 74 | { | ||
| 75 | int start = 0, length = 1, gstart = 0; | ||
| 76 | hb_codepoint_t *codepoints = calloc((unsigned int)len, sizeof(hb_codepoint_t)); | ||
| 77 | |||
| 78 | for (int idx = 1, specidx = 1; idx < len; idx++) { | ||
| 79 | if (glyphs[idx].mode & ATTR_WDUMMY) { | ||
| 80 | length += 1; | ||
| 81 | continue; | ||
| 82 | } | ||
| 83 | |||
| 84 | if (specs[specidx].font != specs[start].font || ATTRCMP(glyphs[gstart], glyphs[idx]) || selected(x + idx, y) != selected(x + gstart, y)) { | ||
| 85 | hbtransformsegment(specs[start].font, glyphs, codepoints, gstart, length); | ||
| 86 | |||
| 87 | /* Reset the sequence. */ | ||
| 88 | length = 1; | ||
| 89 | start = specidx; | ||
| 90 | gstart = idx; | ||
| 91 | } else { | ||
| 92 | length += 1; | ||
| 93 | } | ||
| 94 | |||
| 95 | specidx++; | ||
| 96 | } | ||
| 97 | |||
| 98 | /* EOL. */ | ||
| 99 | hbtransformsegment(specs[start].font, glyphs, codepoints, gstart, length); | ||
| 100 | |||
| 101 | /* Apply the transformation to glyph specs. */ | ||
| 102 | for (int i = 0, specidx = 0; i < len; i++) { | ||
| 103 | if (glyphs[i].mode & ATTR_WDUMMY) | ||
| 104 | continue; | ||
| 105 | if (glyphs[i].mode & ATTR_BOXDRAW) { | ||
| 106 | specidx++; | ||
| 107 | continue; | ||
| 108 | } | ||
| 109 | |||
| 110 | if (codepoints[i] != specs[specidx].glyph) | ||
| 111 | ((Glyph *)glyphs)[i].mode |= ATTR_LIGA; | ||
| 112 | |||
| 113 | specs[specidx++].glyph = codepoints[i]; | ||
| 114 | } | ||
| 115 | |||
| 116 | free(codepoints); | ||
| 117 | } | ||
| 118 | |||
| 119 | void | ||
| 120 | hbtransformsegment(XftFont *xfont, const Glyph *string, hb_codepoint_t *codepoints, int start, int length) | ||
| 121 | { | ||
| 122 | hb_font_t *font = hbfindfont(xfont); | ||
| 123 | if (font == NULL) | ||
| 124 | return; | ||
| 125 | |||
| 126 | Rune rune; | ||
| 127 | ushort mode = USHRT_MAX; | ||
| 128 | hb_buffer_t *buffer = hb_buffer_create(); | ||
| 129 | hb_buffer_set_direction(buffer, HB_DIRECTION_LTR); | ||
| 130 | |||
| 131 | /* Fill buffer with codepoints. */ | ||
| 132 | for (int i = start; i < (start+length); i++) { | ||
| 133 | rune = string[i].u; | ||
| 134 | mode = string[i].mode; | ||
| 135 | if (mode & ATTR_WDUMMY) | ||
| 136 | rune = 0x0020; | ||
| 137 | hb_buffer_add_codepoints(buffer, &rune, 1, 0, 1); | ||
| 138 | } | ||
| 139 | |||
| 140 | /* Shape the segment. */ | ||
| 141 | hb_shape(font, buffer, features, sizeof(features)); | ||
| 142 | |||
| 143 | /* Get new glyph info. */ | ||
| 144 | hb_glyph_info_t *info = hb_buffer_get_glyph_infos(buffer, NULL); | ||
| 145 | |||
| 146 | /* Write new codepoints. */ | ||
| 147 | for (int i = 0; i < length; i++) { | ||
| 148 | hb_codepoint_t gid = info[i].codepoint; | ||
| 149 | codepoints[start+i] = gid; | ||
| 150 | } | ||
| 151 | |||
| 152 | /* Cleanup. */ | ||
| 153 | hb_buffer_destroy(buffer); | ||
| 154 | } | ||
| @@ -0,0 +1,7 @@ | |||
| 1 | #include <X11/Xft/Xft.h> | ||
| 2 | #include <hb.h> | ||
| 3 | #include <hb-ft.h> | ||
| 4 | |||
| 5 | void hbunloadfonts(); | ||
| 6 | void hbtransform(XftGlyphFontSpec *, const Glyph *, size_t, int, int); | ||
| 7 | |||
diff --git a/st-copyout b/st-copyout new file mode 100755 index 0000000..0d19e5a --- /dev/null +++ b/st-copyout | |||
| @@ -0,0 +1,13 @@ | |||
| 1 | #!/bin/sh | ||
| 2 | # Using external pipe with st, give a dmenu prompt of recent commands, | ||
| 3 | # allowing the user to copy the output of one. | ||
| 4 | # xclip required for this script. | ||
| 5 | # By Jaywalker and Luke | ||
| 6 | tmpfile=$(mktemp /tmp/st-cmd-output.XXXXXX) | ||
| 7 | trap 'rm "$tmpfile"' 0 1 15 | ||
| 8 | sed -n "w $tmpfile" | ||
| 9 | sed -i 's/\x0//g' "$tmpfile" | ||
| 10 | ps1="$(grep "\S" "$tmpfile" | tail -n 1 | sed 's/^\s*//' | cut -d' ' -f1)" | ||
| 11 | chosen="$(grep -F "$ps1" "$tmpfile" | sed '$ d' | tac | dmenu -p "Copy which command's output?" -i -l 10 | sed 's/[^^]/[&]/g; s/\^/\\^/g')" | ||
| 12 | eps1="$(echo "$ps1" | sed 's/[^^]/[&]/g; s/\^/\\^/g')" | ||
| 13 | awk "/^$chosen$/{p=1;print;next} p&&/$eps1/{p=0};p" "$tmpfile" | xclip -selection clipboard | ||
diff --git a/st-urlhandler b/st-urlhandler new file mode 100755 index 0000000..0eb4586 --- /dev/null +++ b/st-urlhandler | |||
| @@ -0,0 +1,19 @@ | |||
| 1 | #!/bin/sh | ||
| 2 | |||
| 3 | urlregex="(((http|https|gopher|gemini|ftp|ftps|git)://|www\\.)[a-zA-Z0-9.]*[:;a-zA-Z0-9./+@$&%?$\#=_~-]*)|((magnet:\\?xt=urn:btih:)[a-zA-Z0-9]*)" | ||
| 4 | |||
| 5 | urls="$(sed 's/.*│//g' | tr -d '\n' | # First remove linebreaks and mutt sidebars: | ||
| 6 | grep -aEo "$urlregex" | # grep only urls as defined above. | ||
| 7 | uniq | # Ignore neighboring duplicates. | ||
| 8 | sed "s/\(\.\|,\|;\|\!\\|\?\)$//; | ||
| 9 | s/^www./http:\/\/www\./")" # xdg-open will not detect url without http | ||
| 10 | |||
| 11 | [ -z "$urls" ] && exit 1 | ||
| 12 | |||
| 13 | while getopts "hoc" o; do case "${o}" in | ||
| 14 | h) printf "Optional arguments for custom use:\\n -c: copy\\n -o: xdg-open\\n -h: Show this message\\n" && exit 1 ;; | ||
| 15 | o) chosen="$(echo "$urls" | dmenu -i -p 'Follow which url?' -l 10)" | ||
| 16 | setsid xdg-open "$chosen" >/dev/null 2>&1 & ;; | ||
| 17 | c) echo "$urls" | dmenu -i -p 'Copy which url?' -l 10 | tr -d '\n' | xclip -selection clipboard ;; | ||
| 18 | *) printf "Invalid option: -%s\\n" "$OPTARG" && exit 1 ;; | ||
| 19 | esac done | ||
| @@ -0,0 +1,193 @@ | |||
| 1 | .TH ST 1 st\-VERSION | ||
| 2 | .SH NAME | ||
| 3 | st \- simple terminal | ||
| 4 | .SH SYNOPSIS | ||
| 5 | .B st | ||
| 6 | .RB [ \-aiv ] | ||
| 7 | .RB [ \-c | ||
| 8 | .IR class ] | ||
| 9 | .RB [ \-f | ||
| 10 | .IR font ] | ||
| 11 | .RB [ \-g | ||
| 12 | .IR geometry ] | ||
| 13 | .RB [ \-n | ||
| 14 | .IR name ] | ||
| 15 | .RB [ \-o | ||
| 16 | .IR iofile ] | ||
| 17 | .RB [ \-T | ||
| 18 | .IR title ] | ||
| 19 | .RB [ \-t | ||
| 20 | .IR title ] | ||
| 21 | .RB [ \-l | ||
| 22 | .IR line ] | ||
| 23 | .RB [ \-w | ||
| 24 | .IR windowid ] | ||
| 25 | .RB [[ \-e ] | ||
| 26 | .IR command | ||
| 27 | .RI [ arguments ...]] | ||
| 28 | .PP | ||
| 29 | .B st | ||
| 30 | .RB [ \-aiv ] | ||
| 31 | .RB [ \-c | ||
| 32 | .IR class ] | ||
| 33 | .RB [ \-f | ||
| 34 | .IR font ] | ||
| 35 | .RB [ \-g | ||
| 36 | .IR geometry ] | ||
| 37 | .RB [ \-n | ||
| 38 | .IR name ] | ||
| 39 | .RB [ \-o | ||
| 40 | .IR iofile ] | ||
| 41 | .RB [ \-T | ||
| 42 | .IR title ] | ||
| 43 | .RB [ \-t | ||
| 44 | .IR title ] | ||
| 45 | .RB [ \-w | ||
| 46 | .IR windowid ] | ||
| 47 | .RB \-l | ||
| 48 | .IR line | ||
| 49 | .RI [ stty_args ...] | ||
| 50 | .SH DESCRIPTION | ||
| 51 | .B st | ||
| 52 | is a simple terminal emulator. | ||
| 53 | .SH OPTIONS | ||
| 54 | .TP | ||
| 55 | .B \-a | ||
| 56 | disable alternate screens in terminal | ||
| 57 | .TP | ||
| 58 | .BI \-c " class" | ||
| 59 | defines the window class (default $TERM). | ||
| 60 | .TP | ||
| 61 | .BI \-f " font" | ||
| 62 | defines the | ||
| 63 | .I font | ||
| 64 | to use when st is run. | ||
| 65 | .TP | ||
| 66 | .BI \-g " geometry" | ||
| 67 | defines the X11 geometry string. | ||
| 68 | The form is [=][<cols>{xX}<rows>][{+-}<xoffset>{+-}<yoffset>]. See | ||
| 69 | .BR XParseGeometry (3) | ||
| 70 | for further details. | ||
| 71 | .TP | ||
| 72 | .B \-i | ||
| 73 | will fixate the position given with the -g option. | ||
| 74 | .TP | ||
| 75 | .BI \-n " name" | ||
| 76 | defines the window instance name (default $TERM). | ||
| 77 | .TP | ||
| 78 | .BI \-o " iofile" | ||
| 79 | writes all the I/O to | ||
| 80 | .I iofile. | ||
| 81 | This feature is useful when recording st sessions. A value of "-" means | ||
| 82 | standard output. | ||
| 83 | .TP | ||
| 84 | .BI \-T " title" | ||
| 85 | defines the window title (default 'st'). | ||
| 86 | .TP | ||
| 87 | .BI \-t " title" | ||
| 88 | defines the window title (default 'st'). | ||
| 89 | .TP | ||
| 90 | .BI \-w " windowid" | ||
| 91 | embeds st within the window identified by | ||
| 92 | .I windowid | ||
| 93 | .TP | ||
| 94 | .BI \-l " line" | ||
| 95 | use a tty | ||
| 96 | .I line | ||
| 97 | instead of a pseudo terminal. | ||
| 98 | .I line | ||
| 99 | should be a (pseudo-)serial device (e.g. /dev/ttyS0 on Linux for serial port | ||
| 100 | 0). | ||
| 101 | When this flag is given | ||
| 102 | remaining arguments are used as flags for | ||
| 103 | .BR stty(1). | ||
| 104 | By default st initializes the serial line to 8 bits, no parity, 1 stop bit | ||
| 105 | and a 38400 baud rate. The speed is set by appending it as last argument | ||
| 106 | (e.g. 'st -l /dev/ttyS0 115200'). Arguments before the last one are | ||
| 107 | .BR stty(1) | ||
| 108 | flags. If you want to set odd parity on 115200 baud use for example 'st -l | ||
| 109 | /dev/ttyS0 parenb parodd 115200'. Set the number of bits by using for | ||
| 110 | example 'st -l /dev/ttyS0 cs7 115200'. See | ||
| 111 | .BR stty(1) | ||
| 112 | for more arguments and cases. | ||
| 113 | .TP | ||
| 114 | .B \-v | ||
| 115 | prints version information to stderr, then exits. | ||
| 116 | .TP | ||
| 117 | .BI \-e " command " [ " arguments " "... ]" | ||
| 118 | st executes | ||
| 119 | .I command | ||
| 120 | instead of the shell. If this is used it | ||
| 121 | .B must be the last option | ||
| 122 | on the command line, as in xterm / rxvt. | ||
| 123 | This option is only intended for compatibility, | ||
| 124 | and all the remaining arguments are used as a command | ||
| 125 | even without it. | ||
| 126 | .SH SHORTCUTS | ||
| 127 | .TP | ||
| 128 | .B Alt-j/k or Alt-Up/Down or Alt-Mouse Wheel | ||
| 129 | Scroll up/down one line at a time. | ||
| 130 | .TP | ||
| 131 | .B Alt-u/d or Alt-Page Up/Page Down | ||
| 132 | Scroll up/down one screen at a time. | ||
| 133 | .TP | ||
| 134 | .B Alt-Shift-k/j or Alt-Shift-Page Up/Page Down or Alt-Shift-Mouse Wheel | ||
| 135 | Increase or decrease font size. | ||
| 136 | .TP | ||
| 137 | .B Alt-Home | ||
| 138 | Reset to default font size. | ||
| 139 | .TP | ||
| 140 | .B Shift-Insert or Alt-v | ||
| 141 | Paste from clipboard. | ||
| 142 | .TP | ||
| 143 | .B Alt-c | ||
| 144 | Copy to clipboard. | ||
| 145 | .TP | ||
| 146 | .B Alt-p | ||
| 147 | Paste/input primary selection. | ||
| 148 | .TP | ||
| 149 | .B Alt-l | ||
| 150 | Show dmenu menu of all URLs on screen and choose one to open. | ||
| 151 | .TP | ||
| 152 | .B Alt-y | ||
| 153 | Show dmenu menu of all URLs on screen and choose one to copy. | ||
| 154 | .TP | ||
| 155 | .B Alt-o | ||
| 156 | Show dmenu menu of all recently run commands and copy the output of the chosen command to the clipboard. | ||
| 157 | .I xclip | ||
| 158 | required. | ||
| 159 | .TP | ||
| 160 | .B Alt-a/s | ||
| 161 | Increase or decrease opacity/alpha value (make window more or less transparent). | ||
| 162 | .TP | ||
| 163 | .B Break | ||
| 164 | Send a break in the serial line. | ||
| 165 | Break key is obtained in PC keyboards | ||
| 166 | pressing at the same time control and pause. | ||
| 167 | .TP | ||
| 168 | .B Ctrl-Print Screen | ||
| 169 | Toggle if st should print to the | ||
| 170 | .I iofile. | ||
| 171 | .TP | ||
| 172 | .B Shift-Print Screen | ||
| 173 | Print the full screen to the | ||
| 174 | .I iofile. | ||
| 175 | .TP | ||
| 176 | .B Print Screen | ||
| 177 | Print the selection to the | ||
| 178 | .I iofile. | ||
| 179 | .SH CUSTOMIZATION | ||
| 180 | .B st | ||
| 181 | can be customized by creating a custom config.h and (re)compiling the source | ||
| 182 | code. This keeps it fast, secure and simple. | ||
| 183 | .SH AUTHORS | ||
| 184 | See the LICENSE file for the authors. | ||
| 185 | .SH LICENSE | ||
| 186 | See the LICENSE file for the terms of redistribution. | ||
| 187 | .SH SEE ALSO | ||
| 188 | .BR tabbed (1), | ||
| 189 | .BR utmp (1), | ||
| 190 | .BR stty (1), | ||
| 191 | .BR scroll (1) | ||
| 192 | .SH BUGS | ||
| 193 | See the TODO file in the distribution. | ||
| @@ -0,0 +1,2816 @@ | |||
| 1 | /* See LICENSE for license details. */ | ||
| 2 | #include <ctype.h> | ||
| 3 | #include <errno.h> | ||
| 4 | #include <fcntl.h> | ||
| 5 | #include <limits.h> | ||
| 6 | #include <pwd.h> | ||
| 7 | #include <stdarg.h> | ||
| 8 | #include <stdio.h> | ||
| 9 | #include <stdlib.h> | ||
| 10 | #include <string.h> | ||
| 11 | #include <signal.h> | ||
| 12 | #include <sys/ioctl.h> | ||
| 13 | #include <sys/select.h> | ||
| 14 | #include <sys/types.h> | ||
| 15 | #include <sys/wait.h> | ||
| 16 | #include <termios.h> | ||
| 17 | #include <unistd.h> | ||
| 18 | #include <wchar.h> | ||
| 19 | |||
| 20 | #include "st.h" | ||
| 21 | #include "win.h" | ||
| 22 | |||
| 23 | #if defined(__linux) | ||
| 24 | #include <pty.h> | ||
| 25 | #elif defined(__OpenBSD__) || defined(__NetBSD__) || defined(__APPLE__) | ||
| 26 | #include <util.h> | ||
| 27 | #elif defined(__FreeBSD__) || defined(__DragonFly__) | ||
| 28 | #include <libutil.h> | ||
| 29 | #endif | ||
| 30 | |||
| 31 | /* Arbitrary sizes */ | ||
| 32 | #define UTF_INVALID 0xFFFD | ||
| 33 | #define UTF_SIZ 4 | ||
| 34 | #define ESC_BUF_SIZ (128*UTF_SIZ) | ||
| 35 | #define ESC_ARG_SIZ 16 | ||
| 36 | #define STR_BUF_SIZ ESC_BUF_SIZ | ||
| 37 | #define STR_ARG_SIZ ESC_ARG_SIZ | ||
| 38 | #define HISTSIZE 2000 | ||
| 39 | |||
| 40 | /* macros */ | ||
| 41 | #define IS_SET(flag) ((term.mode & (flag)) != 0) | ||
| 42 | #define ISCONTROLC0(c) (BETWEEN(c, 0, 0x1f) || (c) == 0x7f) | ||
| 43 | #define ISCONTROLC1(c) (BETWEEN(c, 0x80, 0x9f)) | ||
| 44 | #define ISCONTROL(c) (ISCONTROLC0(c) || ISCONTROLC1(c)) | ||
| 45 | #define ISDELIM(u) (u && wcschr(worddelimiters, u)) | ||
| 46 | #define TLINE(y) ((y) < term.scr ? term.hist[((y) + term.histi - \ | ||
| 47 | term.scr + HISTSIZE + 1) % HISTSIZE] : \ | ||
| 48 | term.line[(y) - term.scr]) | ||
| 49 | #define TLINE_HIST(y) ((y) <= HISTSIZE-term.row+2 ? term.hist[(y)] : term.line[(y-HISTSIZE+term.row-3)]) | ||
| 50 | |||
| 51 | enum term_mode { | ||
| 52 | MODE_WRAP = 1 << 0, | ||
| 53 | MODE_INSERT = 1 << 1, | ||
| 54 | MODE_ALTSCREEN = 1 << 2, | ||
| 55 | MODE_CRLF = 1 << 3, | ||
| 56 | MODE_ECHO = 1 << 4, | ||
| 57 | MODE_PRINT = 1 << 5, | ||
| 58 | MODE_UTF8 = 1 << 6, | ||
| 59 | }; | ||
| 60 | |||
| 61 | enum cursor_movement { | ||
| 62 | CURSOR_SAVE, | ||
| 63 | CURSOR_LOAD | ||
| 64 | }; | ||
| 65 | |||
| 66 | enum cursor_state { | ||
| 67 | CURSOR_DEFAULT = 0, | ||
| 68 | CURSOR_WRAPNEXT = 1, | ||
| 69 | CURSOR_ORIGIN = 2 | ||
| 70 | }; | ||
| 71 | |||
| 72 | enum charset { | ||
| 73 | CS_GRAPHIC0, | ||
| 74 | CS_GRAPHIC1, | ||
| 75 | CS_UK, | ||
| 76 | CS_USA, | ||
| 77 | CS_MULTI, | ||
| 78 | CS_GER, | ||
| 79 | CS_FIN | ||
| 80 | }; | ||
| 81 | |||
| 82 | enum escape_state { | ||
| 83 | ESC_START = 1, | ||
| 84 | ESC_CSI = 2, | ||
| 85 | ESC_STR = 4, /* DCS, OSC, PM, APC */ | ||
| 86 | ESC_ALTCHARSET = 8, | ||
| 87 | ESC_STR_END = 16, /* a final string was encountered */ | ||
| 88 | ESC_TEST = 32, /* Enter in test mode */ | ||
| 89 | ESC_UTF8 = 64, | ||
| 90 | }; | ||
| 91 | |||
| 92 | typedef struct { | ||
| 93 | Glyph attr; /* current char attributes */ | ||
| 94 | int x; | ||
| 95 | int y; | ||
| 96 | char state; | ||
| 97 | } TCursor; | ||
| 98 | |||
| 99 | typedef struct { | ||
| 100 | int mode; | ||
| 101 | int type; | ||
| 102 | int snap; | ||
| 103 | /* | ||
| 104 | * Selection variables: | ||
| 105 | * nb – normalized coordinates of the beginning of the selection | ||
| 106 | * ne – normalized coordinates of the end of the selection | ||
| 107 | * ob – original coordinates of the beginning of the selection | ||
| 108 | * oe – original coordinates of the end of the selection | ||
| 109 | */ | ||
| 110 | struct { | ||
| 111 | int x, y; | ||
| 112 | } nb, ne, ob, oe; | ||
| 113 | |||
| 114 | int alt; | ||
| 115 | } Selection; | ||
| 116 | |||
| 117 | /* Internal representation of the screen */ | ||
| 118 | typedef struct { | ||
| 119 | int row; /* nb row */ | ||
| 120 | int col; /* nb col */ | ||
| 121 | int maxcol; | ||
| 122 | Line *line; /* screen */ | ||
| 123 | Line *alt; /* alternate screen */ | ||
| 124 | Line hist[HISTSIZE]; /* history buffer */ | ||
| 125 | int histi; /* history index */ | ||
| 126 | int scr; /* scroll back */ | ||
| 127 | int *dirty; /* dirtyness of lines */ | ||
| 128 | TCursor c; /* cursor */ | ||
| 129 | int ocx; /* old cursor col */ | ||
| 130 | int ocy; /* old cursor row */ | ||
| 131 | int top; /* top scroll limit */ | ||
| 132 | int bot; /* bottom scroll limit */ | ||
| 133 | int mode; /* terminal mode flags */ | ||
| 134 | int esc; /* escape state flags */ | ||
| 135 | char trantbl[4]; /* charset table translation */ | ||
| 136 | int charset; /* current charset */ | ||
| 137 | int icharset; /* selected charset for sequence */ | ||
| 138 | int *tabs; | ||
| 139 | Rune lastc; /* last printed char outside of sequence, 0 if control */ | ||
| 140 | } Term; | ||
| 141 | |||
| 142 | /* CSI Escape sequence structs */ | ||
| 143 | /* ESC '[' [[ [<priv>] <arg> [;]] <mode> [<mode>]] */ | ||
| 144 | typedef struct { | ||
| 145 | char buf[ESC_BUF_SIZ]; /* raw string */ | ||
| 146 | size_t len; /* raw string length */ | ||
| 147 | char priv; | ||
| 148 | int arg[ESC_ARG_SIZ]; | ||
| 149 | int narg; /* nb of args */ | ||
| 150 | char mode[2]; | ||
| 151 | } CSIEscape; | ||
| 152 | |||
| 153 | /* STR Escape sequence structs */ | ||
| 154 | /* ESC type [[ [<priv>] <arg> [;]] <mode>] ESC '\' */ | ||
| 155 | typedef struct { | ||
| 156 | char type; /* ESC type ... */ | ||
| 157 | char *buf; /* allocated raw string */ | ||
| 158 | size_t siz; /* allocation size */ | ||
| 159 | size_t len; /* raw string length */ | ||
| 160 | char *args[STR_ARG_SIZ]; | ||
| 161 | int narg; /* nb of args */ | ||
| 162 | } STREscape; | ||
| 163 | |||
| 164 | static void execsh(char *, char **); | ||
| 165 | static void stty(char **); | ||
| 166 | static void sigchld(int); | ||
| 167 | static void ttywriteraw(const char *, size_t); | ||
| 168 | |||
| 169 | static void csidump(void); | ||
| 170 | static void csihandle(void); | ||
| 171 | static void csiparse(void); | ||
| 172 | static void csireset(void); | ||
| 173 | static void osc_color_response(int, int, int); | ||
| 174 | static int eschandle(uchar); | ||
| 175 | static void strdump(void); | ||
| 176 | static void strhandle(void); | ||
| 177 | static void strparse(void); | ||
| 178 | static void strreset(void); | ||
| 179 | |||
| 180 | static void tprinter(char *, size_t); | ||
| 181 | static void tdumpsel(void); | ||
| 182 | static void tdumpline(int); | ||
| 183 | static void tdump(void); | ||
| 184 | static void tclearregion(int, int, int, int); | ||
| 185 | static void tcursor(int); | ||
| 186 | static void tdeletechar(int); | ||
| 187 | static void tdeleteline(int); | ||
| 188 | static void tinsertblank(int); | ||
| 189 | static void tinsertblankline(int); | ||
| 190 | static int tlinelen(int); | ||
| 191 | static void tmoveto(int, int); | ||
| 192 | static void tmoveato(int, int); | ||
| 193 | static void tnewline(int); | ||
| 194 | static void tputtab(int); | ||
| 195 | static void tputc(Rune); | ||
| 196 | static void treset(void); | ||
| 197 | static void tscrollup(int, int, int); | ||
| 198 | static void tscrolldown(int, int, int); | ||
| 199 | static void tsetattr(const int *, int); | ||
| 200 | static void tsetchar(Rune, const Glyph *, int, int); | ||
| 201 | static void tsetdirt(int, int); | ||
| 202 | static void tsetscroll(int, int); | ||
| 203 | static void tswapscreen(void); | ||
| 204 | static void tsetmode(int, int, const int *, int); | ||
| 205 | static int twrite(const char *, int, int); | ||
| 206 | static void tcontrolcode(uchar ); | ||
| 207 | static void tdectest(char ); | ||
| 208 | static void tdefutf8(char); | ||
| 209 | static int32_t tdefcolor(const int *, int *, int); | ||
| 210 | static void tdeftran(char); | ||
| 211 | static void tstrsequence(uchar); | ||
| 212 | |||
| 213 | static void drawregion(int, int, int, int); | ||
| 214 | |||
| 215 | static void selnormalize(void); | ||
| 216 | static void selscroll(int, int); | ||
| 217 | static void selsnap(int *, int *, int); | ||
| 218 | |||
| 219 | static size_t utf8decode(const char *, Rune *, size_t); | ||
| 220 | static Rune utf8decodebyte(char, size_t *); | ||
| 221 | static char utf8encodebyte(Rune, size_t); | ||
| 222 | static size_t utf8validate(Rune *, size_t); | ||
| 223 | |||
| 224 | static char *base64dec(const char *); | ||
| 225 | static char base64dec_getc(const char **); | ||
| 226 | |||
| 227 | static ssize_t xwrite(int, const char *, size_t); | ||
| 228 | |||
| 229 | /* Globals */ | ||
| 230 | static Term term; | ||
| 231 | static Selection sel; | ||
| 232 | static CSIEscape csiescseq; | ||
| 233 | static STREscape strescseq; | ||
| 234 | static int iofd = 1; | ||
| 235 | static int cmdfd; | ||
| 236 | static pid_t pid; | ||
| 237 | |||
| 238 | static const uchar utfbyte[UTF_SIZ + 1] = {0x80, 0, 0xC0, 0xE0, 0xF0}; | ||
| 239 | static const uchar utfmask[UTF_SIZ + 1] = {0xC0, 0x80, 0xE0, 0xF0, 0xF8}; | ||
| 240 | static const Rune utfmin[UTF_SIZ + 1] = { 0, 0, 0x80, 0x800, 0x10000}; | ||
| 241 | static const Rune utfmax[UTF_SIZ + 1] = {0x10FFFF, 0x7F, 0x7FF, 0xFFFF, 0x10FFFF}; | ||
| 242 | |||
| 243 | ssize_t | ||
| 244 | xwrite(int fd, const char *s, size_t len) | ||
| 245 | { | ||
| 246 | size_t aux = len; | ||
| 247 | ssize_t r; | ||
| 248 | |||
| 249 | while (len > 0) { | ||
| 250 | r = write(fd, s, len); | ||
| 251 | if (r < 0) | ||
| 252 | return r; | ||
| 253 | len -= r; | ||
| 254 | s += r; | ||
| 255 | } | ||
| 256 | |||
| 257 | return aux; | ||
| 258 | } | ||
| 259 | |||
| 260 | void * | ||
| 261 | xmalloc(size_t len) | ||
| 262 | { | ||
| 263 | void *p; | ||
| 264 | |||
| 265 | if (!(p = malloc(len))) | ||
| 266 | die("malloc: %s\n", strerror(errno)); | ||
| 267 | |||
| 268 | return p; | ||
| 269 | } | ||
| 270 | |||
| 271 | void * | ||
| 272 | xrealloc(void *p, size_t len) | ||
| 273 | { | ||
| 274 | if ((p = realloc(p, len)) == NULL) | ||
| 275 | die("realloc: %s\n", strerror(errno)); | ||
| 276 | |||
| 277 | return p; | ||
| 278 | } | ||
| 279 | |||
| 280 | char * | ||
| 281 | xstrdup(const char *s) | ||
| 282 | { | ||
| 283 | if ((s = strdup(s)) == NULL) | ||
| 284 | die("strdup: %s\n", strerror(errno)); | ||
| 285 | char *p; | ||
| 286 | |||
| 287 | if ((p = strdup(s)) == NULL) | ||
| 288 | die("strdup: %s\n", strerror(errno)); | ||
| 289 | |||
| 290 | return p; | ||
| 291 | } | ||
| 292 | |||
| 293 | size_t | ||
| 294 | utf8decode(const char *c, Rune *u, size_t clen) | ||
| 295 | { | ||
| 296 | size_t i, j, len, type; | ||
| 297 | Rune udecoded; | ||
| 298 | |||
| 299 | *u = UTF_INVALID; | ||
| 300 | if (!clen) | ||
| 301 | return 0; | ||
| 302 | udecoded = utf8decodebyte(c[0], &len); | ||
| 303 | if (!BETWEEN(len, 1, UTF_SIZ)) | ||
| 304 | return 1; | ||
| 305 | for (i = 1, j = 1; i < clen && j < len; ++i, ++j) { | ||
| 306 | udecoded = (udecoded << 6) | utf8decodebyte(c[i], &type); | ||
| 307 | if (type != 0) | ||
| 308 | return j; | ||
| 309 | } | ||
| 310 | if (j < len) | ||
| 311 | return 0; | ||
| 312 | *u = udecoded; | ||
| 313 | utf8validate(u, len); | ||
| 314 | |||
| 315 | return len; | ||
| 316 | } | ||
| 317 | |||
| 318 | Rune | ||
| 319 | utf8decodebyte(char c, size_t *i) | ||
| 320 | { | ||
| 321 | for (*i = 0; *i < LEN(utfmask); ++(*i)) | ||
| 322 | if (((uchar)c & utfmask[*i]) == utfbyte[*i]) | ||
| 323 | return (uchar)c & ~utfmask[*i]; | ||
| 324 | |||
| 325 | return 0; | ||
| 326 | } | ||
| 327 | |||
| 328 | size_t | ||
| 329 | utf8encode(Rune u, char *c) | ||
| 330 | { | ||
| 331 | size_t len, i; | ||
| 332 | |||
| 333 | len = utf8validate(&u, 0); | ||
| 334 | if (len > UTF_SIZ) | ||
| 335 | return 0; | ||
| 336 | |||
| 337 | for (i = len - 1; i != 0; --i) { | ||
| 338 | c[i] = utf8encodebyte(u, 0); | ||
| 339 | u >>= 6; | ||
| 340 | } | ||
| 341 | c[0] = utf8encodebyte(u, len); | ||
| 342 | |||
| 343 | return len; | ||
| 344 | } | ||
| 345 | |||
| 346 | char | ||
| 347 | utf8encodebyte(Rune u, size_t i) | ||
| 348 | { | ||
| 349 | return utfbyte[i] | (u & ~utfmask[i]); | ||
| 350 | } | ||
| 351 | |||
| 352 | size_t | ||
| 353 | utf8validate(Rune *u, size_t i) | ||
| 354 | { | ||
| 355 | if (!BETWEEN(*u, utfmin[i], utfmax[i]) || BETWEEN(*u, 0xD800, 0xDFFF)) | ||
| 356 | *u = UTF_INVALID; | ||
| 357 | for (i = 1; *u > utfmax[i]; ++i) | ||
| 358 | ; | ||
| 359 | |||
| 360 | return i; | ||
| 361 | } | ||
| 362 | |||
| 363 | char | ||
| 364 | base64dec_getc(const char **src) | ||
| 365 | { | ||
| 366 | while (**src && !isprint((unsigned char)**src)) | ||
| 367 | (*src)++; | ||
| 368 | return **src ? *((*src)++) : '='; /* emulate padding if string ends */ | ||
| 369 | } | ||
| 370 | |||
| 371 | char * | ||
| 372 | base64dec(const char *src) | ||
| 373 | { | ||
| 374 | size_t in_len = strlen(src); | ||
| 375 | char *result, *dst; | ||
| 376 | static const char base64_digits[256] = { | ||
| 377 | [43] = 62, 0, 0, 0, 63, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, | ||
| 378 | 0, 0, 0, -1, 0, 0, 0, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, | ||
| 379 | 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 0, 0, 0, 0, | ||
| 380 | 0, 0, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, | ||
| 381 | 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51 | ||
| 382 | }; | ||
| 383 | |||
| 384 | if (in_len % 4) | ||
| 385 | in_len += 4 - (in_len % 4); | ||
| 386 | result = dst = xmalloc(in_len / 4 * 3 + 1); | ||
| 387 | while (*src) { | ||
| 388 | int a = base64_digits[(unsigned char) base64dec_getc(&src)]; | ||
| 389 | int b = base64_digits[(unsigned char) base64dec_getc(&src)]; | ||
| 390 | int c = base64_digits[(unsigned char) base64dec_getc(&src)]; | ||
| 391 | int d = base64_digits[(unsigned char) base64dec_getc(&src)]; | ||
| 392 | |||
| 393 | /* invalid input. 'a' can be -1, e.g. if src is "\n" (c-str) */ | ||
| 394 | if (a == -1 || b == -1) | ||
| 395 | break; | ||
| 396 | |||
| 397 | *dst++ = (a << 2) | ((b & 0x30) >> 4); | ||
| 398 | if (c == -1) | ||
| 399 | break; | ||
| 400 | *dst++ = ((b & 0x0f) << 4) | ((c & 0x3c) >> 2); | ||
| 401 | if (d == -1) | ||
| 402 | break; | ||
| 403 | *dst++ = ((c & 0x03) << 6) | d; | ||
| 404 | } | ||
| 405 | *dst = '\0'; | ||
| 406 | return result; | ||
| 407 | } | ||
| 408 | |||
| 409 | void | ||
| 410 | selinit(void) | ||
| 411 | { | ||
| 412 | sel.mode = SEL_IDLE; | ||
| 413 | sel.snap = 0; | ||
| 414 | sel.ob.x = -1; | ||
| 415 | } | ||
| 416 | |||
| 417 | int | ||
| 418 | tlinelen(int y) | ||
| 419 | { | ||
| 420 | int i = term.col; | ||
| 421 | |||
| 422 | if (TLINE(y)[i - 1].mode & ATTR_WRAP) | ||
| 423 | return i; | ||
| 424 | |||
| 425 | while (i > 0 && TLINE(y)[i - 1].u == ' ') | ||
| 426 | --i; | ||
| 427 | |||
| 428 | return i; | ||
| 429 | } | ||
| 430 | |||
| 431 | int | ||
| 432 | tlinehistlen(int y) | ||
| 433 | { | ||
| 434 | int i = term.col; | ||
| 435 | |||
| 436 | if (TLINE_HIST(y)[i - 1].mode & ATTR_WRAP) | ||
| 437 | return i; | ||
| 438 | |||
| 439 | while (i > 0 && TLINE_HIST(y)[i - 1].u == ' ') | ||
| 440 | --i; | ||
| 441 | |||
| 442 | return i; | ||
| 443 | } | ||
| 444 | |||
| 445 | void | ||
| 446 | selstart(int col, int row, int snap) | ||
| 447 | { | ||
| 448 | selclear(); | ||
| 449 | sel.mode = SEL_EMPTY; | ||
| 450 | sel.type = SEL_REGULAR; | ||
| 451 | sel.alt = IS_SET(MODE_ALTSCREEN); | ||
| 452 | sel.snap = snap; | ||
| 453 | sel.oe.x = sel.ob.x = col; | ||
| 454 | sel.oe.y = sel.ob.y = row; | ||
| 455 | selnormalize(); | ||
| 456 | |||
| 457 | if (sel.snap != 0) | ||
| 458 | sel.mode = SEL_READY; | ||
| 459 | tsetdirt(sel.nb.y, sel.ne.y); | ||
| 460 | } | ||
| 461 | |||
| 462 | void | ||
| 463 | selextend(int col, int row, int type, int done) | ||
| 464 | { | ||
| 465 | int oldey, oldex, oldsby, oldsey, oldtype; | ||
| 466 | |||
| 467 | if (sel.mode == SEL_IDLE) | ||
| 468 | return; | ||
| 469 | if (done && sel.mode == SEL_EMPTY) { | ||
| 470 | selclear(); | ||
| 471 | return; | ||
| 472 | } | ||
| 473 | |||
| 474 | oldey = sel.oe.y; | ||
| 475 | oldex = sel.oe.x; | ||
| 476 | oldsby = sel.nb.y; | ||
| 477 | oldsey = sel.ne.y; | ||
| 478 | oldtype = sel.type; | ||
| 479 | |||
| 480 | sel.oe.x = col; | ||
| 481 | sel.oe.y = row; | ||
| 482 | selnormalize(); | ||
| 483 | sel.type = type; | ||
| 484 | |||
| 485 | if (oldey != sel.oe.y || oldex != sel.oe.x || oldtype != sel.type || sel.mode == SEL_EMPTY) | ||
| 486 | tsetdirt(MIN(sel.nb.y, oldsby), MAX(sel.ne.y, oldsey)); | ||
| 487 | |||
| 488 | sel.mode = done ? SEL_IDLE : SEL_READY; | ||
| 489 | } | ||
| 490 | |||
| 491 | |||
| 492 | void | ||
| 493 | selnormalize(void) | ||
| 494 | { | ||
| 495 | int i; | ||
| 496 | |||
| 497 | if (sel.type == SEL_REGULAR && sel.ob.y != sel.oe.y) { | ||
| 498 | sel.nb.x = sel.ob.y < sel.oe.y ? sel.ob.x : sel.oe.x; | ||
| 499 | sel.ne.x = sel.ob.y < sel.oe.y ? sel.oe.x : sel.ob.x; | ||
| 500 | } else { | ||
| 501 | sel.nb.x = MIN(sel.ob.x, sel.oe.x); | ||
| 502 | sel.ne.x = MAX(sel.ob.x, sel.oe.x); | ||
| 503 | } | ||
| 504 | sel.nb.y = MIN(sel.ob.y, sel.oe.y); | ||
| 505 | sel.ne.y = MAX(sel.ob.y, sel.oe.y); | ||
| 506 | |||
| 507 | selsnap(&sel.nb.x, &sel.nb.y, -1); | ||
| 508 | selsnap(&sel.ne.x, &sel.ne.y, +1); | ||
| 509 | |||
| 510 | /* expand selection over line breaks */ | ||
| 511 | if (sel.type == SEL_RECTANGULAR) | ||
| 512 | return; | ||
| 513 | i = tlinelen(sel.nb.y); | ||
| 514 | if (i < sel.nb.x) | ||
| 515 | sel.nb.x = i; | ||
| 516 | if (tlinelen(sel.ne.y) <= sel.ne.x) | ||
| 517 | sel.ne.x = term.col - 1; | ||
| 518 | } | ||
| 519 | |||
| 520 | int | ||
| 521 | selected(int x, int y) | ||
| 522 | { | ||
| 523 | if (sel.mode == SEL_EMPTY || sel.ob.x == -1 || | ||
| 524 | sel.alt != IS_SET(MODE_ALTSCREEN)) | ||
| 525 | return 0; | ||
| 526 | |||
| 527 | if (sel.type == SEL_RECTANGULAR) | ||
| 528 | return BETWEEN(y, sel.nb.y, sel.ne.y) | ||
| 529 | && BETWEEN(x, sel.nb.x, sel.ne.x); | ||
| 530 | |||
| 531 | return BETWEEN(y, sel.nb.y, sel.ne.y) | ||
| 532 | && (y != sel.nb.y || x >= sel.nb.x) | ||
| 533 | && (y != sel.ne.y || x <= sel.ne.x); | ||
| 534 | } | ||
| 535 | |||
| 536 | void | ||
| 537 | selsnap(int *x, int *y, int direction) | ||
| 538 | { | ||
| 539 | int newx, newy, xt, yt; | ||
| 540 | int delim, prevdelim; | ||
| 541 | const Glyph *gp, *prevgp; | ||
| 542 | |||
| 543 | switch (sel.snap) { | ||
| 544 | case SNAP_WORD: | ||
| 545 | /* | ||
| 546 | * Snap around if the word wraps around at the end or | ||
| 547 | * beginning of a line. | ||
| 548 | */ | ||
| 549 | prevgp = &TLINE(*y)[*x]; | ||
| 550 | prevdelim = ISDELIM(prevgp->u); | ||
| 551 | for (;;) { | ||
| 552 | newx = *x + direction; | ||
| 553 | newy = *y; | ||
| 554 | if (!BETWEEN(newx, 0, term.col - 1)) { | ||
| 555 | newy += direction; | ||
| 556 | newx = (newx + term.col) % term.col; | ||
| 557 | if (!BETWEEN(newy, 0, term.row - 1)) | ||
| 558 | break; | ||
| 559 | |||
| 560 | if (direction > 0) | ||
| 561 | yt = *y, xt = *x; | ||
| 562 | else | ||
| 563 | yt = newy, xt = newx; | ||
| 564 | if (!(TLINE(yt)[xt].mode & ATTR_WRAP)) | ||
| 565 | break; | ||
| 566 | } | ||
| 567 | |||
| 568 | if (newx >= tlinelen(newy)) | ||
| 569 | break; | ||
| 570 | |||
| 571 | gp = &TLINE(newy)[newx]; | ||
| 572 | delim = ISDELIM(gp->u); | ||
| 573 | if (!(gp->mode & ATTR_WDUMMY) && (delim != prevdelim | ||
| 574 | || (delim && gp->u != prevgp->u))) | ||
| 575 | break; | ||
| 576 | |||
| 577 | *x = newx; | ||
| 578 | *y = newy; | ||
| 579 | prevgp = gp; | ||
| 580 | prevdelim = delim; | ||
| 581 | } | ||
| 582 | break; | ||
| 583 | case SNAP_LINE: | ||
| 584 | /* | ||
| 585 | * Snap around if the the previous line or the current one | ||
| 586 | * has set ATTR_WRAP at its end. Then the whole next or | ||
| 587 | * previous line will be selected. | ||
| 588 | */ | ||
| 589 | *x = (direction < 0) ? 0 : term.col - 1; | ||
| 590 | if (direction < 0) { | ||
| 591 | for (; *y > 0; *y += direction) { | ||
| 592 | if (!(TLINE(*y-1)[term.col-1].mode | ||
| 593 | & ATTR_WRAP)) { | ||
| 594 | break; | ||
| 595 | } | ||
| 596 | } | ||
| 597 | } else if (direction > 0) { | ||
| 598 | for (; *y < term.row-1; *y += direction) { | ||
| 599 | if (!(TLINE(*y)[term.col-1].mode | ||
| 600 | & ATTR_WRAP)) { | ||
| 601 | break; | ||
| 602 | } | ||
| 603 | } | ||
| 604 | } | ||
| 605 | break; | ||
| 606 | } | ||
| 607 | } | ||
| 608 | |||
| 609 | char * | ||
| 610 | getsel(void) | ||
| 611 | { | ||
| 612 | char *str, *ptr; | ||
| 613 | int y, bufsize, lastx, linelen; | ||
| 614 | const Glyph *gp, *last; | ||
| 615 | |||
| 616 | if (sel.ob.x == -1) | ||
| 617 | return NULL; | ||
| 618 | |||
| 619 | bufsize = (term.col+1) * (sel.ne.y-sel.nb.y+1) * UTF_SIZ; | ||
| 620 | ptr = str = xmalloc(bufsize); | ||
| 621 | |||
| 622 | /* append every set & selected glyph to the selection */ | ||
| 623 | for (y = sel.nb.y; y <= sel.ne.y; y++) { | ||
| 624 | if ((linelen = tlinelen(y)) == 0) { | ||
| 625 | *ptr++ = '\n'; | ||
| 626 | continue; | ||
| 627 | } | ||
| 628 | |||
| 629 | if (sel.type == SEL_RECTANGULAR) { | ||
| 630 | gp = &TLINE(y)[sel.nb.x]; | ||
| 631 | lastx = sel.ne.x; | ||
| 632 | } else { | ||
| 633 | gp = &TLINE(y)[sel.nb.y == y ? sel.nb.x : 0]; | ||
| 634 | lastx = (sel.ne.y == y) ? sel.ne.x : term.col-1; | ||
| 635 | } | ||
| 636 | last = &TLINE(y)[MIN(lastx, linelen-1)]; | ||
| 637 | while (last >= gp && last->u == ' ') | ||
| 638 | --last; | ||
| 639 | |||
| 640 | for ( ; gp <= last; ++gp) { | ||
| 641 | if (gp->mode & ATTR_WDUMMY) | ||
| 642 | continue; | ||
| 643 | |||
| 644 | ptr += utf8encode(gp->u, ptr); | ||
| 645 | } | ||
| 646 | |||
| 647 | /* | ||
| 648 | * Copy and pasting of line endings is inconsistent | ||
| 649 | * in the inconsistent terminal and GUI world. | ||
| 650 | * The best solution seems like to produce '\n' when | ||
| 651 | * something is copied from st and convert '\n' to | ||
| 652 | * '\r', when something to be pasted is received by | ||
| 653 | * st. | ||
| 654 | * FIXME: Fix the computer world. | ||
| 655 | */ | ||
| 656 | if ((y < sel.ne.y || lastx >= linelen) && | ||
| 657 | (!(last->mode & ATTR_WRAP) || sel.type == SEL_RECTANGULAR)) | ||
| 658 | *ptr++ = '\n'; | ||
| 659 | } | ||
| 660 | *ptr = 0; | ||
| 661 | return str; | ||
| 662 | } | ||
| 663 | |||
| 664 | void | ||
| 665 | selclear(void) | ||
| 666 | { | ||
| 667 | if (sel.ob.x == -1) | ||
| 668 | return; | ||
| 669 | sel.mode = SEL_IDLE; | ||
| 670 | sel.ob.x = -1; | ||
| 671 | tsetdirt(sel.nb.y, sel.ne.y); | ||
| 672 | } | ||
| 673 | |||
| 674 | void | ||
| 675 | die(const char *errstr, ...) | ||
| 676 | { | ||
| 677 | va_list ap; | ||
| 678 | |||
| 679 | va_start(ap, errstr); | ||
| 680 | vfprintf(stderr, errstr, ap); | ||
| 681 | va_end(ap); | ||
| 682 | exit(1); | ||
| 683 | } | ||
| 684 | |||
| 685 | void | ||
| 686 | execsh(char *cmd, char **args) | ||
| 687 | { | ||
| 688 | char *sh, *prog, *arg; | ||
| 689 | const struct passwd *pw; | ||
| 690 | |||
| 691 | errno = 0; | ||
| 692 | if ((pw = getpwuid(getuid())) == NULL) { | ||
| 693 | if (errno) | ||
| 694 | die("getpwuid: %s\n", strerror(errno)); | ||
| 695 | else | ||
| 696 | die("who are you?\n"); | ||
| 697 | } | ||
| 698 | |||
| 699 | if ((sh = getenv("SHELL")) == NULL) | ||
| 700 | sh = (pw->pw_shell[0]) ? pw->pw_shell : cmd; | ||
| 701 | |||
| 702 | if (args) { | ||
| 703 | prog = args[0]; | ||
| 704 | arg = NULL; | ||
| 705 | } else if (scroll) { | ||
| 706 | prog = scroll; | ||
| 707 | arg = utmp ? utmp : sh; | ||
| 708 | } else if (utmp) { | ||
| 709 | prog = utmp; | ||
| 710 | arg = NULL; | ||
| 711 | } else { | ||
| 712 | prog = sh; | ||
| 713 | arg = NULL; | ||
| 714 | } | ||
| 715 | DEFAULT(args, ((char *[]) {prog, arg, NULL})); | ||
| 716 | |||
| 717 | unsetenv("COLUMNS"); | ||
| 718 | unsetenv("LINES"); | ||
| 719 | unsetenv("TERMCAP"); | ||
| 720 | setenv("LOGNAME", pw->pw_name, 1); | ||
| 721 | setenv("USER", pw->pw_name, 1); | ||
| 722 | setenv("SHELL", sh, 1); | ||
| 723 | setenv("HOME", pw->pw_dir, 1); | ||
| 724 | setenv("TERM", termname, 1); | ||
| 725 | |||
| 726 | signal(SIGCHLD, SIG_DFL); | ||
| 727 | signal(SIGHUP, SIG_DFL); | ||
| 728 | signal(SIGINT, SIG_DFL); | ||
| 729 | signal(SIGQUIT, SIG_DFL); | ||
| 730 | signal(SIGTERM, SIG_DFL); | ||
| 731 | signal(SIGALRM, SIG_DFL); | ||
| 732 | |||
| 733 | execvp(prog, args); | ||
| 734 | _exit(1); | ||
| 735 | } | ||
| 736 | |||
| 737 | void | ||
| 738 | sigchld(int a) | ||
| 739 | { | ||
| 740 | int stat; | ||
| 741 | pid_t p; | ||
| 742 | |||
| 743 | if ((p = waitpid(pid, &stat, WNOHANG)) < 0) | ||
| 744 | die("waiting for pid %hd failed: %s\n", pid, strerror(errno)); | ||
| 745 | |||
| 746 | if (pid != p) | ||
| 747 | return; | ||
| 748 | |||
| 749 | if (WIFEXITED(stat) && WEXITSTATUS(stat)) | ||
| 750 | die("child exited with status %d\n", WEXITSTATUS(stat)); | ||
| 751 | else if (WIFSIGNALED(stat)) | ||
| 752 | die("child terminated due to signal %d\n", WTERMSIG(stat)); | ||
| 753 | _exit(0); | ||
| 754 | } | ||
| 755 | |||
| 756 | void | ||
| 757 | stty(char **args) | ||
| 758 | { | ||
| 759 | char cmd[_POSIX_ARG_MAX], **p, *q, *s; | ||
| 760 | size_t n, siz; | ||
| 761 | |||
| 762 | if ((n = strlen(stty_args)) > sizeof(cmd)-1) | ||
| 763 | die("incorrect stty parameters\n"); | ||
| 764 | memcpy(cmd, stty_args, n); | ||
| 765 | q = cmd + n; | ||
| 766 | siz = sizeof(cmd) - n; | ||
| 767 | for (p = args; p && (s = *p); ++p) { | ||
| 768 | if ((n = strlen(s)) > siz-1) | ||
| 769 | die("stty parameter length too long\n"); | ||
| 770 | *q++ = ' '; | ||
| 771 | memcpy(q, s, n); | ||
| 772 | q += n; | ||
| 773 | siz -= n + 1; | ||
| 774 | } | ||
| 775 | *q = '\0'; | ||
| 776 | if (system(cmd) != 0) | ||
| 777 | perror("Couldn't call stty"); | ||
| 778 | } | ||
| 779 | |||
| 780 | int | ||
| 781 | ttynew(const char *line, char *cmd, const char *out, char **args) | ||
| 782 | { | ||
| 783 | int m, s; | ||
| 784 | |||
| 785 | if (out) { | ||
| 786 | term.mode |= MODE_PRINT; | ||
| 787 | iofd = (!strcmp(out, "-")) ? | ||
| 788 | 1 : open(out, O_WRONLY | O_CREAT, 0666); | ||
| 789 | if (iofd < 0) { | ||
| 790 | fprintf(stderr, "Error opening %s:%s\n", | ||
| 791 | out, strerror(errno)); | ||
| 792 | } | ||
| 793 | } | ||
| 794 | |||
| 795 | if (line) { | ||
| 796 | if ((cmdfd = open(line, O_RDWR)) < 0) | ||
| 797 | die("open line '%s' failed: %s\n", | ||
| 798 | line, strerror(errno)); | ||
| 799 | dup2(cmdfd, 0); | ||
| 800 | stty(args); | ||
| 801 | return cmdfd; | ||
| 802 | } | ||
| 803 | |||
| 804 | /* seems to work fine on linux, openbsd and freebsd */ | ||
| 805 | if (openpty(&m, &s, NULL, NULL, NULL) < 0) | ||
| 806 | die("openpty failed: %s\n", strerror(errno)); | ||
| 807 | |||
| 808 | switch (pid = fork()) { | ||
| 809 | case -1: | ||
| 810 | die("fork failed: %s\n", strerror(errno)); | ||
| 811 | break; | ||
| 812 | case 0: | ||
| 813 | close(iofd); | ||
| 814 | close(m); | ||
| 815 | setsid(); /* create a new process group */ | ||
| 816 | dup2(s, 0); | ||
| 817 | dup2(s, 1); | ||
| 818 | dup2(s, 2); | ||
| 819 | if (ioctl(s, TIOCSCTTY, NULL) < 0) | ||
| 820 | die("ioctl TIOCSCTTY failed: %s\n", strerror(errno)); | ||
| 821 | if (s > 2) | ||
| 822 | close(s); | ||
| 823 | #ifdef __OpenBSD__ | ||
| 824 | if (pledge("stdio getpw proc exec", NULL) == -1) | ||
| 825 | die("pledge\n"); | ||
| 826 | #endif | ||
| 827 | execsh(cmd, args); | ||
| 828 | break; | ||
| 829 | default: | ||
| 830 | #ifdef __OpenBSD__ | ||
| 831 | if (pledge("stdio rpath tty proc", NULL) == -1) | ||
| 832 | die("pledge\n"); | ||
| 833 | #endif | ||
| 834 | close(s); | ||
| 835 | cmdfd = m; | ||
| 836 | signal(SIGCHLD, sigchld); | ||
| 837 | break; | ||
| 838 | } | ||
| 839 | return cmdfd; | ||
| 840 | } | ||
| 841 | |||
| 842 | size_t | ||
| 843 | ttyread(void) | ||
| 844 | { | ||
| 845 | static char buf[BUFSIZ]; | ||
| 846 | static int buflen = 0; | ||
| 847 | int ret, written; | ||
| 848 | |||
| 849 | /* append read bytes to unprocessed bytes */ | ||
| 850 | ret = read(cmdfd, buf+buflen, LEN(buf)-buflen); | ||
| 851 | |||
| 852 | switch (ret) { | ||
| 853 | case 0: | ||
| 854 | exit(0); | ||
| 855 | case -1: | ||
| 856 | die("couldn't read from shell: %s\n", strerror(errno)); | ||
| 857 | default: | ||
| 858 | buflen += ret; | ||
| 859 | written = twrite(buf, buflen, 0); | ||
| 860 | buflen -= written; | ||
| 861 | /* keep any incomplete UTF-8 byte sequence for the next call */ | ||
| 862 | if (buflen > 0) | ||
| 863 | memmove(buf, buf + written, buflen); | ||
| 864 | return ret; | ||
| 865 | } | ||
| 866 | } | ||
| 867 | |||
| 868 | void | ||
| 869 | ttywrite(const char *s, size_t n, int may_echo) | ||
| 870 | { | ||
| 871 | const char *next; | ||
| 872 | Arg arg = (Arg) { .i = term.scr }; | ||
| 873 | |||
| 874 | kscrolldown(&arg); | ||
| 875 | |||
| 876 | if (may_echo && IS_SET(MODE_ECHO)) | ||
| 877 | twrite(s, n, 1); | ||
| 878 | |||
| 879 | if (!IS_SET(MODE_CRLF)) { | ||
| 880 | ttywriteraw(s, n); | ||
| 881 | return; | ||
| 882 | } | ||
| 883 | |||
| 884 | /* This is similar to how the kernel handles ONLCR for ttys */ | ||
| 885 | while (n > 0) { | ||
| 886 | if (*s == '\r') { | ||
| 887 | next = s + 1; | ||
| 888 | ttywriteraw("\r\n", 2); | ||
| 889 | } else { | ||
| 890 | next = memchr(s, '\r', n); | ||
| 891 | DEFAULT(next, s + n); | ||
| 892 | ttywriteraw(s, next - s); | ||
| 893 | } | ||
| 894 | n -= next - s; | ||
| 895 | s = next; | ||
| 896 | } | ||
| 897 | } | ||
| 898 | |||
| 899 | void | ||
| 900 | ttywriteraw(const char *s, size_t n) | ||
| 901 | { | ||
| 902 | fd_set wfd, rfd; | ||
| 903 | ssize_t r; | ||
| 904 | size_t lim = 256; | ||
| 905 | |||
| 906 | /* | ||
| 907 | * Remember that we are using a pty, which might be a modem line. | ||
| 908 | * Writing too much will clog the line. That's why we are doing this | ||
| 909 | * dance. | ||
| 910 | * FIXME: Migrate the world to Plan 9. | ||
| 911 | */ | ||
| 912 | while (n > 0) { | ||
| 913 | FD_ZERO(&wfd); | ||
| 914 | FD_ZERO(&rfd); | ||
| 915 | FD_SET(cmdfd, &wfd); | ||
| 916 | FD_SET(cmdfd, &rfd); | ||
| 917 | |||
| 918 | /* Check if we can write. */ | ||
| 919 | if (pselect(cmdfd+1, &rfd, &wfd, NULL, NULL, NULL) < 0) { | ||
| 920 | if (errno == EINTR) | ||
| 921 | continue; | ||
| 922 | die("select failed: %s\n", strerror(errno)); | ||
| 923 | } | ||
| 924 | if (FD_ISSET(cmdfd, &wfd)) { | ||
| 925 | /* | ||
| 926 | * Only write the bytes written by ttywrite() or the | ||
| 927 | * default of 256. This seems to be a reasonable value | ||
| 928 | * for a serial line. Bigger values might clog the I/O. | ||
| 929 | */ | ||
| 930 | if ((r = write(cmdfd, s, (n < lim)? n : lim)) < 0) | ||
| 931 | goto write_error; | ||
| 932 | if (r < n) { | ||
| 933 | /* | ||
| 934 | * We weren't able to write out everything. | ||
| 935 | * This means the buffer is getting full | ||
| 936 | * again. Empty it. | ||
| 937 | */ | ||
| 938 | if (n < lim) | ||
| 939 | lim = ttyread(); | ||
| 940 | n -= r; | ||
| 941 | s += r; | ||
| 942 | } else { | ||
| 943 | /* All bytes have been written. */ | ||
| 944 | break; | ||
| 945 | } | ||
| 946 | } | ||
| 947 | if (FD_ISSET(cmdfd, &rfd)) | ||
| 948 | lim = ttyread(); | ||
| 949 | } | ||
| 950 | return; | ||
| 951 | |||
| 952 | write_error: | ||
| 953 | die("write error on tty: %s\n", strerror(errno)); | ||
| 954 | } | ||
| 955 | |||
| 956 | void | ||
| 957 | ttyresize(int tw, int th) | ||
| 958 | { | ||
| 959 | struct winsize w; | ||
| 960 | |||
| 961 | w.ws_row = term.row; | ||
| 962 | w.ws_col = term.col; | ||
| 963 | w.ws_xpixel = tw; | ||
| 964 | w.ws_ypixel = th; | ||
| 965 | if (ioctl(cmdfd, TIOCSWINSZ, &w) < 0) | ||
| 966 | fprintf(stderr, "Couldn't set window size: %s\n", strerror(errno)); | ||
| 967 | } | ||
| 968 | |||
| 969 | void | ||
| 970 | ttyhangup(void) | ||
| 971 | { | ||
| 972 | /* Send SIGHUP to shell */ | ||
| 973 | kill(pid, SIGHUP); | ||
| 974 | } | ||
| 975 | |||
| 976 | int | ||
| 977 | tattrset(int attr) | ||
| 978 | { | ||
| 979 | int i, j; | ||
| 980 | |||
| 981 | for (i = 0; i < term.row-1; i++) { | ||
| 982 | for (j = 0; j < term.col-1; j++) { | ||
| 983 | if (term.line[i][j].mode & attr) | ||
| 984 | return 1; | ||
| 985 | } | ||
| 986 | } | ||
| 987 | |||
| 988 | return 0; | ||
| 989 | } | ||
| 990 | |||
| 991 | void | ||
| 992 | tsetdirt(int top, int bot) | ||
| 993 | { | ||
| 994 | int i; | ||
| 995 | |||
| 996 | LIMIT(top, 0, term.row-1); | ||
| 997 | LIMIT(bot, 0, term.row-1); | ||
| 998 | |||
| 999 | for (i = top; i <= bot; i++) | ||
| 1000 | term.dirty[i] = 1; | ||
| 1001 | } | ||
| 1002 | |||
| 1003 | void | ||
| 1004 | tsetdirtattr(int attr) | ||
| 1005 | { | ||
| 1006 | int i, j; | ||
| 1007 | |||
| 1008 | for (i = 0; i < term.row-1; i++) { | ||
| 1009 | for (j = 0; j < term.col-1; j++) { | ||
| 1010 | if (term.line[i][j].mode & attr) { | ||
| 1011 | tsetdirt(i, i); | ||
| 1012 | break; | ||
| 1013 | } | ||
| 1014 | } | ||
| 1015 | } | ||
| 1016 | } | ||
| 1017 | |||
| 1018 | void | ||
| 1019 | tfulldirt(void) | ||
| 1020 | { | ||
| 1021 | tsetdirt(0, term.row-1); | ||
| 1022 | } | ||
| 1023 | |||
| 1024 | void | ||
| 1025 | tcursor(int mode) | ||
| 1026 | { | ||
| 1027 | static TCursor c[2]; | ||
| 1028 | int alt = IS_SET(MODE_ALTSCREEN); | ||
| 1029 | |||
| 1030 | if (mode == CURSOR_SAVE) { | ||
| 1031 | c[alt] = term.c; | ||
| 1032 | } else if (mode == CURSOR_LOAD) { | ||
| 1033 | term.c = c[alt]; | ||
| 1034 | tmoveto(c[alt].x, c[alt].y); | ||
| 1035 | } | ||
| 1036 | } | ||
| 1037 | |||
| 1038 | void | ||
| 1039 | treset(void) | ||
| 1040 | { | ||
| 1041 | uint i; | ||
| 1042 | |||
| 1043 | term.c = (TCursor){{ | ||
| 1044 | .mode = ATTR_NULL, | ||
| 1045 | .fg = defaultfg, | ||
| 1046 | .bg = defaultbg | ||
| 1047 | }, .x = 0, .y = 0, .state = CURSOR_DEFAULT}; | ||
| 1048 | |||
| 1049 | memset(term.tabs, 0, term.col * sizeof(*term.tabs)); | ||
| 1050 | for (i = tabspaces; i < term.col; i += tabspaces) | ||
| 1051 | term.tabs[i] = 1; | ||
| 1052 | term.top = 0; | ||
| 1053 | term.bot = term.row - 1; | ||
| 1054 | term.mode = MODE_WRAP|MODE_UTF8; | ||
| 1055 | memset(term.trantbl, CS_USA, sizeof(term.trantbl)); | ||
| 1056 | term.charset = 0; | ||
| 1057 | |||
| 1058 | for (i = 0; i < 2; i++) { | ||
| 1059 | tmoveto(0, 0); | ||
| 1060 | tcursor(CURSOR_SAVE); | ||
| 1061 | tclearregion(0, 0, term.col-1, term.row-1); | ||
| 1062 | tswapscreen(); | ||
| 1063 | } | ||
| 1064 | } | ||
| 1065 | |||
| 1066 | void | ||
| 1067 | tnew(int col, int row) | ||
| 1068 | { | ||
| 1069 | term = (Term){ .c = { .attr = { .fg = defaultfg, .bg = defaultbg } } }; | ||
| 1070 | tresize(col, row); | ||
| 1071 | treset(); | ||
| 1072 | } | ||
| 1073 | |||
| 1074 | void | ||
| 1075 | tswapscreen(void) | ||
| 1076 | { | ||
| 1077 | Line *tmp = term.line; | ||
| 1078 | |||
| 1079 | term.line = term.alt; | ||
| 1080 | term.alt = tmp; | ||
| 1081 | term.mode ^= MODE_ALTSCREEN; | ||
| 1082 | tfulldirt(); | ||
| 1083 | } | ||
| 1084 | |||
| 1085 | void | ||
| 1086 | kscrolldown(const Arg* a) | ||
| 1087 | { | ||
| 1088 | int n = a->i; | ||
| 1089 | |||
| 1090 | if (n < 0) | ||
| 1091 | n = term.row + n; | ||
| 1092 | |||
| 1093 | if (n > term.scr) | ||
| 1094 | n = term.scr; | ||
| 1095 | |||
| 1096 | if (term.scr > 0) { | ||
| 1097 | term.scr -= n; | ||
| 1098 | selscroll(0, -n); | ||
| 1099 | tfulldirt(); | ||
| 1100 | } | ||
| 1101 | } | ||
| 1102 | |||
| 1103 | void | ||
| 1104 | kscrollup(const Arg* a) | ||
| 1105 | { | ||
| 1106 | int n = a->i; | ||
| 1107 | |||
| 1108 | if (n < 0) | ||
| 1109 | n = term.row + n; | ||
| 1110 | |||
| 1111 | if (term.scr <= HISTSIZE-n) { | ||
| 1112 | term.scr += n; | ||
| 1113 | selscroll(0, n); | ||
| 1114 | tfulldirt(); | ||
| 1115 | } | ||
| 1116 | } | ||
| 1117 | |||
| 1118 | void | ||
| 1119 | tscrolldown(int orig, int n, int copyhist) | ||
| 1120 | { | ||
| 1121 | int i; | ||
| 1122 | Line temp; | ||
| 1123 | |||
| 1124 | LIMIT(n, 0, term.bot-orig+1); | ||
| 1125 | |||
| 1126 | if (copyhist) { | ||
| 1127 | term.histi = (term.histi - 1 + HISTSIZE) % HISTSIZE; | ||
| 1128 | temp = term.hist[term.histi]; | ||
| 1129 | term.hist[term.histi] = term.line[term.bot]; | ||
| 1130 | term.line[term.bot] = temp; | ||
| 1131 | } | ||
| 1132 | |||
| 1133 | tsetdirt(orig, term.bot-n); | ||
| 1134 | tclearregion(0, term.bot-n+1, term.col-1, term.bot); | ||
| 1135 | |||
| 1136 | for (i = term.bot; i >= orig+n; i--) { | ||
| 1137 | temp = term.line[i]; | ||
| 1138 | term.line[i] = term.line[i-n]; | ||
| 1139 | term.line[i-n] = temp; | ||
| 1140 | } | ||
| 1141 | |||
| 1142 | if (term.scr == 0) | ||
| 1143 | selscroll(orig, n); | ||
| 1144 | } | ||
| 1145 | |||
| 1146 | void | ||
| 1147 | tscrollup(int orig, int n, int copyhist) | ||
| 1148 | { | ||
| 1149 | int i; | ||
| 1150 | Line temp; | ||
| 1151 | |||
| 1152 | LIMIT(n, 0, term.bot-orig+1); | ||
| 1153 | |||
| 1154 | if (copyhist) { | ||
| 1155 | term.histi = (term.histi + 1) % HISTSIZE; | ||
| 1156 | temp = term.hist[term.histi]; | ||
| 1157 | term.hist[term.histi] = term.line[orig]; | ||
| 1158 | term.line[orig] = temp; | ||
| 1159 | } | ||
| 1160 | |||
| 1161 | if (term.scr > 0 && term.scr < HISTSIZE) | ||
| 1162 | term.scr = MIN(term.scr + n, HISTSIZE-1); | ||
| 1163 | |||
| 1164 | tclearregion(0, orig, term.col-1, orig+n-1); | ||
| 1165 | tsetdirt(orig+n, term.bot); | ||
| 1166 | |||
| 1167 | for (i = orig; i <= term.bot-n; i++) { | ||
| 1168 | temp = term.line[i]; | ||
| 1169 | term.line[i] = term.line[i+n]; | ||
| 1170 | term.line[i+n] = temp; | ||
| 1171 | } | ||
| 1172 | |||
| 1173 | if (term.scr == 0) | ||
| 1174 | selscroll(orig, -n); | ||
| 1175 | } | ||
| 1176 | |||
| 1177 | void | ||
| 1178 | selscroll(int orig, int n) | ||
| 1179 | { | ||
| 1180 | if (sel.ob.x == -1) | ||
| 1181 | return; | ||
| 1182 | |||
| 1183 | if (BETWEEN(sel.nb.y, orig, term.bot) != BETWEEN(sel.ne.y, orig, term.bot)) { | ||
| 1184 | selclear(); | ||
| 1185 | } else if (BETWEEN(sel.nb.y, orig, term.bot)) { | ||
| 1186 | sel.ob.y += n; | ||
| 1187 | sel.oe.y += n; | ||
| 1188 | if (sel.ob.y < term.top || sel.ob.y > term.bot || | ||
| 1189 | sel.oe.y < term.top || sel.oe.y > term.bot) { | ||
| 1190 | selclear(); | ||
| 1191 | } else { | ||
| 1192 | selnormalize(); | ||
| 1193 | } | ||
| 1194 | } | ||
| 1195 | } | ||
| 1196 | |||
| 1197 | void | ||
| 1198 | tnewline(int first_col) | ||
| 1199 | { | ||
| 1200 | int y = term.c.y; | ||
| 1201 | |||
| 1202 | if (y == term.bot) { | ||
| 1203 | tscrollup(term.top, 1, 1); | ||
| 1204 | } else { | ||
| 1205 | y++; | ||
| 1206 | } | ||
| 1207 | tmoveto(first_col ? 0 : term.c.x, y); | ||
| 1208 | } | ||
| 1209 | |||
| 1210 | void | ||
| 1211 | csiparse(void) | ||
| 1212 | { | ||
| 1213 | char *p = csiescseq.buf, *np; | ||
| 1214 | long int v; | ||
| 1215 | |||
| 1216 | csiescseq.narg = 0; | ||
| 1217 | if (*p == '?') { | ||
| 1218 | csiescseq.priv = 1; | ||
| 1219 | p++; | ||
| 1220 | } | ||
| 1221 | |||
| 1222 | csiescseq.buf[csiescseq.len] = '\0'; | ||
| 1223 | while (p < csiescseq.buf+csiescseq.len) { | ||
| 1224 | np = NULL; | ||
| 1225 | v = strtol(p, &np, 10); | ||
| 1226 | if (np == p) | ||
| 1227 | v = 0; | ||
| 1228 | if (v == LONG_MAX || v == LONG_MIN) | ||
| 1229 | v = -1; | ||
| 1230 | csiescseq.arg[csiescseq.narg++] = v; | ||
| 1231 | p = np; | ||
| 1232 | if (*p != ';' || csiescseq.narg == ESC_ARG_SIZ) | ||
| 1233 | break; | ||
| 1234 | p++; | ||
| 1235 | } | ||
| 1236 | csiescseq.mode[0] = *p++; | ||
| 1237 | csiescseq.mode[1] = (p < csiescseq.buf+csiescseq.len) ? *p : '\0'; | ||
| 1238 | } | ||
| 1239 | |||
| 1240 | /* for absolute user moves, when decom is set */ | ||
| 1241 | void | ||
| 1242 | tmoveato(int x, int y) | ||
| 1243 | { | ||
| 1244 | tmoveto(x, y + ((term.c.state & CURSOR_ORIGIN) ? term.top: 0)); | ||
| 1245 | } | ||
| 1246 | |||
| 1247 | void | ||
| 1248 | tmoveto(int x, int y) | ||
| 1249 | { | ||
| 1250 | int miny, maxy; | ||
| 1251 | |||
| 1252 | if (term.c.state & CURSOR_ORIGIN) { | ||
| 1253 | miny = term.top; | ||
| 1254 | maxy = term.bot; | ||
| 1255 | } else { | ||
| 1256 | miny = 0; | ||
| 1257 | maxy = term.row - 1; | ||
| 1258 | } | ||
| 1259 | term.c.state &= ~CURSOR_WRAPNEXT; | ||
| 1260 | term.c.x = LIMIT(x, 0, term.col-1); | ||
| 1261 | term.c.y = LIMIT(y, miny, maxy); | ||
| 1262 | } | ||
| 1263 | |||
| 1264 | void | ||
| 1265 | tsetchar(Rune u, const Glyph *attr, int x, int y) | ||
| 1266 | { | ||
| 1267 | static const char *vt100_0[62] = { /* 0x41 - 0x7e */ | ||
| 1268 | "↑", "↓", "→", "←", "█", "▚", "☃", /* A - G */ | ||
| 1269 | 0, 0, 0, 0, 0, 0, 0, 0, /* H - O */ | ||
| 1270 | 0, 0, 0, 0, 0, 0, 0, 0, /* P - W */ | ||
| 1271 | 0, 0, 0, 0, 0, 0, 0, " ", /* X - _ */ | ||
| 1272 | "◆", "▒", "␉", "␌", "␍", "␊", "°", "±", /* ` - g */ | ||
| 1273 | "", "␋", "┘", "┐", "┌", "└", "┼", "⎺", /* h - o */ | ||
| 1274 | "⎻", "─", "⎼", "⎽", "├", "┤", "┴", "┬", /* p - w */ | ||
| 1275 | "│", "≤", "≥", "π", "≠", "£", "·", /* x - ~ */ | ||
| 1276 | }; | ||
| 1277 | |||
| 1278 | /* | ||
| 1279 | * The table is proudly stolen from rxvt. | ||
| 1280 | */ | ||
| 1281 | if (term.trantbl[term.charset] == CS_GRAPHIC0 && | ||
| 1282 | BETWEEN(u, 0x41, 0x7e) && vt100_0[u - 0x41]) | ||
| 1283 | utf8decode(vt100_0[u - 0x41], &u, UTF_SIZ); | ||
| 1284 | |||
| 1285 | if (term.line[y][x].mode & ATTR_WIDE) { | ||
| 1286 | if (x+1 < term.col) { | ||
| 1287 | term.line[y][x+1].u = ' '; | ||
| 1288 | term.line[y][x+1].mode &= ~ATTR_WDUMMY; | ||
| 1289 | } | ||
| 1290 | } else if (term.line[y][x].mode & ATTR_WDUMMY) { | ||
| 1291 | term.line[y][x-1].u = ' '; | ||
| 1292 | term.line[y][x-1].mode &= ~ATTR_WIDE; | ||
| 1293 | } | ||
| 1294 | |||
| 1295 | term.dirty[y] = 1; | ||
| 1296 | term.line[y][x] = *attr; | ||
| 1297 | term.line[y][x].u = u; | ||
| 1298 | |||
| 1299 | if (isboxdraw(u)) | ||
| 1300 | term.line[y][x].mode |= ATTR_BOXDRAW; | ||
| 1301 | } | ||
| 1302 | |||
| 1303 | void | ||
| 1304 | tclearregion(int x1, int y1, int x2, int y2) | ||
| 1305 | { | ||
| 1306 | int x, y, temp; | ||
| 1307 | Glyph *gp; | ||
| 1308 | |||
| 1309 | if (x1 > x2) | ||
| 1310 | temp = x1, x1 = x2, x2 = temp; | ||
| 1311 | if (y1 > y2) | ||
| 1312 | temp = y1, y1 = y2, y2 = temp; | ||
| 1313 | |||
| 1314 | LIMIT(x1, 0, term.maxcol-1); | ||
| 1315 | LIMIT(x2, 0, term.maxcol-1); | ||
| 1316 | LIMIT(y1, 0, term.row-1); | ||
| 1317 | LIMIT(y2, 0, term.row-1); | ||
| 1318 | |||
| 1319 | for (y = y1; y <= y2; y++) { | ||
| 1320 | term.dirty[y] = 1; | ||
| 1321 | for (x = x1; x <= x2; x++) { | ||
| 1322 | gp = &term.line[y][x]; | ||
| 1323 | if (selected(x, y)) | ||
| 1324 | selclear(); | ||
| 1325 | gp->fg = term.c.attr.fg; | ||
| 1326 | gp->bg = term.c.attr.bg; | ||
| 1327 | gp->mode = 0; | ||
| 1328 | gp->u = ' '; | ||
| 1329 | } | ||
| 1330 | } | ||
| 1331 | } | ||
| 1332 | |||
| 1333 | void | ||
| 1334 | tdeletechar(int n) | ||
| 1335 | { | ||
| 1336 | int dst, src, size; | ||
| 1337 | Glyph *line; | ||
| 1338 | |||
| 1339 | LIMIT(n, 0, term.col - term.c.x); | ||
| 1340 | |||
| 1341 | dst = term.c.x; | ||
| 1342 | src = term.c.x + n; | ||
| 1343 | size = term.col - src; | ||
| 1344 | line = term.line[term.c.y]; | ||
| 1345 | |||
| 1346 | memmove(&line[dst], &line[src], size * sizeof(Glyph)); | ||
| 1347 | tclearregion(term.col-n, term.c.y, term.col-1, term.c.y); | ||
| 1348 | } | ||
| 1349 | |||
| 1350 | void | ||
| 1351 | tinsertblank(int n) | ||
| 1352 | { | ||
| 1353 | int dst, src, size; | ||
| 1354 | Glyph *line; | ||
| 1355 | |||
| 1356 | LIMIT(n, 0, term.col - term.c.x); | ||
| 1357 | |||
| 1358 | dst = term.c.x + n; | ||
| 1359 | src = term.c.x; | ||
| 1360 | size = term.col - dst; | ||
| 1361 | line = term.line[term.c.y]; | ||
| 1362 | |||
| 1363 | memmove(&line[dst], &line[src], size * sizeof(Glyph)); | ||
| 1364 | tclearregion(src, term.c.y, dst - 1, term.c.y); | ||
| 1365 | } | ||
| 1366 | |||
| 1367 | void | ||
| 1368 | tinsertblankline(int n) | ||
| 1369 | { | ||
| 1370 | if (BETWEEN(term.c.y, term.top, term.bot)) | ||
| 1371 | tscrolldown(term.c.y, n, 0); | ||
| 1372 | } | ||
| 1373 | |||
| 1374 | void | ||
| 1375 | tdeleteline(int n) | ||
| 1376 | { | ||
| 1377 | if (BETWEEN(term.c.y, term.top, term.bot)) | ||
| 1378 | tscrollup(term.c.y, n, 0); | ||
| 1379 | } | ||
| 1380 | |||
| 1381 | int32_t | ||
| 1382 | tdefcolor(const int *attr, int *npar, int l) | ||
| 1383 | { | ||
| 1384 | int32_t idx = -1; | ||
| 1385 | uint r, g, b; | ||
| 1386 | |||
| 1387 | switch (attr[*npar + 1]) { | ||
| 1388 | case 2: /* direct color in RGB space */ | ||
| 1389 | if (*npar + 4 >= l) { | ||
| 1390 | fprintf(stderr, | ||
| 1391 | "erresc(38): Incorrect number of parameters (%d)\n", | ||
| 1392 | *npar); | ||
| 1393 | break; | ||
| 1394 | } | ||
| 1395 | r = attr[*npar + 2]; | ||
| 1396 | g = attr[*npar + 3]; | ||
| 1397 | b = attr[*npar + 4]; | ||
| 1398 | *npar += 4; | ||
| 1399 | if (!BETWEEN(r, 0, 255) || !BETWEEN(g, 0, 255) || !BETWEEN(b, 0, 255)) | ||
| 1400 | fprintf(stderr, "erresc: bad rgb color (%u,%u,%u)\n", | ||
| 1401 | r, g, b); | ||
| 1402 | else | ||
| 1403 | idx = TRUECOLOR(r, g, b); | ||
| 1404 | break; | ||
| 1405 | case 5: /* indexed color */ | ||
| 1406 | if (*npar + 2 >= l) { | ||
| 1407 | fprintf(stderr, | ||
| 1408 | "erresc(38): Incorrect number of parameters (%d)\n", | ||
| 1409 | *npar); | ||
| 1410 | break; | ||
| 1411 | } | ||
| 1412 | *npar += 2; | ||
| 1413 | if (!BETWEEN(attr[*npar], 0, 255)) | ||
| 1414 | fprintf(stderr, "erresc: bad fgcolor %d\n", attr[*npar]); | ||
| 1415 | else | ||
| 1416 | idx = attr[*npar]; | ||
| 1417 | break; | ||
| 1418 | case 0: /* implemented defined (only foreground) */ | ||
| 1419 | case 1: /* transparent */ | ||
| 1420 | case 3: /* direct color in CMY space */ | ||
| 1421 | case 4: /* direct color in CMYK space */ | ||
| 1422 | default: | ||
| 1423 | fprintf(stderr, | ||
| 1424 | "erresc(38): gfx attr %d unknown\n", attr[*npar]); | ||
| 1425 | break; | ||
| 1426 | } | ||
| 1427 | |||
| 1428 | return idx; | ||
| 1429 | } | ||
| 1430 | |||
| 1431 | void | ||
| 1432 | tsetattr(const int *attr, int l) | ||
| 1433 | { | ||
| 1434 | int i; | ||
| 1435 | int32_t idx; | ||
| 1436 | |||
| 1437 | for (i = 0; i < l; i++) { | ||
| 1438 | switch (attr[i]) { | ||
| 1439 | case 0: | ||
| 1440 | term.c.attr.mode &= ~( | ||
| 1441 | ATTR_BOLD | | ||
| 1442 | ATTR_FAINT | | ||
| 1443 | ATTR_ITALIC | | ||
| 1444 | ATTR_UNDERLINE | | ||
| 1445 | ATTR_BLINK | | ||
| 1446 | ATTR_REVERSE | | ||
| 1447 | ATTR_INVISIBLE | | ||
| 1448 | ATTR_STRUCK ); | ||
| 1449 | term.c.attr.fg = defaultfg; | ||
| 1450 | term.c.attr.bg = defaultbg; | ||
| 1451 | break; | ||
| 1452 | case 1: | ||
| 1453 | term.c.attr.mode |= ATTR_BOLD; | ||
| 1454 | break; | ||
| 1455 | case 2: | ||
| 1456 | term.c.attr.mode |= ATTR_FAINT; | ||
| 1457 | break; | ||
| 1458 | case 3: | ||
| 1459 | term.c.attr.mode |= ATTR_ITALIC; | ||
| 1460 | break; | ||
| 1461 | case 4: | ||
| 1462 | term.c.attr.mode |= ATTR_UNDERLINE; | ||
| 1463 | break; | ||
| 1464 | case 5: /* slow blink */ | ||
| 1465 | /* FALLTHROUGH */ | ||
| 1466 | case 6: /* rapid blink */ | ||
| 1467 | term.c.attr.mode |= ATTR_BLINK; | ||
| 1468 | break; | ||
| 1469 | case 7: | ||
| 1470 | term.c.attr.mode |= ATTR_REVERSE; | ||
| 1471 | break; | ||
| 1472 | case 8: | ||
| 1473 | term.c.attr.mode |= ATTR_INVISIBLE; | ||
| 1474 | break; | ||
| 1475 | case 9: | ||
| 1476 | term.c.attr.mode |= ATTR_STRUCK; | ||
| 1477 | break; | ||
| 1478 | case 22: | ||
| 1479 | term.c.attr.mode &= ~(ATTR_BOLD | ATTR_FAINT); | ||
| 1480 | break; | ||
| 1481 | case 23: | ||
| 1482 | term.c.attr.mode &= ~ATTR_ITALIC; | ||
| 1483 | break; | ||
| 1484 | case 24: | ||
| 1485 | term.c.attr.mode &= ~ATTR_UNDERLINE; | ||
| 1486 | break; | ||
| 1487 | case 25: | ||
| 1488 | term.c.attr.mode &= ~ATTR_BLINK; | ||
| 1489 | break; | ||
| 1490 | case 27: | ||
| 1491 | term.c.attr.mode &= ~ATTR_REVERSE; | ||
| 1492 | break; | ||
| 1493 | case 28: | ||
| 1494 | term.c.attr.mode &= ~ATTR_INVISIBLE; | ||
| 1495 | break; | ||
| 1496 | case 29: | ||
| 1497 | term.c.attr.mode &= ~ATTR_STRUCK; | ||
| 1498 | break; | ||
| 1499 | case 38: | ||
| 1500 | if ((idx = tdefcolor(attr, &i, l)) >= 0) | ||
| 1501 | term.c.attr.fg = idx; | ||
| 1502 | break; | ||
| 1503 | case 39: | ||
| 1504 | term.c.attr.fg = defaultfg; | ||
| 1505 | break; | ||
| 1506 | case 48: | ||
| 1507 | if ((idx = tdefcolor(attr, &i, l)) >= 0) | ||
| 1508 | term.c.attr.bg = idx; | ||
| 1509 | break; | ||
| 1510 | case 49: | ||
| 1511 | term.c.attr.bg = defaultbg; | ||
| 1512 | break; | ||
| 1513 | default: | ||
| 1514 | if (BETWEEN(attr[i], 30, 37)) { | ||
| 1515 | term.c.attr.fg = attr[i] - 30; | ||
| 1516 | } else if (BETWEEN(attr[i], 40, 47)) { | ||
| 1517 | term.c.attr.bg = attr[i] - 40; | ||
| 1518 | } else if (BETWEEN(attr[i], 90, 97)) { | ||
| 1519 | term.c.attr.fg = attr[i] - 90 + 8; | ||
| 1520 | } else if (BETWEEN(attr[i], 100, 107)) { | ||
| 1521 | term.c.attr.bg = attr[i] - 100 + 8; | ||
| 1522 | } else { | ||
| 1523 | fprintf(stderr, | ||
| 1524 | "erresc(default): gfx attr %d unknown\n", | ||
| 1525 | attr[i]); | ||
| 1526 | csidump(); | ||
| 1527 | } | ||
| 1528 | break; | ||
| 1529 | } | ||
| 1530 | } | ||
| 1531 | } | ||
| 1532 | |||
| 1533 | void | ||
| 1534 | tsetscroll(int t, int b) | ||
| 1535 | { | ||
| 1536 | int temp; | ||
| 1537 | |||
| 1538 | LIMIT(t, 0, term.row-1); | ||
| 1539 | LIMIT(b, 0, term.row-1); | ||
| 1540 | if (t > b) { | ||
| 1541 | temp = t; | ||
| 1542 | t = b; | ||
| 1543 | b = temp; | ||
| 1544 | } | ||
| 1545 | term.top = t; | ||
| 1546 | term.bot = b; | ||
| 1547 | } | ||
| 1548 | |||
| 1549 | void | ||
| 1550 | tsetmode(int priv, int set, const int *args, int narg) | ||
| 1551 | { | ||
| 1552 | int alt; const int *lim; | ||
| 1553 | |||
| 1554 | for (lim = args + narg; args < lim; ++args) { | ||
| 1555 | if (priv) { | ||
| 1556 | switch (*args) { | ||
| 1557 | case 1: /* DECCKM -- Cursor key */ | ||
| 1558 | xsetmode(set, MODE_APPCURSOR); | ||
| 1559 | break; | ||
| 1560 | case 5: /* DECSCNM -- Reverse video */ | ||
| 1561 | xsetmode(set, MODE_REVERSE); | ||
| 1562 | break; | ||
| 1563 | case 6: /* DECOM -- Origin */ | ||
| 1564 | MODBIT(term.c.state, set, CURSOR_ORIGIN); | ||
| 1565 | tmoveato(0, 0); | ||
| 1566 | break; | ||
| 1567 | case 7: /* DECAWM -- Auto wrap */ | ||
| 1568 | MODBIT(term.mode, set, MODE_WRAP); | ||
| 1569 | break; | ||
| 1570 | case 0: /* Error (IGNORED) */ | ||
| 1571 | case 2: /* DECANM -- ANSI/VT52 (IGNORED) */ | ||
| 1572 | case 3: /* DECCOLM -- Column (IGNORED) */ | ||
| 1573 | case 4: /* DECSCLM -- Scroll (IGNORED) */ | ||
| 1574 | case 8: /* DECARM -- Auto repeat (IGNORED) */ | ||
| 1575 | case 18: /* DECPFF -- Printer feed (IGNORED) */ | ||
| 1576 | case 19: /* DECPEX -- Printer extent (IGNORED) */ | ||
| 1577 | case 42: /* DECNRCM -- National characters (IGNORED) */ | ||
| 1578 | case 12: /* att610 -- Start blinking cursor (IGNORED) */ | ||
| 1579 | break; | ||
| 1580 | case 25: /* DECTCEM -- Text Cursor Enable Mode */ | ||
| 1581 | xsetmode(!set, MODE_HIDE); | ||
| 1582 | break; | ||
| 1583 | case 9: /* X10 mouse compatibility mode */ | ||
| 1584 | xsetpointermotion(0); | ||
| 1585 | xsetmode(0, MODE_MOUSE); | ||
| 1586 | xsetmode(set, MODE_MOUSEX10); | ||
| 1587 | break; | ||
| 1588 | case 1000: /* 1000: report button press */ | ||
| 1589 | xsetpointermotion(0); | ||
| 1590 | xsetmode(0, MODE_MOUSE); | ||
| 1591 | xsetmode(set, MODE_MOUSEBTN); | ||
| 1592 | break; | ||
| 1593 | case 1002: /* 1002: report motion on button press */ | ||
| 1594 | xsetpointermotion(0); | ||
| 1595 | xsetmode(0, MODE_MOUSE); | ||
| 1596 | xsetmode(set, MODE_MOUSEMOTION); | ||
| 1597 | break; | ||
| 1598 | case 1003: /* 1003: enable all mouse motions */ | ||
| 1599 | xsetpointermotion(set); | ||
| 1600 | xsetmode(0, MODE_MOUSE); | ||
| 1601 | xsetmode(set, MODE_MOUSEMANY); | ||
| 1602 | break; | ||
| 1603 | case 1004: /* 1004: send focus events to tty */ | ||
| 1604 | xsetmode(set, MODE_FOCUS); | ||
| 1605 | break; | ||
| 1606 | case 1006: /* 1006: extended reporting mode */ | ||
| 1607 | xsetmode(set, MODE_MOUSESGR); | ||
| 1608 | break; | ||
| 1609 | case 1034: | ||
| 1610 | xsetmode(set, MODE_8BIT); | ||
| 1611 | break; | ||
| 1612 | case 1049: /* swap screen & set/restore cursor as xterm */ | ||
| 1613 | if (!allowaltscreen) | ||
| 1614 | break; | ||
| 1615 | tcursor((set) ? CURSOR_SAVE : CURSOR_LOAD); | ||
| 1616 | /* FALLTHROUGH */ | ||
| 1617 | case 47: /* swap screen */ | ||
| 1618 | case 1047: | ||
| 1619 | if (!allowaltscreen) | ||
| 1620 | break; | ||
| 1621 | alt = IS_SET(MODE_ALTSCREEN); | ||
| 1622 | if (alt) { | ||
| 1623 | tclearregion(0, 0, term.col-1, | ||
| 1624 | term.row-1); | ||
| 1625 | } | ||
| 1626 | if (set ^ alt) /* set is always 1 or 0 */ | ||
| 1627 | tswapscreen(); | ||
| 1628 | if (*args != 1049) | ||
| 1629 | break; | ||
| 1630 | /* FALLTHROUGH */ | ||
| 1631 | case 1048: | ||
| 1632 | tcursor((set) ? CURSOR_SAVE : CURSOR_LOAD); | ||
| 1633 | break; | ||
| 1634 | case 2004: /* 2004: bracketed paste mode */ | ||
| 1635 | xsetmode(set, MODE_BRCKTPASTE); | ||
| 1636 | break; | ||
| 1637 | /* Not implemented mouse modes. See comments there. */ | ||
| 1638 | case 1001: /* mouse highlight mode; can hang the | ||
| 1639 | terminal by design when implemented. */ | ||
| 1640 | case 1005: /* UTF-8 mouse mode; will confuse | ||
| 1641 | applications not supporting UTF-8 | ||
| 1642 | and luit. */ | ||
| 1643 | case 1015: /* urxvt mangled mouse mode; incompatible | ||
| 1644 | and can be mistaken for other control | ||
| 1645 | codes. */ | ||
| 1646 | break; | ||
| 1647 | default: | ||
| 1648 | fprintf(stderr, | ||
| 1649 | "erresc: unknown private set/reset mode %d\n", | ||
| 1650 | *args); | ||
| 1651 | break; | ||
| 1652 | } | ||
| 1653 | } else { | ||
| 1654 | switch (*args) { | ||
| 1655 | case 0: /* Error (IGNORED) */ | ||
| 1656 | break; | ||
| 1657 | case 2: | ||
| 1658 | xsetmode(set, MODE_KBDLOCK); | ||
| 1659 | break; | ||
| 1660 | case 4: /* IRM -- Insertion-replacement */ | ||
| 1661 | MODBIT(term.mode, set, MODE_INSERT); | ||
| 1662 | break; | ||
| 1663 | case 12: /* SRM -- Send/Receive */ | ||
| 1664 | MODBIT(term.mode, !set, MODE_ECHO); | ||
| 1665 | break; | ||
| 1666 | case 20: /* LNM -- Linefeed/new line */ | ||
| 1667 | MODBIT(term.mode, set, MODE_CRLF); | ||
| 1668 | break; | ||
| 1669 | default: | ||
| 1670 | fprintf(stderr, | ||
| 1671 | "erresc: unknown set/reset mode %d\n", | ||
| 1672 | *args); | ||
| 1673 | break; | ||
| 1674 | } | ||
| 1675 | } | ||
| 1676 | } | ||
| 1677 | } | ||
| 1678 | |||
| 1679 | void | ||
| 1680 | csihandle(void) | ||
| 1681 | { | ||
| 1682 | char buf[40]; | ||
| 1683 | int len; | ||
| 1684 | |||
| 1685 | switch (csiescseq.mode[0]) { | ||
| 1686 | default: | ||
| 1687 | unknown: | ||
| 1688 | fprintf(stderr, "erresc: unknown csi "); | ||
| 1689 | csidump(); | ||
| 1690 | /* die(""); */ | ||
| 1691 | break; | ||
| 1692 | case '@': /* ICH -- Insert <n> blank char */ | ||
| 1693 | DEFAULT(csiescseq.arg[0], 1); | ||
| 1694 | tinsertblank(csiescseq.arg[0]); | ||
| 1695 | break; | ||
| 1696 | case 'A': /* CUU -- Cursor <n> Up */ | ||
| 1697 | DEFAULT(csiescseq.arg[0], 1); | ||
| 1698 | tmoveto(term.c.x, term.c.y-csiescseq.arg[0]); | ||
| 1699 | break; | ||
| 1700 | case 'B': /* CUD -- Cursor <n> Down */ | ||
| 1701 | case 'e': /* VPR --Cursor <n> Down */ | ||
| 1702 | DEFAULT(csiescseq.arg[0], 1); | ||
| 1703 | tmoveto(term.c.x, term.c.y+csiescseq.arg[0]); | ||
| 1704 | break; | ||
| 1705 | case 'i': /* MC -- Media Copy */ | ||
| 1706 | switch (csiescseq.arg[0]) { | ||
| 1707 | case 0: | ||
| 1708 | tdump(); | ||
| 1709 | break; | ||
| 1710 | case 1: | ||
| 1711 | tdumpline(term.c.y); | ||
| 1712 | break; | ||
| 1713 | case 2: | ||
| 1714 | tdumpsel(); | ||
| 1715 | break; | ||
| 1716 | case 4: | ||
| 1717 | term.mode &= ~MODE_PRINT; | ||
| 1718 | break; | ||
| 1719 | case 5: | ||
| 1720 | term.mode |= MODE_PRINT; | ||
| 1721 | break; | ||
| 1722 | } | ||
| 1723 | break; | ||
| 1724 | case 'c': /* DA -- Device Attributes */ | ||
| 1725 | if (csiescseq.arg[0] == 0) | ||
| 1726 | ttywrite(vtiden, strlen(vtiden), 0); | ||
| 1727 | break; | ||
| 1728 | case 'b': /* REP -- if last char is printable print it <n> more times */ | ||
| 1729 | DEFAULT(csiescseq.arg[0], 1); | ||
| 1730 | if (term.lastc) | ||
| 1731 | while (csiescseq.arg[0]-- > 0) | ||
| 1732 | tputc(term.lastc); | ||
| 1733 | break; | ||
| 1734 | case 'C': /* CUF -- Cursor <n> Forward */ | ||
| 1735 | case 'a': /* HPR -- Cursor <n> Forward */ | ||
| 1736 | DEFAULT(csiescseq.arg[0], 1); | ||
| 1737 | tmoveto(term.c.x+csiescseq.arg[0], term.c.y); | ||
| 1738 | break; | ||
| 1739 | case 'D': /* CUB -- Cursor <n> Backward */ | ||
| 1740 | DEFAULT(csiescseq.arg[0], 1); | ||
| 1741 | tmoveto(term.c.x-csiescseq.arg[0], term.c.y); | ||
| 1742 | break; | ||
| 1743 | case 'E': /* CNL -- Cursor <n> Down and first col */ | ||
| 1744 | DEFAULT(csiescseq.arg[0], 1); | ||
| 1745 | tmoveto(0, term.c.y+csiescseq.arg[0]); | ||
| 1746 | break; | ||
| 1747 | case 'F': /* CPL -- Cursor <n> Up and first col */ | ||
| 1748 | DEFAULT(csiescseq.arg[0], 1); | ||
| 1749 | tmoveto(0, term.c.y-csiescseq.arg[0]); | ||
| 1750 | break; | ||
| 1751 | case 'g': /* TBC -- Tabulation clear */ | ||
| 1752 | switch (csiescseq.arg[0]) { | ||
| 1753 | case 0: /* clear current tab stop */ | ||
| 1754 | term.tabs[term.c.x] = 0; | ||
| 1755 | break; | ||
| 1756 | case 3: /* clear all the tabs */ | ||
| 1757 | memset(term.tabs, 0, term.col * sizeof(*term.tabs)); | ||
| 1758 | break; | ||
| 1759 | default: | ||
| 1760 | goto unknown; | ||
| 1761 | } | ||
| 1762 | break; | ||
| 1763 | case 'G': /* CHA -- Move to <col> */ | ||
| 1764 | case '`': /* HPA */ | ||
| 1765 | DEFAULT(csiescseq.arg[0], 1); | ||
| 1766 | tmoveto(csiescseq.arg[0]-1, term.c.y); | ||
| 1767 | break; | ||
| 1768 | case 'H': /* CUP -- Move to <row> <col> */ | ||
| 1769 | case 'f': /* HVP */ | ||
| 1770 | DEFAULT(csiescseq.arg[0], 1); | ||
| 1771 | DEFAULT(csiescseq.arg[1], 1); | ||
| 1772 | tmoveato(csiescseq.arg[1]-1, csiescseq.arg[0]-1); | ||
| 1773 | break; | ||
| 1774 | case 'I': /* CHT -- Cursor Forward Tabulation <n> tab stops */ | ||
| 1775 | DEFAULT(csiescseq.arg[0], 1); | ||
| 1776 | tputtab(csiescseq.arg[0]); | ||
| 1777 | break; | ||
| 1778 | case 'J': /* ED -- Clear screen */ | ||
| 1779 | switch (csiescseq.arg[0]) { | ||
| 1780 | case 0: /* below */ | ||
| 1781 | tclearregion(term.c.x, term.c.y, term.col-1, term.c.y); | ||
| 1782 | if (term.c.y < term.row-1) { | ||
| 1783 | tclearregion(0, term.c.y+1, term.col-1, | ||
| 1784 | term.row-1); | ||
| 1785 | } | ||
| 1786 | break; | ||
| 1787 | case 1: /* above */ | ||
| 1788 | if (term.c.y > 1) | ||
| 1789 | tclearregion(0, 0, term.col-1, term.c.y-1); | ||
| 1790 | tclearregion(0, term.c.y, term.c.x, term.c.y); | ||
| 1791 | break; | ||
| 1792 | case 2: /* all */ | ||
| 1793 | tclearregion(0, 0, term.col-1, term.row-1); | ||
| 1794 | break; | ||
| 1795 | default: | ||
| 1796 | goto unknown; | ||
| 1797 | } | ||
| 1798 | break; | ||
| 1799 | case 'K': /* EL -- Clear line */ | ||
| 1800 | switch (csiescseq.arg[0]) { | ||
| 1801 | case 0: /* right */ | ||
| 1802 | tclearregion(term.c.x, term.c.y, term.col-1, | ||
| 1803 | term.c.y); | ||
| 1804 | break; | ||
| 1805 | case 1: /* left */ | ||
| 1806 | tclearregion(0, term.c.y, term.c.x, term.c.y); | ||
| 1807 | break; | ||
| 1808 | case 2: /* all */ | ||
| 1809 | tclearregion(0, term.c.y, term.col-1, term.c.y); | ||
| 1810 | break; | ||
| 1811 | } | ||
| 1812 | break; | ||
| 1813 | case 'S': /* SU -- Scroll <n> line up */ | ||
| 1814 | DEFAULT(csiescseq.arg[0], 1); | ||
| 1815 | tscrollup(term.top, csiescseq.arg[0], 0); | ||
| 1816 | break; | ||
| 1817 | case 'T': /* SD -- Scroll <n> line down */ | ||
| 1818 | DEFAULT(csiescseq.arg[0], 1); | ||
| 1819 | tscrolldown(term.top, csiescseq.arg[0], 0); | ||
| 1820 | break; | ||
| 1821 | case 'L': /* IL -- Insert <n> blank lines */ | ||
| 1822 | DEFAULT(csiescseq.arg[0], 1); | ||
| 1823 | tinsertblankline(csiescseq.arg[0]); | ||
| 1824 | break; | ||
| 1825 | case 'l': /* RM -- Reset Mode */ | ||
| 1826 | tsetmode(csiescseq.priv, 0, csiescseq.arg, csiescseq.narg); | ||
| 1827 | break; | ||
| 1828 | case 'M': /* DL -- Delete <n> lines */ | ||
| 1829 | DEFAULT(csiescseq.arg[0], 1); | ||
| 1830 | tdeleteline(csiescseq.arg[0]); | ||
| 1831 | break; | ||
| 1832 | case 'X': /* ECH -- Erase <n> char */ | ||
| 1833 | DEFAULT(csiescseq.arg[0], 1); | ||
| 1834 | tclearregion(term.c.x, term.c.y, | ||
| 1835 | term.c.x + csiescseq.arg[0] - 1, term.c.y); | ||
| 1836 | break; | ||
| 1837 | case 'P': /* DCH -- Delete <n> char */ | ||
| 1838 | DEFAULT(csiescseq.arg[0], 1); | ||
| 1839 | tdeletechar(csiescseq.arg[0]); | ||
| 1840 | break; | ||
| 1841 | case 'Z': /* CBT -- Cursor Backward Tabulation <n> tab stops */ | ||
| 1842 | DEFAULT(csiescseq.arg[0], 1); | ||
| 1843 | tputtab(-csiescseq.arg[0]); | ||
| 1844 | break; | ||
| 1845 | case 'd': /* VPA -- Move to <row> */ | ||
| 1846 | DEFAULT(csiescseq.arg[0], 1); | ||
| 1847 | tmoveato(term.c.x, csiescseq.arg[0]-1); | ||
| 1848 | break; | ||
| 1849 | case 'h': /* SM -- Set terminal mode */ | ||
| 1850 | tsetmode(csiescseq.priv, 1, csiescseq.arg, csiescseq.narg); | ||
| 1851 | break; | ||
| 1852 | case 'm': /* SGR -- Terminal attribute (color) */ | ||
| 1853 | tsetattr(csiescseq.arg, csiescseq.narg); | ||
| 1854 | break; | ||
| 1855 | case 'n': /* DSR – Device Status Report (cursor position) */ | ||
| 1856 | if (csiescseq.arg[0] == 6) { | ||
| 1857 | len = snprintf(buf, sizeof(buf), "\033[%i;%iR", | ||
| 1858 | term.c.y+1, term.c.x+1); | ||
| 1859 | ttywrite(buf, len, 0); | ||
| 1860 | } | ||
| 1861 | break; | ||
| 1862 | case 'r': /* DECSTBM -- Set Scrolling Region */ | ||
| 1863 | if (csiescseq.priv) { | ||
| 1864 | goto unknown; | ||
| 1865 | } else { | ||
| 1866 | DEFAULT(csiescseq.arg[0], 1); | ||
| 1867 | DEFAULT(csiescseq.arg[1], term.row); | ||
| 1868 | tsetscroll(csiescseq.arg[0]-1, csiescseq.arg[1]-1); | ||
| 1869 | tmoveato(0, 0); | ||
| 1870 | } | ||
| 1871 | break; | ||
| 1872 | case 's': /* DECSC -- Save cursor position (ANSI.SYS) */ | ||
| 1873 | tcursor(CURSOR_SAVE); | ||
| 1874 | break; | ||
| 1875 | case 'u': /* DECRC -- Restore cursor position (ANSI.SYS) */ | ||
| 1876 | tcursor(CURSOR_LOAD); | ||
| 1877 | break; | ||
| 1878 | case ' ': | ||
| 1879 | switch (csiescseq.mode[1]) { | ||
| 1880 | case 'q': /* DECSCUSR -- Set Cursor Style */ | ||
| 1881 | if (xsetcursor(csiescseq.arg[0])) | ||
| 1882 | goto unknown; | ||
| 1883 | break; | ||
| 1884 | default: | ||
| 1885 | goto unknown; | ||
| 1886 | } | ||
| 1887 | break; | ||
| 1888 | } | ||
| 1889 | } | ||
| 1890 | |||
| 1891 | void | ||
| 1892 | csidump(void) | ||
| 1893 | { | ||
| 1894 | size_t i; | ||
| 1895 | uint c; | ||
| 1896 | |||
| 1897 | fprintf(stderr, "ESC["); | ||
| 1898 | for (i = 0; i < csiescseq.len; i++) { | ||
| 1899 | c = csiescseq.buf[i] & 0xff; | ||
| 1900 | if (isprint(c)) { | ||
| 1901 | putc(c, stderr); | ||
| 1902 | } else if (c == '\n') { | ||
| 1903 | fprintf(stderr, "(\\n)"); | ||
| 1904 | } else if (c == '\r') { | ||
| 1905 | fprintf(stderr, "(\\r)"); | ||
| 1906 | } else if (c == 0x1b) { | ||
| 1907 | fprintf(stderr, "(\\e)"); | ||
| 1908 | } else { | ||
| 1909 | fprintf(stderr, "(%02x)", c); | ||
| 1910 | } | ||
| 1911 | } | ||
| 1912 | putc('\n', stderr); | ||
| 1913 | } | ||
| 1914 | |||
| 1915 | void | ||
| 1916 | csireset(void) | ||
| 1917 | { | ||
| 1918 | memset(&csiescseq, 0, sizeof(csiescseq)); | ||
| 1919 | } | ||
| 1920 | |||
| 1921 | void | ||
| 1922 | osc_color_response(int num, int index, int is_osc4) | ||
| 1923 | { | ||
| 1924 | int n; | ||
| 1925 | char buf[32]; | ||
| 1926 | unsigned char r, g, b; | ||
| 1927 | |||
| 1928 | if (xgetcolor(is_osc4 ? num : index, &r, &g, &b)) { | ||
| 1929 | fprintf(stderr, "erresc: failed to fetch %s color %d\n", | ||
| 1930 | is_osc4 ? "osc4" : "osc", | ||
| 1931 | is_osc4 ? num : index); | ||
| 1932 | return; | ||
| 1933 | } | ||
| 1934 | |||
| 1935 | n = snprintf(buf, sizeof buf, "\033]%s%d;rgb:%02x%02x/%02x%02x/%02x%02x\007", | ||
| 1936 | is_osc4 ? "4;" : "", num, r, r, g, g, b, b); | ||
| 1937 | if (n < 0 || n >= sizeof(buf)) { | ||
| 1938 | fprintf(stderr, "error: %s while printing %s response\n", | ||
| 1939 | n < 0 ? "snprintf failed" : "truncation occurred", | ||
| 1940 | is_osc4 ? "osc4" : "osc"); | ||
| 1941 | } else { | ||
| 1942 | ttywrite(buf, n, 1); | ||
| 1943 | } | ||
| 1944 | } | ||
| 1945 | |||
| 1946 | void | ||
| 1947 | strhandle(void) | ||
| 1948 | { | ||
| 1949 | char *p = NULL, *dec; | ||
| 1950 | int j, narg, par; | ||
| 1951 | const struct { int idx; char *str; } osc_table[] = { | ||
| 1952 | { defaultfg, "foreground" }, | ||
| 1953 | { defaultbg, "background" }, | ||
| 1954 | { defaultcs, "cursor" } | ||
| 1955 | }; | ||
| 1956 | |||
| 1957 | term.esc &= ~(ESC_STR_END|ESC_STR); | ||
| 1958 | strparse(); | ||
| 1959 | par = (narg = strescseq.narg) ? atoi(strescseq.args[0]) : 0; | ||
| 1960 | |||
| 1961 | switch (strescseq.type) { | ||
| 1962 | case ']': /* OSC -- Operating System Command */ | ||
| 1963 | switch (par) { | ||
| 1964 | case 0: | ||
| 1965 | if (narg > 1) { | ||
| 1966 | xsettitle(strescseq.args[1]); | ||
| 1967 | xseticontitle(strescseq.args[1]); | ||
| 1968 | } | ||
| 1969 | return; | ||
| 1970 | case 1: | ||
| 1971 | if (narg > 1) | ||
| 1972 | xseticontitle(strescseq.args[1]); | ||
| 1973 | return; | ||
| 1974 | case 2: | ||
| 1975 | if (narg > 1) | ||
| 1976 | xsettitle(strescseq.args[1]); | ||
| 1977 | return; | ||
| 1978 | case 52: | ||
| 1979 | if (narg > 2 && allowwindowops) { | ||
| 1980 | dec = base64dec(strescseq.args[2]); | ||
| 1981 | if (dec) { | ||
| 1982 | xsetsel(dec); | ||
| 1983 | xclipcopy(); | ||
| 1984 | } else { | ||
| 1985 | fprintf(stderr, "erresc: invalid base64\n"); | ||
| 1986 | } | ||
| 1987 | } | ||
| 1988 | return; | ||
| 1989 | case 10: | ||
| 1990 | case 11: | ||
| 1991 | case 12: | ||
| 1992 | if (narg < 2) | ||
| 1993 | break; | ||
| 1994 | p = strescseq.args[1]; | ||
| 1995 | if ((j = par - 10) < 0 || j >= LEN(osc_table)) | ||
| 1996 | break; /* shouldn't be possible */ | ||
| 1997 | |||
| 1998 | if (!strcmp(p, "?")) { | ||
| 1999 | osc_color_response(par, osc_table[j].idx, 0); | ||
| 2000 | } else if (xsetcolorname(osc_table[j].idx, p)) { | ||
| 2001 | fprintf(stderr, "erresc: invalid %s color: %s\n", | ||
| 2002 | osc_table[j].str, p); | ||
| 2003 | } else { | ||
| 2004 | tfulldirt(); | ||
| 2005 | } | ||
| 2006 | return; | ||
| 2007 | case 4: /* color set */ | ||
| 2008 | if (narg < 3) | ||
| 2009 | break; | ||
| 2010 | p = strescseq.args[2]; | ||
| 2011 | /* FALLTHROUGH */ | ||
| 2012 | case 104: /* color reset */ | ||
| 2013 | j = (narg > 1) ? atoi(strescseq.args[1]) : -1; | ||
| 2014 | |||
| 2015 | if (p && !strcmp(p, "?")) { | ||
| 2016 | osc_color_response(j, 0, 1); | ||
| 2017 | } else if (xsetcolorname(j, p)) { | ||
| 2018 | if (par == 104 && narg <= 1) | ||
| 2019 | return; /* color reset without parameter */ | ||
| 2020 | fprintf(stderr, "erresc: invalid color j=%d, p=%s\n", | ||
| 2021 | j, p ? p : "(null)"); | ||
| 2022 | } else { | ||
| 2023 | /* | ||
| 2024 | * TODO if defaultbg color is changed, borders | ||
| 2025 | * are dirty | ||
| 2026 | */ | ||
| 2027 | tfulldirt(); | ||
| 2028 | } | ||
| 2029 | return; | ||
| 2030 | } | ||
| 2031 | break; | ||
| 2032 | case 'k': /* old title set compatibility */ | ||
| 2033 | xsettitle(strescseq.args[0]); | ||
| 2034 | return; | ||
| 2035 | case 'P': /* DCS -- Device Control String */ | ||
| 2036 | case '_': /* APC -- Application Program Command */ | ||
| 2037 | case '^': /* PM -- Privacy Message */ | ||
| 2038 | return; | ||
| 2039 | } | ||
| 2040 | |||
| 2041 | fprintf(stderr, "erresc: unknown str "); | ||
| 2042 | strdump(); | ||
| 2043 | } | ||
| 2044 | |||
| 2045 | void | ||
| 2046 | strparse(void) | ||
| 2047 | { | ||
| 2048 | int c; | ||
| 2049 | char *p = strescseq.buf; | ||
| 2050 | |||
| 2051 | strescseq.narg = 0; | ||
| 2052 | strescseq.buf[strescseq.len] = '\0'; | ||
| 2053 | |||
| 2054 | if (*p == '\0') | ||
| 2055 | return; | ||
| 2056 | |||
| 2057 | while (strescseq.narg < STR_ARG_SIZ) { | ||
| 2058 | strescseq.args[strescseq.narg++] = p; | ||
| 2059 | while ((c = *p) != ';' && c != '\0') | ||
| 2060 | ++p; | ||
| 2061 | if (c == '\0') | ||
| 2062 | return; | ||
| 2063 | *p++ = '\0'; | ||
| 2064 | } | ||
| 2065 | } | ||
| 2066 | |||
| 2067 | void | ||
| 2068 | externalpipe(const Arg *arg) | ||
| 2069 | { | ||
| 2070 | int to[2]; | ||
| 2071 | char buf[UTF_SIZ]; | ||
| 2072 | void (*oldsigpipe)(int); | ||
| 2073 | Glyph *bp, *end; | ||
| 2074 | int lastpos, n, newline; | ||
| 2075 | |||
| 2076 | if (pipe(to) == -1) | ||
| 2077 | return; | ||
| 2078 | |||
| 2079 | switch (fork()) { | ||
| 2080 | case -1: | ||
| 2081 | close(to[0]); | ||
| 2082 | close(to[1]); | ||
| 2083 | return; | ||
| 2084 | case 0: | ||
| 2085 | dup2(to[0], STDIN_FILENO); | ||
| 2086 | close(to[0]); | ||
| 2087 | close(to[1]); | ||
| 2088 | execvp(((char **)arg->v)[0], (char **)arg->v); | ||
| 2089 | fprintf(stderr, "st: execvp %s\n", ((char **)arg->v)[0]); | ||
| 2090 | perror("failed"); | ||
| 2091 | exit(0); | ||
| 2092 | } | ||
| 2093 | |||
| 2094 | close(to[0]); | ||
| 2095 | /* ignore sigpipe for now, in case child exists early */ | ||
| 2096 | oldsigpipe = signal(SIGPIPE, SIG_IGN); | ||
| 2097 | newline = 0; | ||
| 2098 | for (n = 0; n <= HISTSIZE + 2; n++) { | ||
| 2099 | bp = TLINE_HIST(n); | ||
| 2100 | lastpos = MIN(tlinehistlen(n) + 1, term.col) - 1; | ||
| 2101 | if (lastpos < 0) | ||
| 2102 | break; | ||
| 2103 | if (lastpos == 0) | ||
| 2104 | continue; | ||
| 2105 | end = &bp[lastpos + 1]; | ||
| 2106 | for (; bp < end; ++bp) | ||
| 2107 | if (xwrite(to[1], buf, utf8encode(bp->u, buf)) < 0) | ||
| 2108 | break; | ||
| 2109 | if ((newline = TLINE_HIST(n)[lastpos].mode & ATTR_WRAP)) | ||
| 2110 | continue; | ||
| 2111 | if (xwrite(to[1], "\n", 1) < 0) | ||
| 2112 | break; | ||
| 2113 | newline = 0; | ||
| 2114 | } | ||
| 2115 | if (newline) | ||
| 2116 | (void)xwrite(to[1], "\n", 1); | ||
| 2117 | close(to[1]); | ||
| 2118 | /* restore */ | ||
| 2119 | signal(SIGPIPE, oldsigpipe); | ||
| 2120 | } | ||
| 2121 | |||
| 2122 | void | ||
| 2123 | strdump(void) | ||
| 2124 | { | ||
| 2125 | size_t i; | ||
| 2126 | uint c; | ||
| 2127 | |||
| 2128 | fprintf(stderr, "ESC%c", strescseq.type); | ||
| 2129 | for (i = 0; i < strescseq.len; i++) { | ||
| 2130 | c = strescseq.buf[i] & 0xff; | ||
| 2131 | if (c == '\0') { | ||
| 2132 | putc('\n', stderr); | ||
| 2133 | return; | ||
| 2134 | } else if (isprint(c)) { | ||
| 2135 | putc(c, stderr); | ||
| 2136 | } else if (c == '\n') { | ||
| 2137 | fprintf(stderr, "(\\n)"); | ||
| 2138 | } else if (c == '\r') { | ||
| 2139 | fprintf(stderr, "(\\r)"); | ||
| 2140 | } else if (c == 0x1b) { | ||
| 2141 | fprintf(stderr, "(\\e)"); | ||
| 2142 | } else { | ||
| 2143 | fprintf(stderr, "(%02x)", c); | ||
| 2144 | } | ||
| 2145 | } | ||
| 2146 | fprintf(stderr, "ESC\\\n"); | ||
| 2147 | } | ||
| 2148 | |||
| 2149 | void | ||
| 2150 | strreset(void) | ||
| 2151 | { | ||
| 2152 | strescseq = (STREscape){ | ||
| 2153 | .buf = xrealloc(strescseq.buf, STR_BUF_SIZ), | ||
| 2154 | .siz = STR_BUF_SIZ, | ||
| 2155 | }; | ||
| 2156 | } | ||
| 2157 | |||
| 2158 | void | ||
| 2159 | sendbreak(const Arg *arg) | ||
| 2160 | { | ||
| 2161 | if (tcsendbreak(cmdfd, 0)) | ||
| 2162 | perror("Error sending break"); | ||
| 2163 | } | ||
| 2164 | |||
| 2165 | void | ||
| 2166 | tprinter(char *s, size_t len) | ||
| 2167 | { | ||
| 2168 | if (iofd != -1 && xwrite(iofd, s, len) < 0) { | ||
| 2169 | perror("Error writing to output file"); | ||
| 2170 | close(iofd); | ||
| 2171 | iofd = -1; | ||
| 2172 | } | ||
| 2173 | } | ||
| 2174 | |||
| 2175 | void | ||
| 2176 | toggleprinter(const Arg *arg) | ||
| 2177 | { | ||
| 2178 | term.mode ^= MODE_PRINT; | ||
| 2179 | } | ||
| 2180 | |||
| 2181 | void | ||
| 2182 | printscreen(const Arg *arg) | ||
| 2183 | { | ||
| 2184 | tdump(); | ||
| 2185 | } | ||
| 2186 | |||
| 2187 | void | ||
| 2188 | printsel(const Arg *arg) | ||
| 2189 | { | ||
| 2190 | tdumpsel(); | ||
| 2191 | } | ||
| 2192 | |||
| 2193 | void | ||
| 2194 | tdumpsel(void) | ||
| 2195 | { | ||
| 2196 | char *ptr; | ||
| 2197 | |||
| 2198 | if ((ptr = getsel())) { | ||
| 2199 | tprinter(ptr, strlen(ptr)); | ||
| 2200 | free(ptr); | ||
| 2201 | } | ||
| 2202 | } | ||
| 2203 | |||
| 2204 | void | ||
| 2205 | tdumpline(int n) | ||
| 2206 | { | ||
| 2207 | char buf[UTF_SIZ]; | ||
| 2208 | const Glyph *bp, *end; | ||
| 2209 | |||
| 2210 | bp = &term.line[n][0]; | ||
| 2211 | end = &bp[MIN(tlinelen(n), term.col) - 1]; | ||
| 2212 | if (bp != end || bp->u != ' ') { | ||
| 2213 | for ( ; bp <= end; ++bp) | ||
| 2214 | tprinter(buf, utf8encode(bp->u, buf)); | ||
| 2215 | } | ||
| 2216 | tprinter("\n", 1); | ||
| 2217 | } | ||
| 2218 | |||
| 2219 | void | ||
| 2220 | tdump(void) | ||
| 2221 | { | ||
| 2222 | int i; | ||
| 2223 | |||
| 2224 | for (i = 0; i < term.row; ++i) | ||
| 2225 | tdumpline(i); | ||
| 2226 | } | ||
| 2227 | |||
| 2228 | void | ||
| 2229 | tputtab(int n) | ||
| 2230 | { | ||
| 2231 | uint x = term.c.x; | ||
| 2232 | |||
| 2233 | if (n > 0) { | ||
| 2234 | while (x < term.col && n--) | ||
| 2235 | for (++x; x < term.col && !term.tabs[x]; ++x) | ||
| 2236 | /* nothing */ ; | ||
| 2237 | } else if (n < 0) { | ||
| 2238 | while (x > 0 && n++) | ||
| 2239 | for (--x; x > 0 && !term.tabs[x]; --x) | ||
| 2240 | /* nothing */ ; | ||
| 2241 | } | ||
| 2242 | term.c.x = LIMIT(x, 0, term.col-1); | ||
| 2243 | } | ||
| 2244 | |||
| 2245 | void | ||
| 2246 | tdefutf8(char ascii) | ||
| 2247 | { | ||
| 2248 | if (ascii == 'G') | ||
| 2249 | term.mode |= MODE_UTF8; | ||
| 2250 | else if (ascii == '@') | ||
| 2251 | term.mode &= ~MODE_UTF8; | ||
| 2252 | } | ||
| 2253 | |||
| 2254 | void | ||
| 2255 | tdeftran(char ascii) | ||
| 2256 | { | ||
| 2257 | static char cs[] = "0B"; | ||
| 2258 | static int vcs[] = {CS_GRAPHIC0, CS_USA}; | ||
| 2259 | char *p; | ||
| 2260 | |||
| 2261 | if ((p = strchr(cs, ascii)) == NULL) { | ||
| 2262 | fprintf(stderr, "esc unhandled charset: ESC ( %c\n", ascii); | ||
| 2263 | } else { | ||
| 2264 | term.trantbl[term.icharset] = vcs[p - cs]; | ||
| 2265 | } | ||
| 2266 | } | ||
| 2267 | |||
| 2268 | void | ||
| 2269 | tdectest(char c) | ||
| 2270 | { | ||
| 2271 | int x, y; | ||
| 2272 | |||
| 2273 | if (c == '8') { /* DEC screen alignment test. */ | ||
| 2274 | for (x = 0; x < term.col; ++x) { | ||
| 2275 | for (y = 0; y < term.row; ++y) | ||
| 2276 | tsetchar('E', &term.c.attr, x, y); | ||
| 2277 | } | ||
| 2278 | } | ||
| 2279 | } | ||
| 2280 | |||
| 2281 | void | ||
| 2282 | tstrsequence(uchar c) | ||
| 2283 | { | ||
| 2284 | switch (c) { | ||
| 2285 | case 0x90: /* DCS -- Device Control String */ | ||
| 2286 | c = 'P'; | ||
| 2287 | break; | ||
| 2288 | case 0x9f: /* APC -- Application Program Command */ | ||
| 2289 | c = '_'; | ||
| 2290 | break; | ||
| 2291 | case 0x9e: /* PM -- Privacy Message */ | ||
| 2292 | c = '^'; | ||
| 2293 | break; | ||
| 2294 | case 0x9d: /* OSC -- Operating System Command */ | ||
| 2295 | c = ']'; | ||
| 2296 | break; | ||
| 2297 | } | ||
| 2298 | strreset(); | ||
| 2299 | strescseq.type = c; | ||
| 2300 | term.esc |= ESC_STR; | ||
| 2301 | } | ||
| 2302 | |||
| 2303 | void | ||
| 2304 | tcontrolcode(uchar ascii) | ||
| 2305 | { | ||
| 2306 | switch (ascii) { | ||
| 2307 | case '\t': /* HT */ | ||
| 2308 | tputtab(1); | ||
| 2309 | return; | ||
| 2310 | case '\b': /* BS */ | ||
| 2311 | tmoveto(term.c.x-1, term.c.y); | ||
| 2312 | return; | ||
| 2313 | case '\r': /* CR */ | ||
| 2314 | tmoveto(0, term.c.y); | ||
| 2315 | return; | ||
| 2316 | case '\f': /* LF */ | ||
| 2317 | case '\v': /* VT */ | ||
| 2318 | case '\n': /* LF */ | ||
| 2319 | /* go to first col if the mode is set */ | ||
| 2320 | tnewline(IS_SET(MODE_CRLF)); | ||
| 2321 | return; | ||
| 2322 | case '\a': /* BEL */ | ||
| 2323 | if (term.esc & ESC_STR_END) { | ||
| 2324 | /* backwards compatibility to xterm */ | ||
| 2325 | strhandle(); | ||
| 2326 | } else { | ||
| 2327 | xbell(); | ||
| 2328 | } | ||
| 2329 | break; | ||
| 2330 | case '\033': /* ESC */ | ||
| 2331 | csireset(); | ||
| 2332 | term.esc &= ~(ESC_CSI|ESC_ALTCHARSET|ESC_TEST); | ||
| 2333 | term.esc |= ESC_START; | ||
| 2334 | return; | ||
| 2335 | case '\016': /* SO (LS1 -- Locking shift 1) */ | ||
| 2336 | case '\017': /* SI (LS0 -- Locking shift 0) */ | ||
| 2337 | term.charset = 1 - (ascii - '\016'); | ||
| 2338 | return; | ||
| 2339 | case '\032': /* SUB */ | ||
| 2340 | tsetchar('?', &term.c.attr, term.c.x, term.c.y); | ||
| 2341 | /* FALLTHROUGH */ | ||
| 2342 | case '\030': /* CAN */ | ||
| 2343 | csireset(); | ||
| 2344 | break; | ||
| 2345 | case '\005': /* ENQ (IGNORED) */ | ||
| 2346 | case '\000': /* NUL (IGNORED) */ | ||
| 2347 | case '\021': /* XON (IGNORED) */ | ||
| 2348 | case '\023': /* XOFF (IGNORED) */ | ||
| 2349 | case 0177: /* DEL (IGNORED) */ | ||
| 2350 | return; | ||
| 2351 | case 0x80: /* TODO: PAD */ | ||
| 2352 | case 0x81: /* TODO: HOP */ | ||
| 2353 | case 0x82: /* TODO: BPH */ | ||
| 2354 | case 0x83: /* TODO: NBH */ | ||
| 2355 | case 0x84: /* TODO: IND */ | ||
| 2356 | break; | ||
| 2357 | case 0x85: /* NEL -- Next line */ | ||
| 2358 | tnewline(1); /* always go to first col */ | ||
| 2359 | break; | ||
| 2360 | case 0x86: /* TODO: SSA */ | ||
| 2361 | case 0x87: /* TODO: ESA */ | ||
| 2362 | break; | ||
| 2363 | case 0x88: /* HTS -- Horizontal tab stop */ | ||
| 2364 | term.tabs[term.c.x] = 1; | ||
| 2365 | break; | ||
| 2366 | case 0x89: /* TODO: HTJ */ | ||
| 2367 | case 0x8a: /* TODO: VTS */ | ||
| 2368 | case 0x8b: /* TODO: PLD */ | ||
| 2369 | case 0x8c: /* TODO: PLU */ | ||
| 2370 | case 0x8d: /* TODO: RI */ | ||
| 2371 | case 0x8e: /* TODO: SS2 */ | ||
| 2372 | case 0x8f: /* TODO: SS3 */ | ||
| 2373 | case 0x91: /* TODO: PU1 */ | ||
| 2374 | case 0x92: /* TODO: PU2 */ | ||
| 2375 | case 0x93: /* TODO: STS */ | ||
| 2376 | case 0x94: /* TODO: CCH */ | ||
| 2377 | case 0x95: /* TODO: MW */ | ||
| 2378 | case 0x96: /* TODO: SPA */ | ||
| 2379 | case 0x97: /* TODO: EPA */ | ||
| 2380 | case 0x98: /* TODO: SOS */ | ||
| 2381 | case 0x99: /* TODO: SGCI */ | ||
| 2382 | break; | ||
| 2383 | case 0x9a: /* DECID -- Identify Terminal */ | ||
| 2384 | ttywrite(vtiden, strlen(vtiden), 0); | ||
| 2385 | break; | ||
| 2386 | case 0x9b: /* TODO: CSI */ | ||
| 2387 | case 0x9c: /* TODO: ST */ | ||
| 2388 | break; | ||
| 2389 | case 0x90: /* DCS -- Device Control String */ | ||
| 2390 | case 0x9d: /* OSC -- Operating System Command */ | ||
| 2391 | case 0x9e: /* PM -- Privacy Message */ | ||
| 2392 | case 0x9f: /* APC -- Application Program Command */ | ||
| 2393 | tstrsequence(ascii); | ||
| 2394 | return; | ||
| 2395 | } | ||
| 2396 | /* only CAN, SUB, \a and C1 chars interrupt a sequence */ | ||
| 2397 | term.esc &= ~(ESC_STR_END|ESC_STR); | ||
| 2398 | } | ||
| 2399 | |||
| 2400 | /* | ||
| 2401 | * returns 1 when the sequence is finished and it hasn't to read | ||
| 2402 | * more characters for this sequence, otherwise 0 | ||
| 2403 | */ | ||
| 2404 | int | ||
| 2405 | eschandle(uchar ascii) | ||
| 2406 | { | ||
| 2407 | switch (ascii) { | ||
| 2408 | case '[': | ||
| 2409 | term.esc |= ESC_CSI; | ||
| 2410 | return 0; | ||
| 2411 | case '#': | ||
| 2412 | term.esc |= ESC_TEST; | ||
| 2413 | return 0; | ||
| 2414 | case '%': | ||
| 2415 | term.esc |= ESC_UTF8; | ||
| 2416 | return 0; | ||
| 2417 | case 'P': /* DCS -- Device Control String */ | ||
| 2418 | case '_': /* APC -- Application Program Command */ | ||
| 2419 | case '^': /* PM -- Privacy Message */ | ||
| 2420 | case ']': /* OSC -- Operating System Command */ | ||
| 2421 | case 'k': /* old title set compatibility */ | ||
| 2422 | tstrsequence(ascii); | ||
| 2423 | return 0; | ||
| 2424 | case 'n': /* LS2 -- Locking shift 2 */ | ||
| 2425 | case 'o': /* LS3 -- Locking shift 3 */ | ||
| 2426 | term.charset = 2 + (ascii - 'n'); | ||
| 2427 | break; | ||
| 2428 | case '(': /* GZD4 -- set primary charset G0 */ | ||
| 2429 | case ')': /* G1D4 -- set secondary charset G1 */ | ||
| 2430 | case '*': /* G2D4 -- set tertiary charset G2 */ | ||
| 2431 | case '+': /* G3D4 -- set quaternary charset G3 */ | ||
| 2432 | term.icharset = ascii - '('; | ||
| 2433 | term.esc |= ESC_ALTCHARSET; | ||
| 2434 | return 0; | ||
| 2435 | case 'D': /* IND -- Linefeed */ | ||
| 2436 | if (term.c.y == term.bot) { | ||
| 2437 | tscrollup(term.top, 1, 1); | ||
| 2438 | } else { | ||
| 2439 | tmoveto(term.c.x, term.c.y+1); | ||
| 2440 | } | ||
| 2441 | break; | ||
| 2442 | case 'E': /* NEL -- Next line */ | ||
| 2443 | tnewline(1); /* always go to first col */ | ||
| 2444 | break; | ||
| 2445 | case 'H': /* HTS -- Horizontal tab stop */ | ||
| 2446 | term.tabs[term.c.x] = 1; | ||
| 2447 | break; | ||
| 2448 | case 'M': /* RI -- Reverse index */ | ||
| 2449 | if (term.c.y == term.top) { | ||
| 2450 | tscrolldown(term.top, 1, 1); | ||
| 2451 | } else { | ||
| 2452 | tmoveto(term.c.x, term.c.y-1); | ||
| 2453 | } | ||
| 2454 | break; | ||
| 2455 | case 'Z': /* DECID -- Identify Terminal */ | ||
| 2456 | ttywrite(vtiden, strlen(vtiden), 0); | ||
| 2457 | break; | ||
| 2458 | case 'c': /* RIS -- Reset to initial state */ | ||
| 2459 | treset(); | ||
| 2460 | resettitle(); | ||
| 2461 | xloadcols(); | ||
| 2462 | break; | ||
| 2463 | case '=': /* DECPAM -- Application keypad */ | ||
| 2464 | xsetmode(1, MODE_APPKEYPAD); | ||
| 2465 | break; | ||
| 2466 | case '>': /* DECPNM -- Normal keypad */ | ||
| 2467 | xsetmode(0, MODE_APPKEYPAD); | ||
| 2468 | break; | ||
| 2469 | case '7': /* DECSC -- Save Cursor */ | ||
| 2470 | tcursor(CURSOR_SAVE); | ||
| 2471 | break; | ||
| 2472 | case '8': /* DECRC -- Restore Cursor */ | ||
| 2473 | tcursor(CURSOR_LOAD); | ||
| 2474 | break; | ||
| 2475 | case '\\': /* ST -- String Terminator */ | ||
| 2476 | if (term.esc & ESC_STR_END) | ||
| 2477 | strhandle(); | ||
| 2478 | break; | ||
| 2479 | default: | ||
| 2480 | fprintf(stderr, "erresc: unknown sequence ESC 0x%02X '%c'\n", | ||
| 2481 | (uchar) ascii, isprint(ascii)? ascii:'.'); | ||
| 2482 | break; | ||
| 2483 | } | ||
| 2484 | return 1; | ||
| 2485 | } | ||
| 2486 | |||
| 2487 | void | ||
| 2488 | tputc(Rune u) | ||
| 2489 | { | ||
| 2490 | char c[UTF_SIZ]; | ||
| 2491 | int control; | ||
| 2492 | int width, len; | ||
| 2493 | Glyph *gp; | ||
| 2494 | |||
| 2495 | control = ISCONTROL(u); | ||
| 2496 | if (u < 127 || !IS_SET(MODE_UTF8)) { | ||
| 2497 | c[0] = u; | ||
| 2498 | width = len = 1; | ||
| 2499 | } else { | ||
| 2500 | len = utf8encode(u, c); | ||
| 2501 | if (!control && (width = wcwidth(u)) == -1) | ||
| 2502 | width = 1; | ||
| 2503 | } | ||
| 2504 | |||
| 2505 | if (IS_SET(MODE_PRINT)) | ||
| 2506 | tprinter(c, len); | ||
| 2507 | |||
| 2508 | /* | ||
| 2509 | * STR sequence must be checked before anything else | ||
| 2510 | * because it uses all following characters until it | ||
| 2511 | * receives a ESC, a SUB, a ST or any other C1 control | ||
| 2512 | * character. | ||
| 2513 | */ | ||
| 2514 | if (term.esc & ESC_STR) { | ||
| 2515 | if (u == '\a' || u == 030 || u == 032 || u == 033 || | ||
| 2516 | ISCONTROLC1(u)) { | ||
| 2517 | term.esc &= ~(ESC_START|ESC_STR); | ||
| 2518 | term.esc |= ESC_STR_END; | ||
| 2519 | goto check_control_code; | ||
| 2520 | } | ||
| 2521 | |||
| 2522 | if (strescseq.len+len >= strescseq.siz) { | ||
| 2523 | /* | ||
| 2524 | * Here is a bug in terminals. If the user never sends | ||
| 2525 | * some code to stop the str or esc command, then st | ||
| 2526 | * will stop responding. But this is better than | ||
| 2527 | * silently failing with unknown characters. At least | ||
| 2528 | * then users will report back. | ||
| 2529 | * | ||
| 2530 | * In the case users ever get fixed, here is the code: | ||
| 2531 | */ | ||
| 2532 | /* | ||
| 2533 | * term.esc = 0; | ||
| 2534 | * strhandle(); | ||
| 2535 | */ | ||
| 2536 | if (strescseq.siz > (SIZE_MAX - UTF_SIZ) / 2) | ||
| 2537 | return; | ||
| 2538 | strescseq.siz *= 2; | ||
| 2539 | strescseq.buf = xrealloc(strescseq.buf, strescseq.siz); | ||
| 2540 | } | ||
| 2541 | |||
| 2542 | memmove(&strescseq.buf[strescseq.len], c, len); | ||
| 2543 | strescseq.len += len; | ||
| 2544 | return; | ||
| 2545 | } | ||
| 2546 | |||
| 2547 | check_control_code: | ||
| 2548 | /* | ||
| 2549 | * Actions of control codes must be performed as soon they arrive | ||
| 2550 | * because they can be embedded inside a control sequence, and | ||
| 2551 | * they must not cause conflicts with sequences. | ||
| 2552 | */ | ||
| 2553 | if (control) { | ||
| 2554 | tcontrolcode(u); | ||
| 2555 | /* | ||
| 2556 | * control codes are not shown ever | ||
| 2557 | */ | ||
| 2558 | if (!term.esc) | ||
| 2559 | term.lastc = 0; | ||
| 2560 | return; | ||
| 2561 | } else if (term.esc & ESC_START) { | ||
| 2562 | if (term.esc & ESC_CSI) { | ||
| 2563 | csiescseq.buf[csiescseq.len++] = u; | ||
| 2564 | if (BETWEEN(u, 0x40, 0x7E) | ||
| 2565 | || csiescseq.len >= \ | ||
| 2566 | sizeof(csiescseq.buf)-1) { | ||
| 2567 | term.esc = 0; | ||
| 2568 | csiparse(); | ||
| 2569 | csihandle(); | ||
| 2570 | } | ||
| 2571 | return; | ||
| 2572 | } else if (term.esc & ESC_UTF8) { | ||
| 2573 | tdefutf8(u); | ||
| 2574 | } else if (term.esc & ESC_ALTCHARSET) { | ||
| 2575 | tdeftran(u); | ||
| 2576 | } else if (term.esc & ESC_TEST) { | ||
| 2577 | tdectest(u); | ||
| 2578 | } else { | ||
| 2579 | if (!eschandle(u)) | ||
| 2580 | return; | ||
| 2581 | /* sequence already finished */ | ||
| 2582 | } | ||
| 2583 | term.esc = 0; | ||
| 2584 | /* | ||
| 2585 | * All characters which form part of a sequence are not | ||
| 2586 | * printed | ||
| 2587 | */ | ||
| 2588 | return; | ||
| 2589 | } | ||
| 2590 | if (selected(term.c.x, term.c.y)) | ||
| 2591 | selclear(); | ||
| 2592 | |||
| 2593 | gp = &term.line[term.c.y][term.c.x]; | ||
| 2594 | if (IS_SET(MODE_WRAP) && (term.c.state & CURSOR_WRAPNEXT)) { | ||
| 2595 | gp->mode |= ATTR_WRAP; | ||
| 2596 | tnewline(1); | ||
| 2597 | gp = &term.line[term.c.y][term.c.x]; | ||
| 2598 | } | ||
| 2599 | |||
| 2600 | if (IS_SET(MODE_INSERT) && term.c.x+width < term.col) | ||
| 2601 | memmove(gp+width, gp, (term.col - term.c.x - width) * sizeof(Glyph)); | ||
| 2602 | |||
| 2603 | if (term.c.x+width > term.col) { | ||
| 2604 | tnewline(1); | ||
| 2605 | gp = &term.line[term.c.y][term.c.x]; | ||
| 2606 | } | ||
| 2607 | |||
| 2608 | tsetchar(u, &term.c.attr, term.c.x, term.c.y); | ||
| 2609 | term.lastc = u; | ||
| 2610 | |||
| 2611 | if (width == 2) { | ||
| 2612 | gp->mode |= ATTR_WIDE; | ||
| 2613 | if (term.c.x+1 < term.col) { | ||
| 2614 | if (gp[1].mode == ATTR_WIDE && term.c.x+2 < term.col) { | ||
| 2615 | gp[2].u = ' '; | ||
| 2616 | gp[2].mode &= ~ATTR_WDUMMY; | ||
| 2617 | } | ||
| 2618 | gp[1].u = '\0'; | ||
| 2619 | gp[1].mode = ATTR_WDUMMY; | ||
| 2620 | } | ||
| 2621 | } | ||
| 2622 | if (term.c.x+width < term.col) { | ||
| 2623 | tmoveto(term.c.x+width, term.c.y); | ||
| 2624 | } else { | ||
| 2625 | term.c.state |= CURSOR_WRAPNEXT; | ||
| 2626 | } | ||
| 2627 | } | ||
| 2628 | |||
| 2629 | int | ||
| 2630 | twrite(const char *buf, int buflen, int show_ctrl) | ||
| 2631 | { | ||
| 2632 | int charsize; | ||
| 2633 | Rune u; | ||
| 2634 | int n; | ||
| 2635 | |||
| 2636 | for (n = 0; n < buflen; n += charsize) { | ||
| 2637 | if (IS_SET(MODE_UTF8)) { | ||
| 2638 | /* process a complete utf8 char */ | ||
| 2639 | charsize = utf8decode(buf + n, &u, buflen - n); | ||
| 2640 | if (charsize == 0) | ||
| 2641 | break; | ||
| 2642 | } else { | ||
| 2643 | u = buf[n] & 0xFF; | ||
| 2644 | charsize = 1; | ||
| 2645 | } | ||
| 2646 | if (show_ctrl && ISCONTROL(u)) { | ||
| 2647 | if (u & 0x80) { | ||
| 2648 | u &= 0x7f; | ||
| 2649 | tputc('^'); | ||
| 2650 | tputc('['); | ||
| 2651 | } else if (u != '\n' && u != '\r' && u != '\t') { | ||
| 2652 | u ^= 0x40; | ||
| 2653 | tputc('^'); | ||
| 2654 | } | ||
| 2655 | } | ||
| 2656 | tputc(u); | ||
| 2657 | } | ||
| 2658 | return n; | ||
| 2659 | } | ||
| 2660 | |||
| 2661 | void | ||
| 2662 | tresize(int col, int row) | ||
| 2663 | { | ||
| 2664 | int i, j; | ||
| 2665 | int tmp; | ||
| 2666 | int minrow, mincol; | ||
| 2667 | int *bp; | ||
| 2668 | TCursor c; | ||
| 2669 | |||
| 2670 | tmp = col; | ||
| 2671 | if (!term.maxcol) | ||
| 2672 | term.maxcol = term.col; | ||
| 2673 | col = MAX(col, term.maxcol); | ||
| 2674 | minrow = MIN(row, term.row); | ||
| 2675 | mincol = MIN(col, term.maxcol); | ||
| 2676 | |||
| 2677 | if (col < 1 || row < 1) { | ||
| 2678 | fprintf(stderr, | ||
| 2679 | "tresize: error resizing to %dx%d\n", col, row); | ||
| 2680 | return; | ||
| 2681 | } | ||
| 2682 | |||
| 2683 | /* | ||
| 2684 | * slide screen to keep cursor where we expect it - | ||
| 2685 | * tscrollup would work here, but we can optimize to | ||
| 2686 | * memmove because we're freeing the earlier lines | ||
| 2687 | */ | ||
| 2688 | for (i = 0; i <= term.c.y - row; i++) { | ||
| 2689 | free(term.line[i]); | ||
| 2690 | free(term.alt[i]); | ||
| 2691 | } | ||
| 2692 | /* ensure that both src and dst are not NULL */ | ||
| 2693 | if (i > 0) { | ||
| 2694 | memmove(term.line, term.line + i, row * sizeof(Line)); | ||
| 2695 | memmove(term.alt, term.alt + i, row * sizeof(Line)); | ||
| 2696 | } | ||
| 2697 | for (i += row; i < term.row; i++) { | ||
| 2698 | free(term.line[i]); | ||
| 2699 | free(term.alt[i]); | ||
| 2700 | } | ||
| 2701 | |||
| 2702 | /* resize to new height */ | ||
| 2703 | term.line = xrealloc(term.line, row * sizeof(Line)); | ||
| 2704 | term.alt = xrealloc(term.alt, row * sizeof(Line)); | ||
| 2705 | term.dirty = xrealloc(term.dirty, row * sizeof(*term.dirty)); | ||
| 2706 | term.tabs = xrealloc(term.tabs, col * sizeof(*term.tabs)); | ||
| 2707 | |||
| 2708 | for (i = 0; i < HISTSIZE; i++) { | ||
| 2709 | term.hist[i] = xrealloc(term.hist[i], col * sizeof(Glyph)); | ||
| 2710 | for (j = mincol; j < col; j++) { | ||
| 2711 | term.hist[i][j] = term.c.attr; | ||
| 2712 | term.hist[i][j].u = ' '; | ||
| 2713 | } | ||
| 2714 | } | ||
| 2715 | |||
| 2716 | /* resize each row to new width, zero-pad if needed */ | ||
| 2717 | for (i = 0; i < minrow; i++) { | ||
| 2718 | term.line[i] = xrealloc(term.line[i], col * sizeof(Glyph)); | ||
| 2719 | term.alt[i] = xrealloc(term.alt[i], col * sizeof(Glyph)); | ||
| 2720 | } | ||
| 2721 | |||
| 2722 | /* allocate any new rows */ | ||
| 2723 | for (/* i = minrow */; i < row; i++) { | ||
| 2724 | term.line[i] = xmalloc(col * sizeof(Glyph)); | ||
| 2725 | term.alt[i] = xmalloc(col * sizeof(Glyph)); | ||
| 2726 | } | ||
| 2727 | if (col > term.maxcol) { | ||
| 2728 | bp = term.tabs + term.maxcol; | ||
| 2729 | |||
| 2730 | memset(bp, 0, sizeof(*term.tabs) * (col - term.maxcol)); | ||
| 2731 | while (--bp > term.tabs && !*bp) | ||
| 2732 | /* nothing */ ; | ||
| 2733 | for (bp += tabspaces; bp < term.tabs + col; bp += tabspaces) | ||
| 2734 | *bp = 1; | ||
| 2735 | } | ||
| 2736 | /* update terminal size */ | ||
| 2737 | term.col = tmp; | ||
| 2738 | term.maxcol = col; | ||
| 2739 | term.row = row; | ||
| 2740 | /* reset scrolling region */ | ||
| 2741 | tsetscroll(0, row-1); | ||
| 2742 | /* make use of the LIMIT in tmoveto */ | ||
| 2743 | tmoveto(term.c.x, term.c.y); | ||
| 2744 | /* Clearing both screens (it makes dirty all lines) */ | ||
| 2745 | c = term.c; | ||
| 2746 | for (i = 0; i < 2; i++) { | ||
| 2747 | if (mincol < col && 0 < minrow) { | ||
| 2748 | tclearregion(mincol, 0, col - 1, minrow - 1); | ||
| 2749 | } | ||
| 2750 | if (0 < col && minrow < row) { | ||
| 2751 | tclearregion(0, minrow, col - 1, row - 1); | ||
| 2752 | } | ||
| 2753 | tswapscreen(); | ||
| 2754 | tcursor(CURSOR_LOAD); | ||
| 2755 | } | ||
| 2756 | term.c = c; | ||
| 2757 | } | ||
| 2758 | |||
| 2759 | void | ||
| 2760 | resettitle(void) | ||
| 2761 | { | ||
| 2762 | xsettitle(NULL); | ||
| 2763 | } | ||
| 2764 | |||
| 2765 | void | ||
| 2766 | drawregion(int x1, int y1, int x2, int y2) | ||
| 2767 | { | ||
| 2768 | int y; | ||
| 2769 | |||
| 2770 | for (y = y1; y < y2; y++) { | ||
| 2771 | if (!term.dirty[y]) | ||
| 2772 | continue; | ||
| 2773 | |||
| 2774 | term.dirty[y] = 0; | ||
| 2775 | xdrawline(TLINE(y), x1, y, x2); | ||
| 2776 | } | ||
| 2777 | } | ||
| 2778 | |||
| 2779 | void | ||
| 2780 | draw(void) | ||
| 2781 | { | ||
| 2782 | int cx = term.c.x, ocx = term.ocx, ocy = term.ocy; | ||
| 2783 | |||
| 2784 | if (!xstartdraw()) | ||
| 2785 | return; | ||
| 2786 | |||
| 2787 | /* adjust cursor position */ | ||
| 2788 | LIMIT(term.ocx, 0, term.col-1); | ||
| 2789 | LIMIT(term.ocy, 0, term.row-1); | ||
| 2790 | if (term.line[term.ocy][term.ocx].mode & ATTR_WDUMMY) | ||
| 2791 | term.ocx--; | ||
| 2792 | if (term.line[term.c.y][cx].mode & ATTR_WDUMMY) | ||
| 2793 | cx--; | ||
| 2794 | |||
| 2795 | drawregion(0, 0, term.col, term.row); | ||
| 2796 | if (term.scr == 0) | ||
| 2797 | xdrawcursor(cx, term.c.y, term.line[term.c.y][cx], | ||
| 2798 | term.ocx, term.ocy, term.line[term.ocy][term.ocx], | ||
| 2799 | term.line[term.ocy], term.col); | ||
| 2800 | /* xdrawcursor(cx, term.c.y, term.line[term.c.y][cx], */ | ||
| 2801 | /* term.ocx, term.ocy, term.line[term.ocy][term.ocx], */ | ||
| 2802 | /* term.line[term.ocy], term.col); */ | ||
| 2803 | term.ocx = cx; | ||
| 2804 | term.ocy = term.c.y; | ||
| 2805 | xfinishdraw(); | ||
| 2806 | if (ocx != term.ocx || ocy != term.ocy) | ||
| 2807 | xximspot(term.ocx, term.ocy); | ||
| 2808 | } | ||
| 2809 | |||
| 2810 | void | ||
| 2811 | redraw(void) | ||
| 2812 | { | ||
| 2813 | tfulldirt(); | ||
| 2814 | draw(); | ||
| 2815 | } | ||
| 2816 | |||
| @@ -0,0 +1,146 @@ | |||
| 1 | /* See LICENSE for license details. */ | ||
| 2 | |||
| 3 | #include <stdint.h> | ||
| 4 | #include <sys/types.h> | ||
| 5 | |||
| 6 | /* macros */ | ||
| 7 | #define MIN(a, b) ((a) < (b) ? (a) : (b)) | ||
| 8 | #define MAX(a, b) ((a) < (b) ? (b) : (a)) | ||
| 9 | #define LEN(a) (sizeof(a) / sizeof(a)[0]) | ||
| 10 | #define BETWEEN(x, a, b) ((a) <= (x) && (x) <= (b)) | ||
| 11 | #define DIVCEIL(n, d) (((n) + ((d) - 1)) / (d)) | ||
| 12 | #define DEFAULT(a, b) (a) = (a) ? (a) : (b) | ||
| 13 | #define LIMIT(x, a, b) (x) = (x) < (a) ? (a) : (x) > (b) ? (b) : (x) | ||
| 14 | #define ATTRCMP(a, b) (((a).mode & (~ATTR_WRAP) & (~ATTR_LIGA)) != ((b).mode & (~ATTR_WRAP) & (~ATTR_LIGA)) || \ | ||
| 15 | (a).fg != (b).fg || \ | ||
| 16 | (a).bg != (b).bg) | ||
| 17 | #define TIMEDIFF(t1, t2) ((t1.tv_sec-t2.tv_sec)*1000 + \ | ||
| 18 | (t1.tv_nsec-t2.tv_nsec)/1E6) | ||
| 19 | #define MODBIT(x, set, bit) ((set) ? ((x) |= (bit)) : ((x) &= ~(bit))) | ||
| 20 | |||
| 21 | #define TRUECOLOR(r,g,b) (1 << 24 | (r) << 16 | (g) << 8 | (b)) | ||
| 22 | #define IS_TRUECOL(x) (1 << 24 & (x)) | ||
| 23 | |||
| 24 | enum glyph_attribute { | ||
| 25 | ATTR_NULL = 0, | ||
| 26 | ATTR_BOLD = 1 << 0, | ||
| 27 | ATTR_FAINT = 1 << 1, | ||
| 28 | ATTR_ITALIC = 1 << 2, | ||
| 29 | ATTR_UNDERLINE = 1 << 3, | ||
| 30 | ATTR_BLINK = 1 << 4, | ||
| 31 | ATTR_REVERSE = 1 << 5, | ||
| 32 | ATTR_INVISIBLE = 1 << 6, | ||
| 33 | ATTR_STRUCK = 1 << 7, | ||
| 34 | ATTR_WRAP = 1 << 8, | ||
| 35 | ATTR_WIDE = 1 << 9, | ||
| 36 | ATTR_WDUMMY = 1 << 10, | ||
| 37 | ATTR_BOXDRAW = 1 << 11, | ||
| 38 | ATTR_LIGA = 1 << 12, | ||
| 39 | ATTR_BOLD_FAINT = ATTR_BOLD | ATTR_FAINT, | ||
| 40 | }; | ||
| 41 | |||
| 42 | enum selection_mode { | ||
| 43 | SEL_IDLE = 0, | ||
| 44 | SEL_EMPTY = 1, | ||
| 45 | SEL_READY = 2 | ||
| 46 | }; | ||
| 47 | |||
| 48 | enum selection_type { | ||
| 49 | SEL_REGULAR = 1, | ||
| 50 | SEL_RECTANGULAR = 2 | ||
| 51 | }; | ||
| 52 | |||
| 53 | enum selection_snap { | ||
| 54 | SNAP_WORD = 1, | ||
| 55 | SNAP_LINE = 2 | ||
| 56 | }; | ||
| 57 | |||
| 58 | typedef unsigned char uchar; | ||
| 59 | typedef unsigned int uint; | ||
| 60 | typedef unsigned long ulong; | ||
| 61 | typedef unsigned short ushort; | ||
| 62 | |||
| 63 | typedef uint_least32_t Rune; | ||
| 64 | |||
| 65 | #define Glyph Glyph_ | ||
| 66 | typedef struct { | ||
| 67 | Rune u; /* character code */ | ||
| 68 | ushort mode; /* attribute flags */ | ||
| 69 | uint32_t fg; /* foreground */ | ||
| 70 | uint32_t bg; /* background */ | ||
| 71 | } Glyph; | ||
| 72 | |||
| 73 | typedef Glyph *Line; | ||
| 74 | |||
| 75 | typedef union { | ||
| 76 | int i; | ||
| 77 | uint ui; | ||
| 78 | float f; | ||
| 79 | const void *v; | ||
| 80 | const char *s; | ||
| 81 | } Arg; | ||
| 82 | |||
| 83 | void die(const char *, ...); | ||
| 84 | void redraw(void); | ||
| 85 | void tfulldirt(void); | ||
| 86 | void draw(void); | ||
| 87 | |||
| 88 | void externalpipe(const Arg *); | ||
| 89 | void kscrolldown(const Arg *); | ||
| 90 | void kscrollup(const Arg *); | ||
| 91 | |||
| 92 | void printscreen(const Arg *); | ||
| 93 | void printsel(const Arg *); | ||
| 94 | void sendbreak(const Arg *); | ||
| 95 | void toggleprinter(const Arg *); | ||
| 96 | |||
| 97 | int tattrset(int); | ||
| 98 | void tnew(int, int); | ||
| 99 | void tresize(int, int); | ||
| 100 | void tsetdirtattr(int); | ||
| 101 | void ttyhangup(void); | ||
| 102 | int ttynew(const char *, char *, const char *, char **); | ||
| 103 | size_t ttyread(void); | ||
| 104 | void ttyresize(int, int); | ||
| 105 | void ttywrite(const char *, size_t, int); | ||
| 106 | |||
| 107 | void resettitle(void); | ||
| 108 | |||
| 109 | void selclear(void); | ||
| 110 | void selinit(void); | ||
| 111 | void selstart(int, int, int); | ||
| 112 | void selextend(int, int, int, int); | ||
| 113 | int selected(int, int); | ||
| 114 | char *getsel(void); | ||
| 115 | |||
| 116 | size_t utf8encode(Rune, char *); | ||
| 117 | |||
| 118 | void *xmalloc(size_t); | ||
| 119 | void *xrealloc(void *, size_t); | ||
| 120 | char *xstrdup(const char *); | ||
| 121 | |||
| 122 | int isboxdraw(Rune); | ||
| 123 | ushort boxdrawindex(const Glyph *); | ||
| 124 | #ifdef XFT_VERSION | ||
| 125 | /* only exposed to x.c, otherwise we'll need Xft.h for the types */ | ||
| 126 | void boxdraw_xinit(Display *, Colormap, XftDraw *, Visual *); | ||
| 127 | void drawboxes(int, int, int, int, XftColor *, XftColor *, const XftGlyphFontSpec *, int); | ||
| 128 | #endif | ||
| 129 | |||
| 130 | /* config.h globals */ | ||
| 131 | extern char *utmp; | ||
| 132 | extern char *scroll; | ||
| 133 | extern char *stty_args; | ||
| 134 | extern char *vtiden; | ||
| 135 | extern wchar_t *worddelimiters; | ||
| 136 | extern int allowaltscreen; | ||
| 137 | extern int allowwindowops; | ||
| 138 | extern char *termname; | ||
| 139 | extern unsigned int tabspaces; | ||
| 140 | extern unsigned int defaultfg; | ||
| 141 | extern unsigned int defaultbg; | ||
| 142 | extern float alpha; | ||
| 143 | extern float alphaUnfocus; | ||
| 144 | extern const int boxdraw, boxdraw_bold, boxdraw_braille; | ||
| 145 | extern unsigned int defaultcs; | ||
| 146 | |||
| @@ -0,0 +1,239 @@ | |||
| 1 | st-mono| simpleterm monocolor, | ||
| 2 | acsc=+C\,D-A.B0E``aaffgghFiGjjkkllmmnnooppqqrrssttuuvvwwxxyyzz{{||}}~~, | ||
| 3 | am, | ||
| 4 | bce, | ||
| 5 | bel=^G, | ||
| 6 | blink=\E[5m, | ||
| 7 | bold=\E[1m, | ||
| 8 | cbt=\E[Z, | ||
| 9 | cvvis=\E[?25h, | ||
| 10 | civis=\E[?25l, | ||
| 11 | clear=\E[H\E[2J, | ||
| 12 | cnorm=\E[?12l\E[?25h, | ||
| 13 | colors#2, | ||
| 14 | cols#80, | ||
| 15 | cr=^M, | ||
| 16 | csr=\E[%i%p1%d;%p2%dr, | ||
| 17 | cub=\E[%p1%dD, | ||
| 18 | cub1=^H, | ||
| 19 | cud1=^J, | ||
| 20 | cud=\E[%p1%dB, | ||
| 21 | cuf1=\E[C, | ||
| 22 | cuf=\E[%p1%dC, | ||
| 23 | cup=\E[%i%p1%d;%p2%dH, | ||
| 24 | cuu1=\E[A, | ||
| 25 | cuu=\E[%p1%dA, | ||
| 26 | dch=\E[%p1%dP, | ||
| 27 | dch1=\E[P, | ||
| 28 | dim=\E[2m, | ||
| 29 | dl=\E[%p1%dM, | ||
| 30 | dl1=\E[M, | ||
| 31 | ech=\E[%p1%dX, | ||
| 32 | ed=\E[J, | ||
| 33 | el=\E[K, | ||
| 34 | el1=\E[1K, | ||
| 35 | enacs=\E)0, | ||
| 36 | flash=\E[?5h$<80/>\E[?5l, | ||
| 37 | fsl=^G, | ||
| 38 | home=\E[H, | ||
| 39 | hpa=\E[%i%p1%dG, | ||
| 40 | hs, | ||
| 41 | ht=^I, | ||
| 42 | hts=\EH, | ||
| 43 | ich=\E[%p1%d@, | ||
| 44 | il1=\E[L, | ||
| 45 | il=\E[%p1%dL, | ||
| 46 | ind=^J, | ||
| 47 | indn=\E[%p1%dS, | ||
| 48 | invis=\E[8m, | ||
| 49 | is2=\E[4l\E>\E[?1034l, | ||
| 50 | it#8, | ||
| 51 | kel=\E[1;2F, | ||
| 52 | ked=\E[1;5F, | ||
| 53 | ka1=\E[1~, | ||
| 54 | ka3=\E[5~, | ||
| 55 | kc1=\E[4~, | ||
| 56 | kc3=\E[6~, | ||
| 57 | kbs=\177, | ||
| 58 | kcbt=\E[Z, | ||
| 59 | kb2=\EOu, | ||
| 60 | kcub1=\EOD, | ||
| 61 | kcud1=\EOB, | ||
| 62 | kcuf1=\EOC, | ||
| 63 | kcuu1=\EOA, | ||
| 64 | kDC=\E[3;2~, | ||
| 65 | kent=\EOM, | ||
| 66 | kEND=\E[1;2F, | ||
| 67 | kIC=\E[2;2~, | ||
| 68 | kNXT=\E[6;2~, | ||
| 69 | kPRV=\E[5;2~, | ||
| 70 | kHOM=\E[1;2H, | ||
| 71 | kLFT=\E[1;2D, | ||
| 72 | kRIT=\E[1;2C, | ||
| 73 | kind=\E[1;2B, | ||
| 74 | kri=\E[1;2A, | ||
| 75 | kclr=\E[3;5~, | ||
| 76 | kdl1=\E[3;2~, | ||
| 77 | kdch1=\E[3~, | ||
| 78 | kich1=\E[2~, | ||
| 79 | kend=\E[4~, | ||
| 80 | kf1=\EOP, | ||
| 81 | kf2=\EOQ, | ||
| 82 | kf3=\EOR, | ||
| 83 | kf4=\EOS, | ||
| 84 | kf5=\E[15~, | ||
| 85 | kf6=\E[17~, | ||
| 86 | kf7=\E[18~, | ||
| 87 | kf8=\E[19~, | ||
| 88 | kf9=\E[20~, | ||
| 89 | kf10=\E[21~, | ||
| 90 | kf11=\E[23~, | ||
| 91 | kf12=\E[24~, | ||
| 92 | kf13=\E[1;2P, | ||
| 93 | kf14=\E[1;2Q, | ||
| 94 | kf15=\E[1;2R, | ||
| 95 | kf16=\E[1;2S, | ||
| 96 | kf17=\E[15;2~, | ||
| 97 | kf18=\E[17;2~, | ||
| 98 | kf19=\E[18;2~, | ||
| 99 | kf20=\E[19;2~, | ||
| 100 | kf21=\E[20;2~, | ||
| 101 | kf22=\E[21;2~, | ||
| 102 | kf23=\E[23;2~, | ||
| 103 | kf24=\E[24;2~, | ||
| 104 | kf25=\E[1;5P, | ||
| 105 | kf26=\E[1;5Q, | ||
| 106 | kf27=\E[1;5R, | ||
| 107 | kf28=\E[1;5S, | ||
| 108 | kf29=\E[15;5~, | ||
| 109 | kf30=\E[17;5~, | ||
| 110 | kf31=\E[18;5~, | ||
| 111 | kf32=\E[19;5~, | ||
| 112 | kf33=\E[20;5~, | ||
| 113 | kf34=\E[21;5~, | ||
| 114 | kf35=\E[23;5~, | ||
| 115 | kf36=\E[24;5~, | ||
| 116 | kf37=\E[1;6P, | ||
| 117 | kf38=\E[1;6Q, | ||
| 118 | kf39=\E[1;6R, | ||
| 119 | kf40=\E[1;6S, | ||
| 120 | kf41=\E[15;6~, | ||
| 121 | kf42=\E[17;6~, | ||
| 122 | kf43=\E[18;6~, | ||
| 123 | kf44=\E[19;6~, | ||
| 124 | kf45=\E[20;6~, | ||
| 125 | kf46=\E[21;6~, | ||
| 126 | kf47=\E[23;6~, | ||
| 127 | kf48=\E[24;6~, | ||
| 128 | kf49=\E[1;3P, | ||
| 129 | kf50=\E[1;3Q, | ||
| 130 | kf51=\E[1;3R, | ||
| 131 | kf52=\E[1;3S, | ||
| 132 | kf53=\E[15;3~, | ||
| 133 | kf54=\E[17;3~, | ||
| 134 | kf55=\E[18;3~, | ||
| 135 | kf56=\E[19;3~, | ||
| 136 | kf57=\E[20;3~, | ||
| 137 | kf58=\E[21;3~, | ||
| 138 | kf59=\E[23;3~, | ||
| 139 | kf60=\E[24;3~, | ||
| 140 | kf61=\E[1;4P, | ||
| 141 | kf62=\E[1;4Q, | ||
| 142 | kf63=\E[1;4R, | ||
| 143 | khome=\E[1~, | ||
| 144 | kil1=\E[2;5~, | ||
| 145 | krmir=\E[2;2~, | ||
| 146 | knp=\E[6~, | ||
| 147 | kmous=\E[M, | ||
| 148 | kpp=\E[5~, | ||
| 149 | lines#24, | ||
| 150 | mir, | ||
| 151 | msgr, | ||
| 152 | npc, | ||
| 153 | op=\E[39;49m, | ||
| 154 | pairs#64, | ||
| 155 | mc0=\E[i, | ||
| 156 | mc4=\E[4i, | ||
| 157 | mc5=\E[5i, | ||
| 158 | rc=\E8, | ||
| 159 | rev=\E[7m, | ||
| 160 | ri=\EM, | ||
| 161 | rin=\E[%p1%dT, | ||
| 162 | ritm=\E[23m, | ||
| 163 | rmacs=\E(B, | ||
| 164 | rmcup=\E[?1049l, | ||
| 165 | rmir=\E[4l, | ||
| 166 | rmkx=\E[?1l\E>, | ||
| 167 | rmso=\E[27m, | ||
| 168 | rmul=\E[24m, | ||
| 169 | rs1=\Ec, | ||
| 170 | rs2=\E[4l\E>\E[?1034l, | ||
| 171 | sc=\E7, | ||
| 172 | sitm=\E[3m, | ||
| 173 | sgr0=\E[0m, | ||
| 174 | smacs=\E(0, | ||
| 175 | smcup=\E[?1049h, | ||
| 176 | smir=\E[4h, | ||
| 177 | smkx=\E[?1h\E=, | ||
| 178 | smso=\E[7m, | ||
| 179 | smul=\E[4m, | ||
| 180 | tbc=\E[3g, | ||
| 181 | tsl=\E]0;, | ||
| 182 | xenl, | ||
| 183 | vpa=\E[%i%p1%dd, | ||
| 184 | # XTerm extensions | ||
| 185 | rmxx=\E[29m, | ||
| 186 | smxx=\E[9m, | ||
| 187 | # disabled rep for now: causes some issues with older ncurses versions. | ||
| 188 | # rep=%p1%c\E[%p2%{1}%-%db, | ||
| 189 | # tmux extensions, see TERMINFO EXTENSIONS in tmux(1) | ||
| 190 | Tc, | ||
| 191 | Ms=\E]52;%p1%s;%p2%s\007, | ||
| 192 | Se=\E[2 q, | ||
| 193 | Ss=\E[%p1%d q, | ||
| 194 | |||
| 195 | st| simpleterm, | ||
| 196 | use=st-mono, | ||
| 197 | colors#8, | ||
| 198 | setab=\E[4%p1%dm, | ||
| 199 | setaf=\E[3%p1%dm, | ||
| 200 | setb=\E[4%?%p1%{1}%=%t4%e%p1%{3}%=%t6%e%p1%{4}%=%t1%e%p1%{6}%=%t3%e%p1%d%;m, | ||
| 201 | setf=\E[3%?%p1%{1}%=%t4%e%p1%{3}%=%t6%e%p1%{4}%=%t1%e%p1%{6}%=%t3%e%p1%d%;m, | ||
| 202 | sgr=%?%p9%t\E(0%e\E(B%;\E[0%?%p6%t;1%;%?%p2%t;4%;%?%p1%p3%|%t;7%;%?%p4%t;5%;%?%p7%t;8%;m, | ||
| 203 | |||
| 204 | st-256color| simpleterm with 256 colors, | ||
| 205 | use=st, | ||
| 206 | ccc, | ||
| 207 | colors#256, | ||
| 208 | oc=\E]104\007, | ||
| 209 | pairs#32767, | ||
| 210 | # Nicked from xterm-256color | ||
| 211 | initc=\E]4;%p1%d;rgb\:%p2%{255}%*%{1000}%/%2.2X/%p3%{255}%*%{1000}%/%2.2X/%p4%{255}%*%{1000}%/%2.2X\E\\, | ||
| 212 | setab=\E[%?%p1%{8}%<%t4%p1%d%e%p1%{16}%<%t10%p1%{8}%-%d%e48;5;%p1%d%;m, | ||
| 213 | setaf=\E[%?%p1%{8}%<%t3%p1%d%e%p1%{16}%<%t9%p1%{8}%-%d%e38;5;%p1%d%;m, | ||
| 214 | |||
| 215 | st-meta| simpleterm with meta key, | ||
| 216 | use=st, | ||
| 217 | km, | ||
| 218 | rmm=\E[?1034l, | ||
| 219 | smm=\E[?1034h, | ||
| 220 | rs2=\E[4l\E>\E[?1034h, | ||
| 221 | is2=\E[4l\E>\E[?1034h, | ||
| 222 | |||
| 223 | st-meta-256color| simpleterm with meta key and 256 colors, | ||
| 224 | use=st-256color, | ||
| 225 | km, | ||
| 226 | rmm=\E[?1034l, | ||
| 227 | smm=\E[?1034h, | ||
| 228 | rs2=\E[4l\E>\E[?1034h, | ||
| 229 | is2=\E[4l\E>\E[?1034h, | ||
| 230 | |||
| 231 | st-bs| simpleterm with backspace as backspace, | ||
| 232 | use=st, | ||
| 233 | kbs=\010, | ||
| 234 | kdch1=\177, | ||
| 235 | |||
| 236 | st-bs-256color| simpleterm with backspace as backspace and 256colors, | ||
| 237 | use=st-256color, | ||
| 238 | kbs=\010, | ||
| 239 | kdch1=\177, | ||
| @@ -0,0 +1,42 @@ | |||
| 1 | /* See LICENSE for license details. */ | ||
| 2 | |||
| 3 | enum win_mode { | ||
| 4 | MODE_VISIBLE = 1 << 0, | ||
| 5 | MODE_FOCUSED = 1 << 1, | ||
| 6 | MODE_APPKEYPAD = 1 << 2, | ||
| 7 | MODE_MOUSEBTN = 1 << 3, | ||
| 8 | MODE_MOUSEMOTION = 1 << 4, | ||
| 9 | MODE_REVERSE = 1 << 5, | ||
| 10 | MODE_KBDLOCK = 1 << 6, | ||
| 11 | MODE_HIDE = 1 << 7, | ||
| 12 | MODE_APPCURSOR = 1 << 8, | ||
| 13 | MODE_MOUSESGR = 1 << 9, | ||
| 14 | MODE_8BIT = 1 << 10, | ||
| 15 | MODE_BLINK = 1 << 11, | ||
| 16 | MODE_FBLINK = 1 << 12, | ||
| 17 | MODE_FOCUS = 1 << 13, | ||
| 18 | MODE_MOUSEX10 = 1 << 14, | ||
| 19 | MODE_MOUSEMANY = 1 << 15, | ||
| 20 | MODE_BRCKTPASTE = 1 << 16, | ||
| 21 | MODE_NUMLOCK = 1 << 17, | ||
| 22 | MODE_MOUSE = MODE_MOUSEBTN|MODE_MOUSEMOTION|MODE_MOUSEX10\ | ||
| 23 | |MODE_MOUSEMANY, | ||
| 24 | }; | ||
| 25 | |||
| 26 | void xbell(void); | ||
| 27 | void xclipcopy(void); | ||
| 28 | void xdrawcursor(int, int, Glyph, int, int, Glyph, Line, int); | ||
| 29 | void xdrawline(Line, int, int, int); | ||
| 30 | void xfinishdraw(void); | ||
| 31 | void xloadcols(void); | ||
| 32 | int xsetcolorname(int, const char *); | ||
| 33 | int xgetcolor(int, unsigned char *, unsigned char *, unsigned char *); | ||
| 34 | void xseticontitle(char *); | ||
| 35 | void xsettitle(char *); | ||
| 36 | int xsetcursor(int); | ||
| 37 | void xsetmode(int, unsigned int); | ||
| 38 | void xsetpointermotion(int); | ||
| 39 | void xsetsel(char *); | ||
| 40 | int xstartdraw(void); | ||
| 41 | void xximspot(int, int); | ||
| 42 | |||
| @@ -0,0 +1,2366 @@ | |||
| 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 | #include <X11/Xresource.h> | ||
| 18 | |||
| 19 | char *argv0; | ||
| 20 | #include "arg.h" | ||
| 21 | #include "st.h" | ||
| 22 | #include "win.h" | ||
| 23 | #include "hb.h" | ||
| 24 | |||
| 25 | /* types used in config.h */ | ||
| 26 | typedef struct { | ||
| 27 | uint mod; | ||
| 28 | KeySym keysym; | ||
| 29 | void (*func)(const Arg *); | ||
| 30 | const Arg arg; | ||
| 31 | } Shortcut; | ||
| 32 | |||
| 33 | typedef struct { | ||
| 34 | uint mod; | ||
| 35 | uint button; | ||
| 36 | void (*func)(const Arg *); | ||
| 37 | const Arg arg; | ||
| 38 | uint release; | ||
| 39 | } MouseShortcut; | ||
| 40 | |||
| 41 | typedef struct { | ||
| 42 | KeySym k; | ||
| 43 | uint mask; | ||
| 44 | char *s; | ||
| 45 | /* three-valued logic variables: 0 indifferent, 1 on, -1 off */ | ||
| 46 | signed char appkey; /* application keypad */ | ||
| 47 | signed char appcursor; /* application cursor */ | ||
| 48 | } Key; | ||
| 49 | |||
| 50 | /* Xresources preferences */ | ||
| 51 | enum resource_type { | ||
| 52 | STRING = 0, | ||
| 53 | INTEGER = 1, | ||
| 54 | FLOAT = 2 | ||
| 55 | }; | ||
| 56 | |||
| 57 | typedef struct { | ||
| 58 | char *name; | ||
| 59 | enum resource_type type; | ||
| 60 | void *dst; | ||
| 61 | } ResourcePref; | ||
| 62 | |||
| 63 | /* X modifiers */ | ||
| 64 | #define XK_ANY_MOD UINT_MAX | ||
| 65 | #define XK_NO_MOD 0 | ||
| 66 | #define XK_SWITCH_MOD (1<<13|1<<14) | ||
| 67 | |||
| 68 | /* function definitions used in config.h */ | ||
| 69 | static void clipcopy(const Arg *); | ||
| 70 | static void clippaste(const Arg *); | ||
| 71 | static void numlock(const Arg *); | ||
| 72 | static void selpaste(const Arg *); | ||
| 73 | static void changealpha(const Arg *); | ||
| 74 | static void zoom(const Arg *); | ||
| 75 | static void zoomabs(const Arg *); | ||
| 76 | static void zoomreset(const Arg *); | ||
| 77 | static void ttysend(const Arg *); | ||
| 78 | |||
| 79 | /* config.h for applying patches and the configuration. */ | ||
| 80 | #include "config.h" | ||
| 81 | |||
| 82 | /* XEMBED messages */ | ||
| 83 | #define XEMBED_FOCUS_IN 4 | ||
| 84 | #define XEMBED_FOCUS_OUT 5 | ||
| 85 | |||
| 86 | /* macros */ | ||
| 87 | #define IS_SET(flag) ((win.mode & (flag)) != 0) | ||
| 88 | #define TRUERED(x) (((x) & 0xff0000) >> 8) | ||
| 89 | #define TRUEGREEN(x) (((x) & 0xff00)) | ||
| 90 | #define TRUEBLUE(x) (((x) & 0xff) << 8) | ||
| 91 | |||
| 92 | typedef XftDraw *Draw; | ||
| 93 | typedef XftColor Color; | ||
| 94 | typedef XftGlyphFontSpec GlyphFontSpec; | ||
| 95 | |||
| 96 | /* Purely graphic info */ | ||
| 97 | typedef struct { | ||
| 98 | int tw, th; /* tty width and height */ | ||
| 99 | int w, h; /* window width and height */ | ||
| 100 | int ch; /* char height */ | ||
| 101 | int cw; /* char width */ | ||
| 102 | int mode; /* window state/mode flags */ | ||
| 103 | int cursor; /* cursor style */ | ||
| 104 | } TermWindow; | ||
| 105 | |||
| 106 | typedef struct { | ||
| 107 | Display *dpy; | ||
| 108 | Colormap cmap; | ||
| 109 | Window win; | ||
| 110 | Drawable buf; | ||
| 111 | GlyphFontSpec *specbuf; /* font spec buffer used for rendering */ | ||
| 112 | Atom xembed, wmdeletewin, netwmname, netwmiconname, netwmpid; | ||
| 113 | struct { | ||
| 114 | XIM xim; | ||
| 115 | XIC xic; | ||
| 116 | XPoint spot; | ||
| 117 | XVaNestedList spotlist; | ||
| 118 | } ime; | ||
| 119 | Draw draw; | ||
| 120 | Visual *vis; | ||
| 121 | XSetWindowAttributes attrs; | ||
| 122 | int scr; | ||
| 123 | int isfixed; /* is fixed geometry? */ | ||
| 124 | int depth; /* bit depth */ | ||
| 125 | int l, t; /* left and top offset */ | ||
| 126 | int gm; /* geometry mask */ | ||
| 127 | } XWindow; | ||
| 128 | |||
| 129 | typedef struct { | ||
| 130 | Atom xtarget; | ||
| 131 | char *primary, *clipboard; | ||
| 132 | struct timespec tclick1; | ||
| 133 | struct timespec tclick2; | ||
| 134 | } XSelection; | ||
| 135 | |||
| 136 | /* Font structure */ | ||
| 137 | #define Font Font_ | ||
| 138 | typedef struct { | ||
| 139 | int height; | ||
| 140 | int width; | ||
| 141 | int ascent; | ||
| 142 | int descent; | ||
| 143 | int badslant; | ||
| 144 | int badweight; | ||
| 145 | short lbearing; | ||
| 146 | short rbearing; | ||
| 147 | XftFont *match; | ||
| 148 | FcFontSet *set; | ||
| 149 | FcPattern *pattern; | ||
| 150 | } Font; | ||
| 151 | |||
| 152 | /* Drawing Context */ | ||
| 153 | typedef struct { | ||
| 154 | Color *col; | ||
| 155 | size_t collen; | ||
| 156 | Font font, bfont, ifont, ibfont; | ||
| 157 | GC gc; | ||
| 158 | } DC; | ||
| 159 | |||
| 160 | static inline ushort sixd_to_16bit(int); | ||
| 161 | static int xmakeglyphfontspecs(XftGlyphFontSpec *, const Glyph *, int, int, int); | ||
| 162 | static void xdrawglyphfontspecs(const XftGlyphFontSpec *, Glyph, int, int, int); | ||
| 163 | static void xdrawglyph(Glyph, int, int); | ||
| 164 | static void xclear(int, int, int, int); | ||
| 165 | static int xgeommasktogravity(int); | ||
| 166 | static int ximopen(Display *); | ||
| 167 | static void ximinstantiate(Display *, XPointer, XPointer); | ||
| 168 | static void ximdestroy(XIM, XPointer, XPointer); | ||
| 169 | static int xicdestroy(XIC, XPointer, XPointer); | ||
| 170 | static void xinit(int, int); | ||
| 171 | static void cresize(int, int); | ||
| 172 | static void xresize(int, int); | ||
| 173 | static void xhints(void); | ||
| 174 | static int xloadcolor(int, const char *, Color *); | ||
| 175 | static int xloadfont(Font *, FcPattern *); | ||
| 176 | static int xloadsparefont(FcPattern *, int); | ||
| 177 | static void xloadsparefonts(void); | ||
| 178 | static void xloadfonts(const char *, double); | ||
| 179 | static void xunloadfont(Font *); | ||
| 180 | static void xunloadfonts(void); | ||
| 181 | static void xsetenv(void); | ||
| 182 | static void xseturgency(int); | ||
| 183 | static int evcol(XEvent *); | ||
| 184 | static int evrow(XEvent *); | ||
| 185 | static float clamp(float, float, float); | ||
| 186 | |||
| 187 | static void expose(XEvent *); | ||
| 188 | static void visibility(XEvent *); | ||
| 189 | static void unmap(XEvent *); | ||
| 190 | static void kpress(XEvent *); | ||
| 191 | static void cmessage(XEvent *); | ||
| 192 | static void resize(XEvent *); | ||
| 193 | static void focus(XEvent *); | ||
| 194 | static uint buttonmask(uint); | ||
| 195 | static int mouseaction(XEvent *, uint); | ||
| 196 | static void brelease(XEvent *); | ||
| 197 | static void bpress(XEvent *); | ||
| 198 | static void bmotion(XEvent *); | ||
| 199 | static void propnotify(XEvent *); | ||
| 200 | static void selnotify(XEvent *); | ||
| 201 | static void selclear_(XEvent *); | ||
| 202 | static void selrequest(XEvent *); | ||
| 203 | static void setsel(char *, Time); | ||
| 204 | static void mousesel(XEvent *, int); | ||
| 205 | static void mousereport(XEvent *); | ||
| 206 | static char *kmap(KeySym, uint); | ||
| 207 | static int match(uint, uint); | ||
| 208 | |||
| 209 | static void run(void); | ||
| 210 | static void usage(void); | ||
| 211 | |||
| 212 | static void (*handler[LASTEvent])(XEvent *) = { | ||
| 213 | [KeyPress] = kpress, | ||
| 214 | [ClientMessage] = cmessage, | ||
| 215 | [ConfigureNotify] = resize, | ||
| 216 | [VisibilityNotify] = visibility, | ||
| 217 | [UnmapNotify] = unmap, | ||
| 218 | [Expose] = expose, | ||
| 219 | [FocusIn] = focus, | ||
| 220 | [FocusOut] = focus, | ||
| 221 | [MotionNotify] = bmotion, | ||
| 222 | [ButtonPress] = bpress, | ||
| 223 | [ButtonRelease] = brelease, | ||
| 224 | /* | ||
| 225 | * Uncomment if you want the selection to disappear when you select something | ||
| 226 | * different in another window. | ||
| 227 | */ | ||
| 228 | /* [SelectionClear] = selclear_, */ | ||
| 229 | [SelectionNotify] = selnotify, | ||
| 230 | /* | ||
| 231 | * PropertyNotify is only turned on when there is some INCR transfer happening | ||
| 232 | * for the selection retrieval. | ||
| 233 | */ | ||
| 234 | [PropertyNotify] = propnotify, | ||
| 235 | [SelectionRequest] = selrequest, | ||
| 236 | }; | ||
| 237 | |||
| 238 | /* Globals */ | ||
| 239 | static DC dc; | ||
| 240 | static XWindow xw; | ||
| 241 | static XSelection xsel; | ||
| 242 | static TermWindow win; | ||
| 243 | |||
| 244 | /* Font Ring Cache */ | ||
| 245 | enum { | ||
| 246 | FRC_NORMAL, | ||
| 247 | FRC_ITALIC, | ||
| 248 | FRC_BOLD, | ||
| 249 | FRC_ITALICBOLD | ||
| 250 | }; | ||
| 251 | |||
| 252 | typedef struct { | ||
| 253 | XftFont *font; | ||
| 254 | int flags; | ||
| 255 | Rune unicodep; | ||
| 256 | } Fontcache; | ||
| 257 | |||
| 258 | /* Fontcache is an array now. A new font will be appended to the array. */ | ||
| 259 | static Fontcache *frc = NULL; | ||
| 260 | static int frclen = 0; | ||
| 261 | static int frccap = 0; | ||
| 262 | static char *usedfont = NULL; | ||
| 263 | static double usedfontsize = 0; | ||
| 264 | static double defaultfontsize = 0; | ||
| 265 | |||
| 266 | static char *opt_alpha = NULL; | ||
| 267 | static char *opt_class = NULL; | ||
| 268 | static char **opt_cmd = NULL; | ||
| 269 | static char *opt_embed = NULL; | ||
| 270 | static char *opt_font = NULL; | ||
| 271 | static char *opt_io = NULL; | ||
| 272 | static char *opt_line = NULL; | ||
| 273 | static char *opt_name = NULL; | ||
| 274 | static char *opt_title = NULL; | ||
| 275 | |||
| 276 | static int focused = 0; | ||
| 277 | |||
| 278 | static int oldbutton = 3; /* button event on startup: 3 = release */ | ||
| 279 | static uint buttons; /* bit field of pressed buttons */ | ||
| 280 | |||
| 281 | void | ||
| 282 | clipcopy(const Arg *dummy) | ||
| 283 | { | ||
| 284 | Atom clipboard; | ||
| 285 | |||
| 286 | free(xsel.clipboard); | ||
| 287 | xsel.clipboard = NULL; | ||
| 288 | |||
| 289 | if (xsel.primary != NULL) { | ||
| 290 | xsel.clipboard = xstrdup(xsel.primary); | ||
| 291 | clipboard = XInternAtom(xw.dpy, "CLIPBOARD", 0); | ||
| 292 | XSetSelectionOwner(xw.dpy, clipboard, xw.win, CurrentTime); | ||
| 293 | } | ||
| 294 | } | ||
| 295 | |||
| 296 | void | ||
| 297 | clippaste(const Arg *dummy) | ||
| 298 | { | ||
| 299 | Atom clipboard; | ||
| 300 | |||
| 301 | clipboard = XInternAtom(xw.dpy, "CLIPBOARD", 0); | ||
| 302 | XConvertSelection(xw.dpy, clipboard, xsel.xtarget, clipboard, | ||
| 303 | xw.win, CurrentTime); | ||
| 304 | } | ||
| 305 | |||
| 306 | void | ||
| 307 | selpaste(const Arg *dummy) | ||
| 308 | { | ||
| 309 | XConvertSelection(xw.dpy, XA_PRIMARY, xsel.xtarget, XA_PRIMARY, | ||
| 310 | xw.win, CurrentTime); | ||
| 311 | } | ||
| 312 | |||
| 313 | void | ||
| 314 | numlock(const Arg *dummy) | ||
| 315 | { | ||
| 316 | win.mode ^= MODE_NUMLOCK; | ||
| 317 | } | ||
| 318 | |||
| 319 | void | ||
| 320 | changealpha(const Arg *arg) | ||
| 321 | { | ||
| 322 | if((alpha > 0 && arg->f < 0) || (alpha < 1 && arg->f > 0)) | ||
| 323 | alpha += arg->f; | ||
| 324 | alpha = clamp(alpha, 0.0, 1.0); | ||
| 325 | alphaUnfocus = clamp(alpha-alphaOffset, 0.0, 1.0); | ||
| 326 | |||
| 327 | xloadcols(); | ||
| 328 | redraw(); | ||
| 329 | } | ||
| 330 | |||
| 331 | void | ||
| 332 | zoom(const Arg *arg) | ||
| 333 | { | ||
| 334 | Arg larg; | ||
| 335 | |||
| 336 | larg.f = usedfontsize + arg->f; | ||
| 337 | zoomabs(&larg); | ||
| 338 | } | ||
| 339 | |||
| 340 | void | ||
| 341 | zoomabs(const Arg *arg) | ||
| 342 | { | ||
| 343 | xunloadfonts(); | ||
| 344 | xloadfonts(usedfont, arg->f); | ||
| 345 | xloadsparefonts(); | ||
| 346 | cresize(0, 0); | ||
| 347 | redraw(); | ||
| 348 | xhints(); | ||
| 349 | } | ||
| 350 | |||
| 351 | void | ||
| 352 | zoomreset(const Arg *arg) | ||
| 353 | { | ||
| 354 | Arg larg; | ||
| 355 | |||
| 356 | if (defaultfontsize > 0) { | ||
| 357 | larg.f = defaultfontsize; | ||
| 358 | zoomabs(&larg); | ||
| 359 | } | ||
| 360 | } | ||
| 361 | |||
| 362 | void | ||
| 363 | ttysend(const Arg *arg) | ||
| 364 | { | ||
| 365 | ttywrite(arg->s, strlen(arg->s), 1); | ||
| 366 | } | ||
| 367 | |||
| 368 | int | ||
| 369 | evcol(XEvent *e) | ||
| 370 | { | ||
| 371 | int x = e->xbutton.x - borderpx; | ||
| 372 | LIMIT(x, 0, win.tw - 1); | ||
| 373 | return x / win.cw; | ||
| 374 | } | ||
| 375 | |||
| 376 | int | ||
| 377 | evrow(XEvent *e) | ||
| 378 | { | ||
| 379 | int y = e->xbutton.y - borderpx; | ||
| 380 | LIMIT(y, 0, win.th - 1); | ||
| 381 | return y / win.ch; | ||
| 382 | } | ||
| 383 | |||
| 384 | float | ||
| 385 | clamp(float value, float lower, float upper) { | ||
| 386 | if(value < lower) | ||
| 387 | return lower; | ||
| 388 | if(value > upper) | ||
| 389 | return upper; | ||
| 390 | return value; | ||
| 391 | } | ||
| 392 | |||
| 393 | void | ||
| 394 | mousesel(XEvent *e, int done) | ||
| 395 | { | ||
| 396 | int type, seltype = SEL_REGULAR; | ||
| 397 | uint state = e->xbutton.state & ~(Button1Mask | forcemousemod); | ||
| 398 | |||
| 399 | for (type = 1; type < LEN(selmasks); ++type) { | ||
| 400 | if (match(selmasks[type], state)) { | ||
| 401 | seltype = type; | ||
| 402 | break; | ||
| 403 | } | ||
| 404 | } | ||
| 405 | selextend(evcol(e), evrow(e), seltype, done); | ||
| 406 | if (done) | ||
| 407 | setsel(getsel(), e->xbutton.time); | ||
| 408 | } | ||
| 409 | |||
| 410 | void | ||
| 411 | mousereport(XEvent *e) | ||
| 412 | { | ||
| 413 | int len, x = evcol(e), y = evrow(e), | ||
| 414 | button = e->xbutton.button, state = e->xbutton.state; | ||
| 415 | char buf[40]; | ||
| 416 | static int ox, oy; | ||
| 417 | |||
| 418 | /* from urxvt */ | ||
| 419 | if (e->xbutton.type == MotionNotify) { | ||
| 420 | if (x == ox && y == oy) | ||
| 421 | return; | ||
| 422 | if (!IS_SET(MODE_MOUSEMOTION) && !IS_SET(MODE_MOUSEMANY)) | ||
| 423 | return; | ||
| 424 | /* MOUSE_MOTION: no reporting if no button is pressed */ | ||
| 425 | if (IS_SET(MODE_MOUSEMOTION) && oldbutton == 3) | ||
| 426 | return; | ||
| 427 | |||
| 428 | button = oldbutton + 32; | ||
| 429 | ox = x; | ||
| 430 | oy = y; | ||
| 431 | } else { | ||
| 432 | if (!IS_SET(MODE_MOUSESGR) && e->xbutton.type == ButtonRelease) { | ||
| 433 | button = 3; | ||
| 434 | } else { | ||
| 435 | button -= Button1; | ||
| 436 | if (button >= 3) | ||
| 437 | button += 64 - 3; | ||
| 438 | } | ||
| 439 | if (e->xbutton.type == ButtonPress) { | ||
| 440 | oldbutton = button; | ||
| 441 | ox = x; | ||
| 442 | oy = y; | ||
| 443 | } else if (e->xbutton.type == ButtonRelease) { | ||
| 444 | oldbutton = 3; | ||
| 445 | /* MODE_MOUSEX10: no button release reporting */ | ||
| 446 | if (IS_SET(MODE_MOUSEX10)) | ||
| 447 | return; | ||
| 448 | if (button == 64 || button == 65) | ||
| 449 | return; | ||
| 450 | } | ||
| 451 | } | ||
| 452 | |||
| 453 | if (!IS_SET(MODE_MOUSEX10)) { | ||
| 454 | button += ((state & ShiftMask ) ? 4 : 0) | ||
| 455 | + ((state & Mod4Mask ) ? 8 : 0) | ||
| 456 | + ((state & ControlMask) ? 16 : 0); | ||
| 457 | } | ||
| 458 | |||
| 459 | if (IS_SET(MODE_MOUSESGR)) { | ||
| 460 | len = snprintf(buf, sizeof(buf), "\033[<%d;%d;%d%c", | ||
| 461 | button, x+1, y+1, | ||
| 462 | e->type == ButtonRelease ? 'm' : 'M'); | ||
| 463 | } else if (x < 223 && y < 223) { | ||
| 464 | len = snprintf(buf, sizeof(buf), "\033[M%c%c%c", | ||
| 465 | 32+button, 32+x+1, 32+y+1); | ||
| 466 | } else { | ||
| 467 | return; | ||
| 468 | } | ||
| 469 | |||
| 470 | ttywrite(buf, len, 0); | ||
| 471 | } | ||
| 472 | |||
| 473 | uint | ||
| 474 | buttonmask(uint button) | ||
| 475 | { | ||
| 476 | return button == Button1 ? Button1Mask | ||
| 477 | : button == Button2 ? Button2Mask | ||
| 478 | : button == Button3 ? Button3Mask | ||
| 479 | : button == Button4 ? Button4Mask | ||
| 480 | : button == Button5 ? Button5Mask | ||
| 481 | : 0; | ||
| 482 | } | ||
| 483 | |||
| 484 | int | ||
| 485 | mouseaction(XEvent *e, uint release) | ||
| 486 | { | ||
| 487 | MouseShortcut *ms; | ||
| 488 | |||
| 489 | /* ignore Button<N>mask for Button<N> - it's set on release */ | ||
| 490 | uint state = e->xbutton.state & ~buttonmask(e->xbutton.button); | ||
| 491 | |||
| 492 | for (ms = mshortcuts; ms < mshortcuts + LEN(mshortcuts); ms++) { | ||
| 493 | if (ms->release == release && | ||
| 494 | ms->button == e->xbutton.button && | ||
| 495 | (match(ms->mod, state) || /* exact or forced */ | ||
| 496 | match(ms->mod, state & ~forcemousemod))) { | ||
| 497 | ms->func(&(ms->arg)); | ||
| 498 | return 1; | ||
| 499 | } | ||
| 500 | } | ||
| 501 | |||
| 502 | return 0; | ||
| 503 | } | ||
| 504 | |||
| 505 | void | ||
| 506 | bpress(XEvent *e) | ||
| 507 | { | ||
| 508 | int btn = e->xbutton.button; | ||
| 509 | struct timespec now; | ||
| 510 | int snap; | ||
| 511 | |||
| 512 | if (1 <= btn && btn <= 11) | ||
| 513 | buttons |= 1 << (btn-1); | ||
| 514 | |||
| 515 | if (IS_SET(MODE_MOUSE) && !(e->xbutton.state & forcemousemod)) { | ||
| 516 | mousereport(e); | ||
| 517 | return; | ||
| 518 | } | ||
| 519 | |||
| 520 | if (mouseaction(e, 0)) | ||
| 521 | return; | ||
| 522 | |||
| 523 | if (btn == Button1) { | ||
| 524 | /* | ||
| 525 | * If the user clicks below predefined timeouts specific | ||
| 526 | * snapping behaviour is exposed. | ||
| 527 | */ | ||
| 528 | clock_gettime(CLOCK_MONOTONIC, &now); | ||
| 529 | if (TIMEDIFF(now, xsel.tclick2) <= tripleclicktimeout) { | ||
| 530 | snap = SNAP_LINE; | ||
| 531 | } else if (TIMEDIFF(now, xsel.tclick1) <= doubleclicktimeout) { | ||
| 532 | snap = SNAP_WORD; | ||
| 533 | } else { | ||
| 534 | snap = 0; | ||
| 535 | } | ||
| 536 | xsel.tclick2 = xsel.tclick1; | ||
| 537 | xsel.tclick1 = now; | ||
| 538 | |||
| 539 | selstart(evcol(e), evrow(e), snap); | ||
| 540 | } | ||
| 541 | } | ||
| 542 | |||
| 543 | void | ||
| 544 | propnotify(XEvent *e) | ||
| 545 | { | ||
| 546 | XPropertyEvent *xpev; | ||
| 547 | Atom clipboard = XInternAtom(xw.dpy, "CLIPBOARD", 0); | ||
| 548 | |||
| 549 | xpev = &e->xproperty; | ||
| 550 | if (xpev->state == PropertyNewValue && | ||
| 551 | (xpev->atom == XA_PRIMARY || | ||
| 552 | xpev->atom == clipboard)) { | ||
| 553 | selnotify(e); | ||
| 554 | } | ||
| 555 | } | ||
| 556 | |||
| 557 | void | ||
| 558 | selnotify(XEvent *e) | ||
| 559 | { | ||
| 560 | ulong nitems, ofs, rem; | ||
| 561 | int format; | ||
| 562 | uchar *data, *last, *repl; | ||
| 563 | Atom type, incratom, property = None; | ||
| 564 | |||
| 565 | incratom = XInternAtom(xw.dpy, "INCR", 0); | ||
| 566 | |||
| 567 | ofs = 0; | ||
| 568 | if (e->type == SelectionNotify) | ||
| 569 | property = e->xselection.property; | ||
| 570 | else if (e->type == PropertyNotify) | ||
| 571 | property = e->xproperty.atom; | ||
| 572 | |||
| 573 | if (property == None) | ||
| 574 | return; | ||
| 575 | |||
| 576 | do { | ||
| 577 | if (XGetWindowProperty(xw.dpy, xw.win, property, ofs, | ||
| 578 | BUFSIZ/4, False, AnyPropertyType, | ||
| 579 | &type, &format, &nitems, &rem, | ||
| 580 | &data)) { | ||
| 581 | fprintf(stderr, "Clipboard allocation failed\n"); | ||
| 582 | return; | ||
| 583 | } | ||
| 584 | |||
| 585 | if (e->type == PropertyNotify && nitems == 0 && rem == 0) { | ||
| 586 | /* | ||
| 587 | * If there is some PropertyNotify with no data, then | ||
| 588 | * this is the signal of the selection owner that all | ||
| 589 | * data has been transferred. We won't need to receive | ||
| 590 | * PropertyNotify events anymore. | ||
| 591 | */ | ||
| 592 | MODBIT(xw.attrs.event_mask, 0, PropertyChangeMask); | ||
| 593 | XChangeWindowAttributes(xw.dpy, xw.win, CWEventMask, | ||
| 594 | &xw.attrs); | ||
| 595 | } | ||
| 596 | |||
| 597 | if (type == incratom) { | ||
| 598 | /* | ||
| 599 | * Activate the PropertyNotify events so we receive | ||
| 600 | * when the selection owner does send us the next | ||
| 601 | * chunk of data. | ||
| 602 | */ | ||
| 603 | MODBIT(xw.attrs.event_mask, 1, PropertyChangeMask); | ||
| 604 | XChangeWindowAttributes(xw.dpy, xw.win, CWEventMask, | ||
| 605 | &xw.attrs); | ||
| 606 | |||
| 607 | /* | ||
| 608 | * Deleting the property is the transfer start signal. | ||
| 609 | */ | ||
| 610 | XDeleteProperty(xw.dpy, xw.win, (int)property); | ||
| 611 | continue; | ||
| 612 | } | ||
| 613 | |||
| 614 | /* | ||
| 615 | * As seen in getsel: | ||
| 616 | * Line endings are inconsistent in the terminal and GUI world | ||
| 617 | * copy and pasting. When receiving some selection data, | ||
| 618 | * replace all '\n' with '\r'. | ||
| 619 | * FIXME: Fix the computer world. | ||
| 620 | */ | ||
| 621 | repl = data; | ||
| 622 | last = data + nitems * format / 8; | ||
| 623 | while ((repl = memchr(repl, '\n', last - repl))) { | ||
| 624 | *repl++ = '\r'; | ||
| 625 | } | ||
| 626 | |||
| 627 | if (IS_SET(MODE_BRCKTPASTE) && ofs == 0) | ||
| 628 | ttywrite("\033[200~", 6, 0); | ||
| 629 | ttywrite((char *)data, nitems * format / 8, 1); | ||
| 630 | if (IS_SET(MODE_BRCKTPASTE) && rem == 0) | ||
| 631 | ttywrite("\033[201~", 6, 0); | ||
| 632 | XFree(data); | ||
| 633 | /* number of 32-bit chunks returned */ | ||
| 634 | ofs += nitems * format / 32; | ||
| 635 | } while (rem > 0); | ||
| 636 | |||
| 637 | /* | ||
| 638 | * Deleting the property again tells the selection owner to send the | ||
| 639 | * next data chunk in the property. | ||
| 640 | */ | ||
| 641 | XDeleteProperty(xw.dpy, xw.win, (int)property); | ||
| 642 | } | ||
| 643 | |||
| 644 | void | ||
| 645 | xclipcopy(void) | ||
| 646 | { | ||
| 647 | clipcopy(NULL); | ||
| 648 | } | ||
| 649 | |||
| 650 | void | ||
| 651 | selclear_(XEvent *e) | ||
| 652 | { | ||
| 653 | selclear(); | ||
| 654 | } | ||
| 655 | |||
| 656 | void | ||
| 657 | selrequest(XEvent *e) | ||
| 658 | { | ||
| 659 | XSelectionRequestEvent *xsre; | ||
| 660 | XSelectionEvent xev; | ||
| 661 | Atom xa_targets, string, clipboard; | ||
| 662 | char *seltext; | ||
| 663 | |||
| 664 | xsre = (XSelectionRequestEvent *) e; | ||
| 665 | xev.type = SelectionNotify; | ||
| 666 | xev.requestor = xsre->requestor; | ||
| 667 | xev.selection = xsre->selection; | ||
| 668 | xev.target = xsre->target; | ||
| 669 | xev.time = xsre->time; | ||
| 670 | if (xsre->property == None) | ||
| 671 | xsre->property = xsre->target; | ||
| 672 | |||
| 673 | /* reject */ | ||
| 674 | xev.property = None; | ||
| 675 | |||
| 676 | xa_targets = XInternAtom(xw.dpy, "TARGETS", 0); | ||
| 677 | if (xsre->target == xa_targets) { | ||
| 678 | /* respond with the supported type */ | ||
| 679 | string = xsel.xtarget; | ||
| 680 | XChangeProperty(xsre->display, xsre->requestor, xsre->property, | ||
| 681 | XA_ATOM, 32, PropModeReplace, | ||
| 682 | (uchar *) &string, 1); | ||
| 683 | xev.property = xsre->property; | ||
| 684 | } else if (xsre->target == xsel.xtarget || xsre->target == XA_STRING) { | ||
| 685 | /* | ||
| 686 | * xith XA_STRING non ascii characters may be incorrect in the | ||
| 687 | * requestor. It is not our problem, use utf8. | ||
| 688 | */ | ||
| 689 | clipboard = XInternAtom(xw.dpy, "CLIPBOARD", 0); | ||
| 690 | if (xsre->selection == XA_PRIMARY) { | ||
| 691 | seltext = xsel.primary; | ||
| 692 | } else if (xsre->selection == clipboard) { | ||
| 693 | seltext = xsel.clipboard; | ||
| 694 | } else { | ||
| 695 | fprintf(stderr, | ||
| 696 | "Unhandled clipboard selection 0x%lx\n", | ||
| 697 | xsre->selection); | ||
| 698 | return; | ||
| 699 | } | ||
| 700 | if (seltext != NULL) { | ||
| 701 | XChangeProperty(xsre->display, xsre->requestor, | ||
| 702 | xsre->property, xsre->target, | ||
| 703 | 8, PropModeReplace, | ||
| 704 | (uchar *)seltext, strlen(seltext)); | ||
| 705 | xev.property = xsre->property; | ||
| 706 | } | ||
| 707 | } | ||
| 708 | |||
| 709 | /* all done, send a notification to the listener */ | ||
| 710 | if (!XSendEvent(xsre->display, xsre->requestor, 1, 0, (XEvent *) &xev)) | ||
| 711 | fprintf(stderr, "Error sending SelectionNotify event\n"); | ||
| 712 | } | ||
| 713 | |||
| 714 | void | ||
| 715 | setsel(char *str, Time t) | ||
| 716 | { | ||
| 717 | if (!str) | ||
| 718 | return; | ||
| 719 | |||
| 720 | free(xsel.primary); | ||
| 721 | xsel.primary = str; | ||
| 722 | |||
| 723 | XSetSelectionOwner(xw.dpy, XA_PRIMARY, xw.win, t); | ||
| 724 | if (XGetSelectionOwner(xw.dpy, XA_PRIMARY) != xw.win) | ||
| 725 | selclear(); | ||
| 726 | } | ||
| 727 | |||
| 728 | void | ||
| 729 | xsetsel(char *str) | ||
| 730 | { | ||
| 731 | setsel(str, CurrentTime); | ||
| 732 | } | ||
| 733 | |||
| 734 | void | ||
| 735 | brelease(XEvent *e) | ||
| 736 | { | ||
| 737 | int btn = e->xbutton.button; | ||
| 738 | |||
| 739 | if (1 <= btn && btn <= 11) | ||
| 740 | buttons &= ~(1 << (btn-1)); | ||
| 741 | |||
| 742 | if (IS_SET(MODE_MOUSE) && !(e->xbutton.state & forcemousemod)) { | ||
| 743 | mousereport(e); | ||
| 744 | return; | ||
| 745 | } | ||
| 746 | |||
| 747 | if (mouseaction(e, 1)) | ||
| 748 | return; | ||
| 749 | if (btn == Button1) | ||
| 750 | mousesel(e, 1); | ||
| 751 | } | ||
| 752 | |||
| 753 | void | ||
| 754 | bmotion(XEvent *e) | ||
| 755 | { | ||
| 756 | if (IS_SET(MODE_MOUSE) && !(e->xbutton.state & forcemousemod)) { | ||
| 757 | mousereport(e); | ||
| 758 | return; | ||
| 759 | } | ||
| 760 | |||
| 761 | mousesel(e, 0); | ||
| 762 | } | ||
| 763 | |||
| 764 | void | ||
| 765 | cresize(int width, int height) | ||
| 766 | { | ||
| 767 | int col, row; | ||
| 768 | |||
| 769 | if (width != 0) | ||
| 770 | win.w = width; | ||
| 771 | if (height != 0) | ||
| 772 | win.h = height; | ||
| 773 | |||
| 774 | col = (win.w - 2 * borderpx) / win.cw; | ||
| 775 | row = (win.h - 2 * borderpx) / win.ch; | ||
| 776 | col = MAX(1, col); | ||
| 777 | row = MAX(1, row); | ||
| 778 | |||
| 779 | tresize(col, row); | ||
| 780 | xresize(col, row); | ||
| 781 | ttyresize(win.tw, win.th); | ||
| 782 | } | ||
| 783 | |||
| 784 | void | ||
| 785 | xresize(int col, int row) | ||
| 786 | { | ||
| 787 | win.tw = col * win.cw; | ||
| 788 | win.th = row * win.ch; | ||
| 789 | |||
| 790 | XFreePixmap(xw.dpy, xw.buf); | ||
| 791 | xw.buf = XCreatePixmap(xw.dpy, xw.win, win.w, win.h, | ||
| 792 | xw.depth); | ||
| 793 | XftDrawChange(xw.draw, xw.buf); | ||
| 794 | xclear(0, 0, win.w, win.h); | ||
| 795 | |||
| 796 | /* resize to new width */ | ||
| 797 | xw.specbuf = xrealloc(xw.specbuf, col * sizeof(GlyphFontSpec)); | ||
| 798 | } | ||
| 799 | |||
| 800 | ushort | ||
| 801 | sixd_to_16bit(int x) | ||
| 802 | { | ||
| 803 | return x == 0 ? 0 : 0x3737 + 0x2828 * x; | ||
| 804 | } | ||
| 805 | |||
| 806 | int | ||
| 807 | xloadcolor(int i, const char *name, Color *ncolor) | ||
| 808 | { | ||
| 809 | XRenderColor color = { .alpha = 0xffff }; | ||
| 810 | |||
| 811 | if (!name) { | ||
| 812 | if (BETWEEN(i, 16, 255)) { /* 256 color */ | ||
| 813 | if (i < 6*6*6+16) { /* same colors as xterm */ | ||
| 814 | color.red = sixd_to_16bit( ((i-16)/36)%6 ); | ||
| 815 | color.green = sixd_to_16bit( ((i-16)/6) %6 ); | ||
| 816 | color.blue = sixd_to_16bit( ((i-16)/1) %6 ); | ||
| 817 | } else { /* greyscale */ | ||
| 818 | color.red = 0x0808 + 0x0a0a * (i - (6*6*6+16)); | ||
| 819 | color.green = color.blue = color.red; | ||
| 820 | } | ||
| 821 | return XftColorAllocValue(xw.dpy, xw.vis, | ||
| 822 | xw.cmap, &color, ncolor); | ||
| 823 | } else | ||
| 824 | name = colorname[i]; | ||
| 825 | } | ||
| 826 | |||
| 827 | return XftColorAllocName(xw.dpy, xw.vis, xw.cmap, name, ncolor); | ||
| 828 | } | ||
| 829 | |||
| 830 | void | ||
| 831 | xloadalpha(void) | ||
| 832 | { | ||
| 833 | float const usedAlpha = focused ? alpha : alphaUnfocus; | ||
| 834 | if (opt_alpha) alpha = strtof(opt_alpha, NULL); | ||
| 835 | dc.col[defaultbg].color.alpha = (unsigned short)(0xffff * usedAlpha); | ||
| 836 | dc.col[defaultbg].pixel &= 0x00FFFFFF; | ||
| 837 | dc.col[defaultbg].pixel |= (unsigned char)(0xff * usedAlpha) << 24; | ||
| 838 | } | ||
| 839 | |||
| 840 | void | ||
| 841 | xloadcols(void) | ||
| 842 | { | ||
| 843 | int i; | ||
| 844 | static int loaded; | ||
| 845 | Color *cp; | ||
| 846 | |||
| 847 | if (!loaded) { | ||
| 848 | dc.collen = 1 + (defaultbg = MAX(LEN(colorname), 256)); | ||
| 849 | dc.col = xmalloc(dc.collen * sizeof(Color)); | ||
| 850 | } | ||
| 851 | |||
| 852 | for (i = 0; i+1 < dc.collen; i++) | ||
| 853 | if (!xloadcolor(i, NULL, &dc.col[i])) { | ||
| 854 | if (colorname[i]) | ||
| 855 | die("could not allocate color '%s'\n", colorname[i]); | ||
| 856 | else | ||
| 857 | die("could not allocate color %d\n", i); | ||
| 858 | } | ||
| 859 | |||
| 860 | if (dc.collen) // cannot die, as the color is already loaded. | ||
| 861 | xloadcolor(background, NULL, &dc.col[defaultbg]); | ||
| 862 | |||
| 863 | xloadalpha(); | ||
| 864 | loaded = 1; | ||
| 865 | } | ||
| 866 | |||
| 867 | int | ||
| 868 | xgetcolor(int x, unsigned char *r, unsigned char *g, unsigned char *b) | ||
| 869 | { | ||
| 870 | if (!BETWEEN(x, 0, dc.collen)) | ||
| 871 | return 1; | ||
| 872 | |||
| 873 | *r = dc.col[x].color.red >> 8; | ||
| 874 | *g = dc.col[x].color.green >> 8; | ||
| 875 | *b = dc.col[x].color.blue >> 8; | ||
| 876 | |||
| 877 | return 0; | ||
| 878 | } | ||
| 879 | |||
| 880 | int | ||
| 881 | xsetcolorname(int x, const char *name) | ||
| 882 | { | ||
| 883 | Color ncolor; | ||
| 884 | |||
| 885 | if (!BETWEEN(x, 0, dc.collen)) | ||
| 886 | return 1; | ||
| 887 | |||
| 888 | if (!xloadcolor(x, name, &ncolor)) | ||
| 889 | return 1; | ||
| 890 | |||
| 891 | XftColorFree(xw.dpy, xw.vis, xw.cmap, &dc.col[x]); | ||
| 892 | dc.col[x] = ncolor; | ||
| 893 | |||
| 894 | return 0; | ||
| 895 | } | ||
| 896 | |||
| 897 | /* | ||
| 898 | * Absolute coordinates. | ||
| 899 | */ | ||
| 900 | void | ||
| 901 | xclear(int x1, int y1, int x2, int y2) | ||
| 902 | { | ||
| 903 | XftDrawRect(xw.draw, | ||
| 904 | &dc.col[IS_SET(MODE_REVERSE)? defaultfg : defaultbg], | ||
| 905 | x1, y1, x2-x1, y2-y1); | ||
| 906 | } | ||
| 907 | |||
| 908 | void | ||
| 909 | xhints(void) | ||
| 910 | { | ||
| 911 | XClassHint class = {opt_name ? opt_name : "st", | ||
| 912 | opt_class ? opt_class : "St"}; | ||
| 913 | XWMHints wm = {.flags = InputHint, .input = 1}; | ||
| 914 | XSizeHints *sizeh; | ||
| 915 | |||
| 916 | sizeh = XAllocSizeHints(); | ||
| 917 | |||
| 918 | sizeh->flags = PSize | PResizeInc | PBaseSize | PMinSize; | ||
| 919 | sizeh->height = win.h; | ||
| 920 | sizeh->width = win.w; | ||
| 921 | sizeh->height_inc = win.ch; | ||
| 922 | sizeh->width_inc = win.cw; | ||
| 923 | sizeh->base_height = 2 * borderpx; | ||
| 924 | sizeh->base_width = 2 * borderpx; | ||
| 925 | sizeh->min_height = win.ch + 2 * borderpx; | ||
| 926 | sizeh->min_width = win.cw + 2 * borderpx; | ||
| 927 | if (xw.isfixed) { | ||
| 928 | sizeh->flags |= PMaxSize; | ||
| 929 | sizeh->min_width = sizeh->max_width = win.w; | ||
| 930 | sizeh->min_height = sizeh->max_height = win.h; | ||
| 931 | } | ||
| 932 | if (xw.gm & (XValue|YValue)) { | ||
| 933 | sizeh->flags |= USPosition | PWinGravity; | ||
| 934 | sizeh->x = xw.l; | ||
| 935 | sizeh->y = xw.t; | ||
| 936 | sizeh->win_gravity = xgeommasktogravity(xw.gm); | ||
| 937 | } | ||
| 938 | |||
| 939 | XSetWMProperties(xw.dpy, xw.win, NULL, NULL, NULL, 0, sizeh, &wm, | ||
| 940 | &class); | ||
| 941 | XFree(sizeh); | ||
| 942 | } | ||
| 943 | |||
| 944 | int | ||
| 945 | xgeommasktogravity(int mask) | ||
| 946 | { | ||
| 947 | switch (mask & (XNegative|YNegative)) { | ||
| 948 | case 0: | ||
| 949 | return NorthWestGravity; | ||
| 950 | case XNegative: | ||
| 951 | return NorthEastGravity; | ||
| 952 | case YNegative: | ||
| 953 | return SouthWestGravity; | ||
| 954 | } | ||
| 955 | |||
| 956 | return SouthEastGravity; | ||
| 957 | } | ||
| 958 | |||
| 959 | int | ||
| 960 | xloadfont(Font *f, FcPattern *pattern) | ||
| 961 | { | ||
| 962 | FcPattern *configured; | ||
| 963 | FcPattern *match; | ||
| 964 | FcResult result; | ||
| 965 | XGlyphInfo extents; | ||
| 966 | int wantattr, haveattr; | ||
| 967 | |||
| 968 | /* | ||
| 969 | * Manually configure instead of calling XftMatchFont | ||
| 970 | * so that we can use the configured pattern for | ||
| 971 | * "missing glyph" lookups. | ||
| 972 | */ | ||
| 973 | configured = FcPatternDuplicate(pattern); | ||
| 974 | if (!configured) | ||
| 975 | return 1; | ||
| 976 | |||
| 977 | FcConfigSubstitute(NULL, configured, FcMatchPattern); | ||
| 978 | XftDefaultSubstitute(xw.dpy, xw.scr, configured); | ||
| 979 | |||
| 980 | match = FcFontMatch(NULL, configured, &result); | ||
| 981 | if (!match) { | ||
| 982 | FcPatternDestroy(configured); | ||
| 983 | return 1; | ||
| 984 | } | ||
| 985 | |||
| 986 | if (!(f->match = XftFontOpenPattern(xw.dpy, match))) { | ||
| 987 | FcPatternDestroy(configured); | ||
| 988 | FcPatternDestroy(match); | ||
| 989 | return 1; | ||
| 990 | } | ||
| 991 | |||
| 992 | if ((XftPatternGetInteger(pattern, "slant", 0, &wantattr) == | ||
| 993 | XftResultMatch)) { | ||
| 994 | /* | ||
| 995 | * Check if xft was unable to find a font with the appropriate | ||
| 996 | * slant but gave us one anyway. Try to mitigate. | ||
| 997 | */ | ||
| 998 | if ((XftPatternGetInteger(f->match->pattern, "slant", 0, | ||
| 999 | &haveattr) != XftResultMatch) || haveattr < wantattr) { | ||
| 1000 | f->badslant = 1; | ||
| 1001 | fputs("font slant does not match\n", stderr); | ||
| 1002 | } | ||
| 1003 | } | ||
| 1004 | |||
| 1005 | if ((XftPatternGetInteger(pattern, "weight", 0, &wantattr) == | ||
| 1006 | XftResultMatch)) { | ||
| 1007 | if ((XftPatternGetInteger(f->match->pattern, "weight", 0, | ||
| 1008 | &haveattr) != XftResultMatch) || haveattr != wantattr) { | ||
| 1009 | f->badweight = 1; | ||
| 1010 | fputs("font weight does not match\n", stderr); | ||
| 1011 | } | ||
| 1012 | } | ||
| 1013 | |||
| 1014 | XftTextExtentsUtf8(xw.dpy, f->match, | ||
| 1015 | (const FcChar8 *) ascii_printable, | ||
| 1016 | strlen(ascii_printable), &extents); | ||
| 1017 | |||
| 1018 | f->set = NULL; | ||
| 1019 | f->pattern = configured; | ||
| 1020 | |||
| 1021 | f->ascent = f->match->ascent; | ||
| 1022 | f->descent = f->match->descent; | ||
| 1023 | f->lbearing = 0; | ||
| 1024 | f->rbearing = f->match->max_advance_width; | ||
| 1025 | |||
| 1026 | f->height = f->ascent + f->descent; | ||
| 1027 | f->width = DIVCEIL(extents.xOff, strlen(ascii_printable)); | ||
| 1028 | |||
| 1029 | return 0; | ||
| 1030 | } | ||
| 1031 | |||
| 1032 | void | ||
| 1033 | xloadfonts(const char *fontstr, double fontsize) | ||
| 1034 | { | ||
| 1035 | FcPattern *pattern; | ||
| 1036 | double fontval; | ||
| 1037 | |||
| 1038 | if (fontstr[0] == '-') | ||
| 1039 | pattern = XftXlfdParse(fontstr, False, False); | ||
| 1040 | else | ||
| 1041 | pattern = FcNameParse((const FcChar8 *)fontstr); | ||
| 1042 | |||
| 1043 | if (!pattern) | ||
| 1044 | die("can't open font %s\n", fontstr); | ||
| 1045 | |||
| 1046 | if (fontsize > 1) { | ||
| 1047 | FcPatternDel(pattern, FC_PIXEL_SIZE); | ||
| 1048 | FcPatternDel(pattern, FC_SIZE); | ||
| 1049 | FcPatternAddDouble(pattern, FC_PIXEL_SIZE, (double)fontsize); | ||
| 1050 | usedfontsize = fontsize; | ||
| 1051 | } else { | ||
| 1052 | if (FcPatternGetDouble(pattern, FC_PIXEL_SIZE, 0, &fontval) == | ||
| 1053 | FcResultMatch) { | ||
| 1054 | usedfontsize = fontval; | ||
| 1055 | } else if (FcPatternGetDouble(pattern, FC_SIZE, 0, &fontval) == | ||
| 1056 | FcResultMatch) { | ||
| 1057 | usedfontsize = -1; | ||
| 1058 | } else { | ||
| 1059 | /* | ||
| 1060 | * Default font size is 12, if none given. This is to | ||
| 1061 | * have a known usedfontsize value. | ||
| 1062 | */ | ||
| 1063 | FcPatternAddDouble(pattern, FC_PIXEL_SIZE, 12); | ||
| 1064 | usedfontsize = 12; | ||
| 1065 | } | ||
| 1066 | defaultfontsize = usedfontsize; | ||
| 1067 | } | ||
| 1068 | |||
| 1069 | if (xloadfont(&dc.font, pattern)) | ||
| 1070 | die("can't open font %s\n", fontstr); | ||
| 1071 | |||
| 1072 | if (usedfontsize < 0) { | ||
| 1073 | FcPatternGetDouble(dc.font.match->pattern, | ||
| 1074 | FC_PIXEL_SIZE, 0, &fontval); | ||
| 1075 | usedfontsize = fontval; | ||
| 1076 | if (fontsize == 0) | ||
| 1077 | defaultfontsize = fontval; | ||
| 1078 | } | ||
| 1079 | |||
| 1080 | /* Setting character width and height. */ | ||
| 1081 | win.cw = ceilf(dc.font.width * cwscale); | ||
| 1082 | win.ch = ceilf(dc.font.height * chscale); | ||
| 1083 | |||
| 1084 | FcPatternDel(pattern, FC_SLANT); | ||
| 1085 | FcPatternAddInteger(pattern, FC_SLANT, FC_SLANT_ITALIC); | ||
| 1086 | if (xloadfont(&dc.ifont, pattern)) | ||
| 1087 | die("can't open font %s\n", fontstr); | ||
| 1088 | |||
| 1089 | FcPatternDel(pattern, FC_WEIGHT); | ||
| 1090 | FcPatternAddInteger(pattern, FC_WEIGHT, FC_WEIGHT_BOLD); | ||
| 1091 | if (xloadfont(&dc.ibfont, pattern)) | ||
| 1092 | die("can't open font %s\n", fontstr); | ||
| 1093 | |||
| 1094 | FcPatternDel(pattern, FC_SLANT); | ||
| 1095 | FcPatternAddInteger(pattern, FC_SLANT, FC_SLANT_ROMAN); | ||
| 1096 | if (xloadfont(&dc.bfont, pattern)) | ||
| 1097 | die("can't open font %s\n", fontstr); | ||
| 1098 | |||
| 1099 | FcPatternDestroy(pattern); | ||
| 1100 | } | ||
| 1101 | |||
| 1102 | int | ||
| 1103 | xloadsparefont(FcPattern *pattern, int flags) | ||
| 1104 | { | ||
| 1105 | FcPattern *match; | ||
| 1106 | FcResult result; | ||
| 1107 | |||
| 1108 | match = FcFontMatch(NULL, pattern, &result); | ||
| 1109 | if (!match) { | ||
| 1110 | return 1; | ||
| 1111 | } | ||
| 1112 | |||
| 1113 | if (!(frc[frclen].font = XftFontOpenPattern(xw.dpy, match))) { | ||
| 1114 | FcPatternDestroy(match); | ||
| 1115 | return 1; | ||
| 1116 | } | ||
| 1117 | |||
| 1118 | frc[frclen].flags = flags; | ||
| 1119 | /* Believe U+0000 glyph will present in each default font */ | ||
| 1120 | frc[frclen].unicodep = 0; | ||
| 1121 | frclen++; | ||
| 1122 | |||
| 1123 | return 0; | ||
| 1124 | } | ||
| 1125 | |||
| 1126 | void | ||
| 1127 | xloadsparefonts(void) | ||
| 1128 | { | ||
| 1129 | FcPattern *pattern; | ||
| 1130 | double sizeshift, fontval; | ||
| 1131 | int fc; | ||
| 1132 | char **fp; | ||
| 1133 | |||
| 1134 | if (frclen != 0) | ||
| 1135 | die("can't embed spare fonts. cache isn't empty"); | ||
| 1136 | |||
| 1137 | /* Calculate count of spare fonts */ | ||
| 1138 | fc = sizeof(font2) / sizeof(*font2); | ||
| 1139 | if (fc == 0) | ||
| 1140 | return; | ||
| 1141 | |||
| 1142 | /* Allocate memory for cache entries. */ | ||
| 1143 | if (frccap < 4 * fc) { | ||
| 1144 | frccap += 4 * fc - frccap; | ||
| 1145 | frc = xrealloc(frc, frccap * sizeof(Fontcache)); | ||
| 1146 | } | ||
| 1147 | |||
| 1148 | for (fp = font2; fp - font2 < fc; ++fp) { | ||
| 1149 | |||
| 1150 | if (**fp == '-') | ||
| 1151 | pattern = XftXlfdParse(*fp, False, False); | ||
| 1152 | else | ||
| 1153 | pattern = FcNameParse((FcChar8 *)*fp); | ||
| 1154 | |||
| 1155 | if (!pattern) | ||
| 1156 | die("can't open spare font %s\n", *fp); | ||
| 1157 | |||
| 1158 | if (defaultfontsize > 0) { | ||
| 1159 | sizeshift = usedfontsize - defaultfontsize; | ||
| 1160 | if (sizeshift != 0 && | ||
| 1161 | FcPatternGetDouble(pattern, FC_PIXEL_SIZE, 0, &fontval) == | ||
| 1162 | FcResultMatch) { | ||
| 1163 | fontval += sizeshift; | ||
| 1164 | FcPatternDel(pattern, FC_PIXEL_SIZE); | ||
| 1165 | FcPatternDel(pattern, FC_SIZE); | ||
| 1166 | FcPatternAddDouble(pattern, FC_PIXEL_SIZE, fontval); | ||
| 1167 | } | ||
| 1168 | } | ||
| 1169 | |||
| 1170 | FcPatternAddBool(pattern, FC_SCALABLE, 1); | ||
| 1171 | |||
| 1172 | FcConfigSubstitute(NULL, pattern, FcMatchPattern); | ||
| 1173 | XftDefaultSubstitute(xw.dpy, xw.scr, pattern); | ||
| 1174 | |||
| 1175 | if (xloadsparefont(pattern, FRC_NORMAL)) | ||
| 1176 | die("can't open spare font %s\n", *fp); | ||
| 1177 | |||
| 1178 | FcPatternDel(pattern, FC_SLANT); | ||
| 1179 | FcPatternAddInteger(pattern, FC_SLANT, FC_SLANT_ITALIC); | ||
| 1180 | if (xloadsparefont(pattern, FRC_ITALIC)) | ||
| 1181 | die("can't open spare font %s\n", *fp); | ||
| 1182 | |||
| 1183 | FcPatternDel(pattern, FC_WEIGHT); | ||
| 1184 | FcPatternAddInteger(pattern, FC_WEIGHT, FC_WEIGHT_BOLD); | ||
| 1185 | if (xloadsparefont(pattern, FRC_ITALICBOLD)) | ||
| 1186 | die("can't open spare font %s\n", *fp); | ||
| 1187 | |||
| 1188 | FcPatternDel(pattern, FC_SLANT); | ||
| 1189 | FcPatternAddInteger(pattern, FC_SLANT, FC_SLANT_ROMAN); | ||
| 1190 | if (xloadsparefont(pattern, FRC_BOLD)) | ||
| 1191 | die("can't open spare font %s\n", *fp); | ||
| 1192 | |||
| 1193 | FcPatternDestroy(pattern); | ||
| 1194 | } | ||
| 1195 | } | ||
| 1196 | |||
| 1197 | void | ||
| 1198 | xunloadfont(Font *f) | ||
| 1199 | { | ||
| 1200 | XftFontClose(xw.dpy, f->match); | ||
| 1201 | FcPatternDestroy(f->pattern); | ||
| 1202 | if (f->set) | ||
| 1203 | FcFontSetDestroy(f->set); | ||
| 1204 | } | ||
| 1205 | |||
| 1206 | void | ||
| 1207 | xunloadfonts(void) | ||
| 1208 | { | ||
| 1209 | /* Clear Harfbuzz font cache. */ | ||
| 1210 | hbunloadfonts(); | ||
| 1211 | |||
| 1212 | /* Free the loaded fonts in the font cache. */ | ||
| 1213 | while (frclen > 0) | ||
| 1214 | XftFontClose(xw.dpy, frc[--frclen].font); | ||
| 1215 | |||
| 1216 | xunloadfont(&dc.font); | ||
| 1217 | xunloadfont(&dc.bfont); | ||
| 1218 | xunloadfont(&dc.ifont); | ||
| 1219 | xunloadfont(&dc.ibfont); | ||
| 1220 | } | ||
| 1221 | |||
| 1222 | int | ||
| 1223 | ximopen(Display *dpy) | ||
| 1224 | { | ||
| 1225 | XIMCallback imdestroy = { .client_data = NULL, .callback = ximdestroy }; | ||
| 1226 | XICCallback icdestroy = { .client_data = NULL, .callback = xicdestroy }; | ||
| 1227 | |||
| 1228 | xw.ime.xim = XOpenIM(xw.dpy, NULL, NULL, NULL); | ||
| 1229 | if (xw.ime.xim == NULL) | ||
| 1230 | return 0; | ||
| 1231 | |||
| 1232 | if (XSetIMValues(xw.ime.xim, XNDestroyCallback, &imdestroy, NULL)) | ||
| 1233 | fprintf(stderr, "XSetIMValues: " | ||
| 1234 | "Could not set XNDestroyCallback.\n"); | ||
| 1235 | |||
| 1236 | xw.ime.spotlist = XVaCreateNestedList(0, XNSpotLocation, &xw.ime.spot, | ||
| 1237 | NULL); | ||
| 1238 | |||
| 1239 | if (xw.ime.xic == NULL) { | ||
| 1240 | xw.ime.xic = XCreateIC(xw.ime.xim, XNInputStyle, | ||
| 1241 | XIMPreeditNothing | XIMStatusNothing, | ||
| 1242 | XNClientWindow, xw.win, | ||
| 1243 | XNDestroyCallback, &icdestroy, | ||
| 1244 | NULL); | ||
| 1245 | } | ||
| 1246 | if (xw.ime.xic == NULL) | ||
| 1247 | fprintf(stderr, "XCreateIC: Could not create input context.\n"); | ||
| 1248 | |||
| 1249 | return 1; | ||
| 1250 | } | ||
| 1251 | |||
| 1252 | void | ||
| 1253 | ximinstantiate(Display *dpy, XPointer client, XPointer call) | ||
| 1254 | { | ||
| 1255 | if (ximopen(dpy)) | ||
| 1256 | XUnregisterIMInstantiateCallback(xw.dpy, NULL, NULL, NULL, | ||
| 1257 | ximinstantiate, NULL); | ||
| 1258 | } | ||
| 1259 | |||
| 1260 | void | ||
| 1261 | ximdestroy(XIM xim, XPointer client, XPointer call) | ||
| 1262 | { | ||
| 1263 | xw.ime.xim = NULL; | ||
| 1264 | XRegisterIMInstantiateCallback(xw.dpy, NULL, NULL, NULL, | ||
| 1265 | ximinstantiate, NULL); | ||
| 1266 | XFree(xw.ime.spotlist); | ||
| 1267 | } | ||
| 1268 | |||
| 1269 | int | ||
| 1270 | xicdestroy(XIC xim, XPointer client, XPointer call) | ||
| 1271 | { | ||
| 1272 | xw.ime.xic = NULL; | ||
| 1273 | return 1; | ||
| 1274 | } | ||
| 1275 | |||
| 1276 | void | ||
| 1277 | xinit(int cols, int rows) | ||
| 1278 | { | ||
| 1279 | XGCValues gcvalues; | ||
| 1280 | Cursor cursor; | ||
| 1281 | Window parent; | ||
| 1282 | pid_t thispid = getpid(); | ||
| 1283 | XColor xmousefg, xmousebg; | ||
| 1284 | XWindowAttributes attr; | ||
| 1285 | XVisualInfo vis; | ||
| 1286 | |||
| 1287 | xw.scr = XDefaultScreen(xw.dpy); | ||
| 1288 | |||
| 1289 | if (!(opt_embed && (parent = strtol(opt_embed, NULL, 0)))) { | ||
| 1290 | parent = XRootWindow(xw.dpy, xw.scr); | ||
| 1291 | xw.depth = 32; | ||
| 1292 | } else { | ||
| 1293 | XGetWindowAttributes(xw.dpy, parent, &attr); | ||
| 1294 | xw.depth = attr.depth; | ||
| 1295 | } | ||
| 1296 | |||
| 1297 | XMatchVisualInfo(xw.dpy, xw.scr, xw.depth, TrueColor, &vis); | ||
| 1298 | xw.vis = vis.visual; | ||
| 1299 | |||
| 1300 | /* font */ | ||
| 1301 | if (!FcInit()) | ||
| 1302 | die("could not init fontconfig.\n"); | ||
| 1303 | |||
| 1304 | usedfont = (opt_font == NULL)? font : opt_font; | ||
| 1305 | xloadfonts(usedfont, 0); | ||
| 1306 | |||
| 1307 | /* spare fonts */ | ||
| 1308 | xloadsparefonts(); | ||
| 1309 | |||
| 1310 | /* colors */ | ||
| 1311 | xw.cmap = XCreateColormap(xw.dpy, parent, xw.vis, None); | ||
| 1312 | xloadcols(); | ||
| 1313 | |||
| 1314 | /* adjust fixed window geometry */ | ||
| 1315 | win.w = 2 * borderpx + cols * win.cw; | ||
| 1316 | win.h = 2 * borderpx + rows * win.ch; | ||
| 1317 | if (xw.gm & XNegative) | ||
| 1318 | xw.l += DisplayWidth(xw.dpy, xw.scr) - win.w - 2; | ||
| 1319 | if (xw.gm & YNegative) | ||
| 1320 | xw.t += DisplayHeight(xw.dpy, xw.scr) - win.h - 2; | ||
| 1321 | |||
| 1322 | /* Events */ | ||
| 1323 | xw.attrs.background_pixel = dc.col[defaultbg].pixel; | ||
| 1324 | xw.attrs.border_pixel = dc.col[defaultbg].pixel; | ||
| 1325 | xw.attrs.bit_gravity = NorthWestGravity; | ||
| 1326 | xw.attrs.event_mask = FocusChangeMask | KeyPressMask | KeyReleaseMask | ||
| 1327 | | ExposureMask | VisibilityChangeMask | StructureNotifyMask | ||
| 1328 | | ButtonMotionMask | ButtonPressMask | ButtonReleaseMask; | ||
| 1329 | xw.attrs.colormap = xw.cmap; | ||
| 1330 | |||
| 1331 | xw.win = XCreateWindow(xw.dpy, parent, xw.l, xw.t, | ||
| 1332 | win.w, win.h, 0, xw.depth, InputOutput, | ||
| 1333 | xw.vis, CWBackPixel | CWBorderPixel | CWBitGravity | ||
| 1334 | | CWEventMask | CWColormap, &xw.attrs); | ||
| 1335 | |||
| 1336 | memset(&gcvalues, 0, sizeof(gcvalues)); | ||
| 1337 | gcvalues.graphics_exposures = False; | ||
| 1338 | xw.buf = XCreatePixmap(xw.dpy, xw.win, win.w, win.h, xw.depth); | ||
| 1339 | dc.gc = XCreateGC(xw.dpy, xw.buf, GCGraphicsExposures, &gcvalues); | ||
| 1340 | XSetForeground(xw.dpy, dc.gc, dc.col[defaultbg].pixel); | ||
| 1341 | XFillRectangle(xw.dpy, xw.buf, dc.gc, 0, 0, win.w, win.h); | ||
| 1342 | |||
| 1343 | /* font spec buffer */ | ||
| 1344 | xw.specbuf = xmalloc(cols * sizeof(GlyphFontSpec)); | ||
| 1345 | |||
| 1346 | /* Xft rendering context */ | ||
| 1347 | xw.draw = XftDrawCreate(xw.dpy, xw.buf, xw.vis, xw.cmap); | ||
| 1348 | |||
| 1349 | /* input methods */ | ||
| 1350 | if (!ximopen(xw.dpy)) { | ||
| 1351 | XRegisterIMInstantiateCallback(xw.dpy, NULL, NULL, NULL, | ||
| 1352 | ximinstantiate, NULL); | ||
| 1353 | } | ||
| 1354 | |||
| 1355 | /* white cursor, black outline */ | ||
| 1356 | cursor = XCreateFontCursor(xw.dpy, mouseshape); | ||
| 1357 | XDefineCursor(xw.dpy, xw.win, cursor); | ||
| 1358 | |||
| 1359 | if (XParseColor(xw.dpy, xw.cmap, colorname[mousefg], &xmousefg) == 0) { | ||
| 1360 | xmousefg.red = 0xffff; | ||
| 1361 | xmousefg.green = 0xffff; | ||
| 1362 | xmousefg.blue = 0xffff; | ||
| 1363 | } | ||
| 1364 | |||
| 1365 | if (XParseColor(xw.dpy, xw.cmap, colorname[mousebg], &xmousebg) == 0) { | ||
| 1366 | xmousebg.red = 0x0000; | ||
| 1367 | xmousebg.green = 0x0000; | ||
| 1368 | xmousebg.blue = 0x0000; | ||
| 1369 | } | ||
| 1370 | |||
| 1371 | XRecolorCursor(xw.dpy, cursor, &xmousefg, &xmousebg); | ||
| 1372 | |||
| 1373 | xw.xembed = XInternAtom(xw.dpy, "_XEMBED", False); | ||
| 1374 | xw.wmdeletewin = XInternAtom(xw.dpy, "WM_DELETE_WINDOW", False); | ||
| 1375 | xw.netwmname = XInternAtom(xw.dpy, "_NET_WM_NAME", False); | ||
| 1376 | xw.netwmiconname = XInternAtom(xw.dpy, "_NET_WM_ICON_NAME", False); | ||
| 1377 | XSetWMProtocols(xw.dpy, xw.win, &xw.wmdeletewin, 1); | ||
| 1378 | |||
| 1379 | xw.netwmpid = XInternAtom(xw.dpy, "_NET_WM_PID", False); | ||
| 1380 | XChangeProperty(xw.dpy, xw.win, xw.netwmpid, XA_CARDINAL, 32, | ||
| 1381 | PropModeReplace, (uchar *)&thispid, 1); | ||
| 1382 | |||
| 1383 | win.mode = MODE_NUMLOCK; | ||
| 1384 | resettitle(); | ||
| 1385 | xhints(); | ||
| 1386 | XMapWindow(xw.dpy, xw.win); | ||
| 1387 | XSync(xw.dpy, False); | ||
| 1388 | |||
| 1389 | clock_gettime(CLOCK_MONOTONIC, &xsel.tclick1); | ||
| 1390 | clock_gettime(CLOCK_MONOTONIC, &xsel.tclick2); | ||
| 1391 | xsel.primary = NULL; | ||
| 1392 | xsel.clipboard = NULL; | ||
| 1393 | xsel.xtarget = XInternAtom(xw.dpy, "UTF8_STRING", 0); | ||
| 1394 | if (xsel.xtarget == None) | ||
| 1395 | xsel.xtarget = XA_STRING; | ||
| 1396 | |||
| 1397 | boxdraw_xinit(xw.dpy, xw.cmap, xw.draw, xw.vis); | ||
| 1398 | } | ||
| 1399 | |||
| 1400 | int | ||
| 1401 | xmakeglyphfontspecs(XftGlyphFontSpec *specs, const Glyph *glyphs, int len, int x, int y) | ||
| 1402 | { | ||
| 1403 | float winx = borderpx + x * win.cw, winy = borderpx + y * win.ch, xp, yp; | ||
| 1404 | ushort mode, prevmode = USHRT_MAX; | ||
| 1405 | Font *font = &dc.font; | ||
| 1406 | int frcflags = FRC_NORMAL; | ||
| 1407 | float runewidth = win.cw; | ||
| 1408 | Rune rune; | ||
| 1409 | FT_UInt glyphidx; | ||
| 1410 | FcResult fcres; | ||
| 1411 | FcPattern *fcpattern, *fontpattern; | ||
| 1412 | FcFontSet *fcsets[] = { NULL }; | ||
| 1413 | FcCharSet *fccharset; | ||
| 1414 | int i, f, numspecs = 0; | ||
| 1415 | |||
| 1416 | for (i = 0, xp = winx, yp = winy + font->ascent; i < len; ++i) { | ||
| 1417 | /* Fetch rune and mode for current glyph. */ | ||
| 1418 | rune = glyphs[i].u; | ||
| 1419 | mode = glyphs[i].mode; | ||
| 1420 | |||
| 1421 | /* Skip dummy wide-character spacing. */ | ||
| 1422 | if (mode & ATTR_WDUMMY) | ||
| 1423 | continue; | ||
| 1424 | |||
| 1425 | /* Determine font for glyph if different from previous glyph. */ | ||
| 1426 | if (prevmode != mode) { | ||
| 1427 | prevmode = mode; | ||
| 1428 | font = &dc.font; | ||
| 1429 | frcflags = FRC_NORMAL; | ||
| 1430 | runewidth = win.cw * ((mode & ATTR_WIDE) ? 2.0f : 1.0f); | ||
| 1431 | if ((mode & ATTR_ITALIC) && (mode & ATTR_BOLD)) { | ||
| 1432 | font = &dc.ibfont; | ||
| 1433 | frcflags = FRC_ITALICBOLD; | ||
| 1434 | } else if (mode & ATTR_ITALIC) { | ||
| 1435 | font = &dc.ifont; | ||
| 1436 | frcflags = FRC_ITALIC; | ||
| 1437 | } else if (mode & ATTR_BOLD) { | ||
| 1438 | font = &dc.bfont; | ||
| 1439 | frcflags = FRC_BOLD; | ||
| 1440 | } | ||
| 1441 | yp = winy + font->ascent; | ||
| 1442 | } | ||
| 1443 | |||
| 1444 | if (mode & ATTR_BOXDRAW) { | ||
| 1445 | /* minor shoehorning: boxdraw uses only this ushort */ | ||
| 1446 | glyphidx = boxdrawindex(&glyphs[i]); | ||
| 1447 | } else { | ||
| 1448 | /* Lookup character index with default font. */ | ||
| 1449 | glyphidx = XftCharIndex(xw.dpy, font->match, rune); | ||
| 1450 | } | ||
| 1451 | if (glyphidx) { | ||
| 1452 | specs[numspecs].font = font->match; | ||
| 1453 | specs[numspecs].glyph = glyphidx; | ||
| 1454 | specs[numspecs].x = (short)xp; | ||
| 1455 | specs[numspecs].y = (short)yp; | ||
| 1456 | xp += runewidth; | ||
| 1457 | numspecs++; | ||
| 1458 | continue; | ||
| 1459 | } | ||
| 1460 | |||
| 1461 | /* Fallback on font cache, search the font cache for match. */ | ||
| 1462 | for (f = 0; f < frclen; f++) { | ||
| 1463 | glyphidx = XftCharIndex(xw.dpy, frc[f].font, rune); | ||
| 1464 | /* Everything correct. */ | ||
| 1465 | if (glyphidx && frc[f].flags == frcflags) | ||
| 1466 | break; | ||
| 1467 | /* We got a default font for a not found glyph. */ | ||
| 1468 | if (!glyphidx && frc[f].flags == frcflags | ||
| 1469 | && frc[f].unicodep == rune) { | ||
| 1470 | break; | ||
| 1471 | } | ||
| 1472 | } | ||
| 1473 | |||
| 1474 | /* Nothing was found. Use fontconfig to find matching font. */ | ||
| 1475 | if (f >= frclen) { | ||
| 1476 | if (!font->set) | ||
| 1477 | font->set = FcFontSort(0, font->pattern, | ||
| 1478 | 1, 0, &fcres); | ||
| 1479 | fcsets[0] = font->set; | ||
| 1480 | |||
| 1481 | /* | ||
| 1482 | * Nothing was found in the cache. Now use | ||
| 1483 | * some dozen of Fontconfig calls to get the | ||
| 1484 | * font for one single character. | ||
| 1485 | * | ||
| 1486 | * Xft and fontconfig are design failures. | ||
| 1487 | */ | ||
| 1488 | fcpattern = FcPatternDuplicate(font->pattern); | ||
| 1489 | fccharset = FcCharSetCreate(); | ||
| 1490 | |||
| 1491 | FcCharSetAddChar(fccharset, rune); | ||
| 1492 | FcPatternAddCharSet(fcpattern, FC_CHARSET, | ||
| 1493 | fccharset); | ||
| 1494 | FcPatternAddBool(fcpattern, FC_SCALABLE, 1); | ||
| 1495 | |||
| 1496 | FcConfigSubstitute(0, fcpattern, | ||
| 1497 | FcMatchPattern); | ||
| 1498 | FcDefaultSubstitute(fcpattern); | ||
| 1499 | |||
| 1500 | fontpattern = FcFontSetMatch(0, fcsets, 1, | ||
| 1501 | fcpattern, &fcres); | ||
| 1502 | |||
| 1503 | /* Allocate memory for the new cache entry. */ | ||
| 1504 | if (frclen >= frccap) { | ||
| 1505 | frccap += 16; | ||
| 1506 | frc = xrealloc(frc, frccap * sizeof(Fontcache)); | ||
| 1507 | } | ||
| 1508 | |||
| 1509 | frc[frclen].font = XftFontOpenPattern(xw.dpy, | ||
| 1510 | fontpattern); | ||
| 1511 | if (!frc[frclen].font) | ||
| 1512 | die("XftFontOpenPattern failed seeking fallback font: %s\n", | ||
| 1513 | strerror(errno)); | ||
| 1514 | frc[frclen].flags = frcflags; | ||
| 1515 | frc[frclen].unicodep = rune; | ||
| 1516 | |||
| 1517 | glyphidx = XftCharIndex(xw.dpy, frc[frclen].font, rune); | ||
| 1518 | |||
| 1519 | f = frclen; | ||
| 1520 | frclen++; | ||
| 1521 | |||
| 1522 | FcPatternDestroy(fcpattern); | ||
| 1523 | FcCharSetDestroy(fccharset); | ||
| 1524 | } | ||
| 1525 | |||
| 1526 | specs[numspecs].font = frc[f].font; | ||
| 1527 | specs[numspecs].glyph = glyphidx; | ||
| 1528 | specs[numspecs].x = (short)xp; | ||
| 1529 | specs[numspecs].y = (short)yp; | ||
| 1530 | xp += runewidth; | ||
| 1531 | numspecs++; | ||
| 1532 | } | ||
| 1533 | |||
| 1534 | /* Harfbuzz transformation for ligatures. */ | ||
| 1535 | hbtransform(specs, glyphs, len, x, y); | ||
| 1536 | |||
| 1537 | return numspecs; | ||
| 1538 | } | ||
| 1539 | |||
| 1540 | void | ||
| 1541 | xdrawglyphfontspecs(const XftGlyphFontSpec *specs, Glyph base, int len, int x, int y) | ||
| 1542 | { | ||
| 1543 | int charlen = len * ((base.mode & ATTR_WIDE) ? 2 : 1); | ||
| 1544 | int winx = borderpx + x * win.cw, winy = borderpx + y * win.ch, | ||
| 1545 | width = charlen * win.cw; | ||
| 1546 | Color *fg, *bg, *temp, revfg, revbg, truefg, truebg; | ||
| 1547 | XRenderColor colfg, colbg; | ||
| 1548 | XRectangle r; | ||
| 1549 | |||
| 1550 | /* Fallback on color display for attributes not supported by the font */ | ||
| 1551 | if (base.mode & ATTR_ITALIC && base.mode & ATTR_BOLD) { | ||
| 1552 | if (dc.ibfont.badslant || dc.ibfont.badweight) | ||
| 1553 | base.fg = defaultattr; | ||
| 1554 | } else if ((base.mode & ATTR_ITALIC && dc.ifont.badslant) || | ||
| 1555 | (base.mode & ATTR_BOLD && dc.bfont.badweight)) { | ||
| 1556 | base.fg = defaultattr; | ||
| 1557 | } | ||
| 1558 | |||
| 1559 | if (IS_TRUECOL(base.fg)) { | ||
| 1560 | colfg.alpha = 0xffff; | ||
| 1561 | colfg.red = TRUERED(base.fg); | ||
| 1562 | colfg.green = TRUEGREEN(base.fg); | ||
| 1563 | colfg.blue = TRUEBLUE(base.fg); | ||
| 1564 | XftColorAllocValue(xw.dpy, xw.vis, xw.cmap, &colfg, &truefg); | ||
| 1565 | fg = &truefg; | ||
| 1566 | } else { | ||
| 1567 | fg = &dc.col[base.fg]; | ||
| 1568 | } | ||
| 1569 | |||
| 1570 | if (IS_TRUECOL(base.bg)) { | ||
| 1571 | colbg.alpha = 0xffff; | ||
| 1572 | colbg.green = TRUEGREEN(base.bg); | ||
| 1573 | colbg.red = TRUERED(base.bg); | ||
| 1574 | colbg.blue = TRUEBLUE(base.bg); | ||
| 1575 | XftColorAllocValue(xw.dpy, xw.vis, xw.cmap, &colbg, &truebg); | ||
| 1576 | bg = &truebg; | ||
| 1577 | } else { | ||
| 1578 | bg = &dc.col[base.bg]; | ||
| 1579 | } | ||
| 1580 | |||
| 1581 | /* Change basic system colors [0-7] to bright system colors [8-15] */ | ||
| 1582 | if ((base.mode & ATTR_BOLD_FAINT) == ATTR_BOLD && BETWEEN(base.fg, 0, 7)) | ||
| 1583 | fg = &dc.col[base.fg + 8]; | ||
| 1584 | |||
| 1585 | if (IS_SET(MODE_REVERSE)) { | ||
| 1586 | if (fg == &dc.col[defaultfg]) { | ||
| 1587 | fg = &dc.col[defaultbg]; | ||
| 1588 | } else { | ||
| 1589 | colfg.red = ~fg->color.red; | ||
| 1590 | colfg.green = ~fg->color.green; | ||
| 1591 | colfg.blue = ~fg->color.blue; | ||
| 1592 | colfg.alpha = fg->color.alpha; | ||
| 1593 | XftColorAllocValue(xw.dpy, xw.vis, xw.cmap, &colfg, | ||
| 1594 | &revfg); | ||
| 1595 | fg = &revfg; | ||
| 1596 | } | ||
| 1597 | |||
| 1598 | if (bg == &dc.col[defaultbg]) { | ||
| 1599 | bg = &dc.col[defaultfg]; | ||
| 1600 | } else { | ||
| 1601 | colbg.red = ~bg->color.red; | ||
| 1602 | colbg.green = ~bg->color.green; | ||
| 1603 | colbg.blue = ~bg->color.blue; | ||
| 1604 | colbg.alpha = bg->color.alpha; | ||
| 1605 | XftColorAllocValue(xw.dpy, xw.vis, xw.cmap, &colbg, | ||
| 1606 | &revbg); | ||
| 1607 | bg = &revbg; | ||
| 1608 | } | ||
| 1609 | } | ||
| 1610 | |||
| 1611 | if ((base.mode & ATTR_BOLD_FAINT) == ATTR_FAINT) { | ||
| 1612 | colfg.red = fg->color.red / 2; | ||
| 1613 | colfg.green = fg->color.green / 2; | ||
| 1614 | colfg.blue = fg->color.blue / 2; | ||
| 1615 | colfg.alpha = fg->color.alpha; | ||
| 1616 | XftColorAllocValue(xw.dpy, xw.vis, xw.cmap, &colfg, &revfg); | ||
| 1617 | fg = &revfg; | ||
| 1618 | } | ||
| 1619 | |||
| 1620 | if (base.mode & ATTR_REVERSE) { | ||
| 1621 | temp = fg; | ||
| 1622 | fg = bg; | ||
| 1623 | bg = temp; | ||
| 1624 | } | ||
| 1625 | |||
| 1626 | if (base.mode & ATTR_BLINK && win.mode & MODE_BLINK) | ||
| 1627 | fg = bg; | ||
| 1628 | |||
| 1629 | if (base.mode & ATTR_INVISIBLE) | ||
| 1630 | fg = bg; | ||
| 1631 | |||
| 1632 | /* Intelligent cleaning up of the borders. */ | ||
| 1633 | if (x == 0) { | ||
| 1634 | xclear(0, (y == 0)? 0 : winy, borderpx, | ||
| 1635 | winy + win.ch + | ||
| 1636 | ((winy + win.ch >= borderpx + win.th)? win.h : 0)); | ||
| 1637 | } | ||
| 1638 | if (winx + width >= borderpx + win.tw) { | ||
| 1639 | xclear(winx + width, (y == 0)? 0 : winy, win.w, | ||
| 1640 | ((winy + win.ch >= borderpx + win.th)? win.h : (winy + win.ch))); | ||
| 1641 | } | ||
| 1642 | if (y == 0) | ||
| 1643 | xclear(winx, 0, winx + width, borderpx); | ||
| 1644 | if (winy + win.ch >= borderpx + win.th) | ||
| 1645 | xclear(winx, winy + win.ch, winx + width, win.h); | ||
| 1646 | |||
| 1647 | /* Clean up the region we want to draw to. */ | ||
| 1648 | XftDrawRect(xw.draw, bg, winx, winy, width, win.ch); | ||
| 1649 | |||
| 1650 | /* Set the clip region because Xft is sometimes dirty. */ | ||
| 1651 | r.x = 0; | ||
| 1652 | r.y = 0; | ||
| 1653 | r.height = win.ch; | ||
| 1654 | r.width = width; | ||
| 1655 | XftDrawSetClipRectangles(xw.draw, winx, winy, &r, 1); | ||
| 1656 | |||
| 1657 | if (base.mode & ATTR_BOXDRAW) { | ||
| 1658 | drawboxes(winx, winy, width / len, win.ch, fg, bg, specs, len); | ||
| 1659 | } else { | ||
| 1660 | /* Render the glyphs. */ | ||
| 1661 | XftDrawGlyphFontSpec(xw.draw, fg, specs, len); | ||
| 1662 | } | ||
| 1663 | |||
| 1664 | /* Render underline and strikethrough. */ | ||
| 1665 | if (base.mode & ATTR_UNDERLINE) { | ||
| 1666 | XftDrawRect(xw.draw, fg, winx, winy + dc.font.ascent + 1, | ||
| 1667 | width, 1); | ||
| 1668 | } | ||
| 1669 | |||
| 1670 | if (base.mode & ATTR_STRUCK) { | ||
| 1671 | XftDrawRect(xw.draw, fg, winx, winy + 2 * dc.font.ascent * chscale / 3, | ||
| 1672 | width, 1); | ||
| 1673 | } | ||
| 1674 | |||
| 1675 | /* Reset clip to none. */ | ||
| 1676 | XftDrawSetClip(xw.draw, 0); | ||
| 1677 | } | ||
| 1678 | |||
| 1679 | void | ||
| 1680 | xdrawglyph(Glyph g, int x, int y) | ||
| 1681 | { | ||
| 1682 | int numspecs; | ||
| 1683 | XftGlyphFontSpec spec; | ||
| 1684 | |||
| 1685 | numspecs = xmakeglyphfontspecs(&spec, &g, 1, x, y); | ||
| 1686 | xdrawglyphfontspecs(&spec, g, numspecs, x, y); | ||
| 1687 | } | ||
| 1688 | |||
| 1689 | void | ||
| 1690 | xdrawcursor(int cx, int cy, Glyph g, int ox, int oy, Glyph og, Line line, int len) | ||
| 1691 | { | ||
| 1692 | Color drawcol; | ||
| 1693 | |||
| 1694 | /* remove the old cursor */ | ||
| 1695 | if (selected(ox, oy)) | ||
| 1696 | og.mode ^= ATTR_REVERSE; | ||
| 1697 | |||
| 1698 | /* Redraw the line where cursor was previously. | ||
| 1699 | * It will restore the ligatures broken by the cursor. */ | ||
| 1700 | xdrawline(line, 0, oy, len); | ||
| 1701 | |||
| 1702 | if (IS_SET(MODE_HIDE)) | ||
| 1703 | return; | ||
| 1704 | |||
| 1705 | /* | ||
| 1706 | * Select the right color for the right mode. | ||
| 1707 | */ | ||
| 1708 | g.mode &= ATTR_BOLD|ATTR_ITALIC|ATTR_UNDERLINE|ATTR_STRUCK|ATTR_WIDE|ATTR_BOXDRAW; | ||
| 1709 | |||
| 1710 | if (IS_SET(MODE_REVERSE)) { | ||
| 1711 | g.mode |= ATTR_REVERSE; | ||
| 1712 | g.bg = defaultfg; | ||
| 1713 | if (selected(cx, cy)) { | ||
| 1714 | drawcol = dc.col[defaultcs]; | ||
| 1715 | g.fg = defaultrcs; | ||
| 1716 | } else { | ||
| 1717 | drawcol = dc.col[defaultrcs]; | ||
| 1718 | g.fg = defaultcs; | ||
| 1719 | } | ||
| 1720 | } else { | ||
| 1721 | if (selected(cx, cy)) { | ||
| 1722 | g.fg = defaultfg; | ||
| 1723 | g.bg = defaultrcs; | ||
| 1724 | } else { | ||
| 1725 | g.fg = defaultbg; | ||
| 1726 | g.bg = defaultcs; | ||
| 1727 | } | ||
| 1728 | drawcol = dc.col[g.bg]; | ||
| 1729 | } | ||
| 1730 | |||
| 1731 | /* draw the new one */ | ||
| 1732 | if (IS_SET(MODE_FOCUSED)) { | ||
| 1733 | switch (win.cursor) { | ||
| 1734 | case 7: /* st extension */ | ||
| 1735 | g.u = 0x2603; /* snowman (U+2603) */ | ||
| 1736 | /* FALLTHROUGH */ | ||
| 1737 | case 0: /* Blinking Block */ | ||
| 1738 | case 1: /* Blinking Block (Default) */ | ||
| 1739 | case 2: /* Steady Block */ | ||
| 1740 | xdrawglyph(g, cx, cy); | ||
| 1741 | break; | ||
| 1742 | case 3: /* Blinking Underline */ | ||
| 1743 | case 4: /* Steady Underline */ | ||
| 1744 | XftDrawRect(xw.draw, &drawcol, | ||
| 1745 | borderpx + cx * win.cw, | ||
| 1746 | borderpx + (cy + 1) * win.ch - \ | ||
| 1747 | cursorthickness, | ||
| 1748 | win.cw, cursorthickness); | ||
| 1749 | break; | ||
| 1750 | case 5: /* Blinking bar */ | ||
| 1751 | case 6: /* Steady bar */ | ||
| 1752 | XftDrawRect(xw.draw, &drawcol, | ||
| 1753 | borderpx + cx * win.cw, | ||
| 1754 | borderpx + cy * win.ch, | ||
| 1755 | cursorthickness, win.ch); | ||
| 1756 | break; | ||
| 1757 | } | ||
| 1758 | } else { | ||
| 1759 | XftDrawRect(xw.draw, &drawcol, | ||
| 1760 | borderpx + cx * win.cw, | ||
| 1761 | borderpx + cy * win.ch, | ||
| 1762 | win.cw - 1, 1); | ||
| 1763 | XftDrawRect(xw.draw, &drawcol, | ||
| 1764 | borderpx + cx * win.cw, | ||
| 1765 | borderpx + cy * win.ch, | ||
| 1766 | 1, win.ch - 1); | ||
| 1767 | XftDrawRect(xw.draw, &drawcol, | ||
| 1768 | borderpx + (cx + 1) * win.cw - 1, | ||
| 1769 | borderpx + cy * win.ch, | ||
| 1770 | 1, win.ch - 1); | ||
| 1771 | XftDrawRect(xw.draw, &drawcol, | ||
| 1772 | borderpx + cx * win.cw, | ||
| 1773 | borderpx + (cy + 1) * win.ch - 1, | ||
| 1774 | win.cw, 1); | ||
| 1775 | } | ||
| 1776 | } | ||
| 1777 | |||
| 1778 | void | ||
| 1779 | xsetenv(void) | ||
| 1780 | { | ||
| 1781 | char buf[sizeof(long) * 8 + 1]; | ||
| 1782 | |||
| 1783 | snprintf(buf, sizeof(buf), "%lu", xw.win); | ||
| 1784 | setenv("WINDOWID", buf, 1); | ||
| 1785 | } | ||
| 1786 | |||
| 1787 | void | ||
| 1788 | xseticontitle(char *p) | ||
| 1789 | { | ||
| 1790 | XTextProperty prop; | ||
| 1791 | DEFAULT(p, opt_title); | ||
| 1792 | |||
| 1793 | if (Xutf8TextListToTextProperty(xw.dpy, &p, 1, XUTF8StringStyle, | ||
| 1794 | &prop) != Success) | ||
| 1795 | return; | ||
| 1796 | XSetWMIconName(xw.dpy, xw.win, &prop); | ||
| 1797 | XSetTextProperty(xw.dpy, xw.win, &prop, xw.netwmiconname); | ||
| 1798 | XFree(prop.value); | ||
| 1799 | } | ||
| 1800 | |||
| 1801 | void | ||
| 1802 | xsettitle(char *p) | ||
| 1803 | { | ||
| 1804 | XTextProperty prop; | ||
| 1805 | DEFAULT(p, opt_title); | ||
| 1806 | |||
| 1807 | if (Xutf8TextListToTextProperty(xw.dpy, &p, 1, XUTF8StringStyle, | ||
| 1808 | &prop) != Success) | ||
| 1809 | return; | ||
| 1810 | XSetWMName(xw.dpy, xw.win, &prop); | ||
| 1811 | XSetTextProperty(xw.dpy, xw.win, &prop, xw.netwmname); | ||
| 1812 | XFree(prop.value); | ||
| 1813 | } | ||
| 1814 | |||
| 1815 | int | ||
| 1816 | xstartdraw(void) | ||
| 1817 | { | ||
| 1818 | return IS_SET(MODE_VISIBLE); | ||
| 1819 | } | ||
| 1820 | |||
| 1821 | void | ||
| 1822 | xdrawline(Line line, int x1, int y1, int x2) | ||
| 1823 | { | ||
| 1824 | int i, x, ox, numspecs; | ||
| 1825 | Glyph base, new; | ||
| 1826 | XftGlyphFontSpec *specs = xw.specbuf; | ||
| 1827 | |||
| 1828 | numspecs = xmakeglyphfontspecs(specs, &line[x1], x2 - x1, x1, y1); | ||
| 1829 | i = ox = 0; | ||
| 1830 | for (x = x1; x < x2 && i < numspecs; x++) { | ||
| 1831 | new = line[x]; | ||
| 1832 | if (new.mode == ATTR_WDUMMY) | ||
| 1833 | continue; | ||
| 1834 | if (selected(x, y1)) | ||
| 1835 | new.mode ^= ATTR_REVERSE; | ||
| 1836 | if (i > 0 && ATTRCMP(base, new)) { | ||
| 1837 | xdrawglyphfontspecs(specs, base, i, ox, y1); | ||
| 1838 | specs += i; | ||
| 1839 | numspecs -= i; | ||
| 1840 | i = 0; | ||
| 1841 | } | ||
| 1842 | if (i == 0) { | ||
| 1843 | ox = x; | ||
| 1844 | base = new; | ||
| 1845 | } | ||
| 1846 | i++; | ||
| 1847 | } | ||
| 1848 | if (i > 0) | ||
| 1849 | xdrawglyphfontspecs(specs, base, i, ox, y1); | ||
| 1850 | } | ||
| 1851 | |||
| 1852 | void | ||
| 1853 | xfinishdraw(void) | ||
| 1854 | { | ||
| 1855 | XCopyArea(xw.dpy, xw.buf, xw.win, dc.gc, 0, 0, win.w, | ||
| 1856 | win.h, 0, 0); | ||
| 1857 | XSetForeground(xw.dpy, dc.gc, | ||
| 1858 | dc.col[IS_SET(MODE_REVERSE)? | ||
| 1859 | defaultfg : defaultbg].pixel); | ||
| 1860 | } | ||
| 1861 | |||
| 1862 | void | ||
| 1863 | xximspot(int x, int y) | ||
| 1864 | { | ||
| 1865 | if (xw.ime.xic == NULL) | ||
| 1866 | return; | ||
| 1867 | |||
| 1868 | xw.ime.spot.x = borderpx + x * win.cw; | ||
| 1869 | xw.ime.spot.y = borderpx + (y + 1) * win.ch; | ||
| 1870 | |||
| 1871 | XSetICValues(xw.ime.xic, XNPreeditAttributes, xw.ime.spotlist, NULL); | ||
| 1872 | } | ||
| 1873 | |||
| 1874 | void | ||
| 1875 | expose(XEvent *ev) | ||
| 1876 | { | ||
| 1877 | redraw(); | ||
| 1878 | } | ||
| 1879 | |||
| 1880 | void | ||
| 1881 | visibility(XEvent *ev) | ||
| 1882 | { | ||
| 1883 | XVisibilityEvent *e = &ev->xvisibility; | ||
| 1884 | |||
| 1885 | MODBIT(win.mode, e->state != VisibilityFullyObscured, MODE_VISIBLE); | ||
| 1886 | } | ||
| 1887 | |||
| 1888 | void | ||
| 1889 | unmap(XEvent *ev) | ||
| 1890 | { | ||
| 1891 | win.mode &= ~MODE_VISIBLE; | ||
| 1892 | } | ||
| 1893 | |||
| 1894 | void | ||
| 1895 | xsetpointermotion(int set) | ||
| 1896 | { | ||
| 1897 | MODBIT(xw.attrs.event_mask, set, PointerMotionMask); | ||
| 1898 | XChangeWindowAttributes(xw.dpy, xw.win, CWEventMask, &xw.attrs); | ||
| 1899 | } | ||
| 1900 | |||
| 1901 | void | ||
| 1902 | xsetmode(int set, unsigned int flags) | ||
| 1903 | { | ||
| 1904 | int mode = win.mode; | ||
| 1905 | MODBIT(win.mode, set, flags); | ||
| 1906 | if ((win.mode & MODE_REVERSE) != (mode & MODE_REVERSE)) | ||
| 1907 | redraw(); | ||
| 1908 | } | ||
| 1909 | |||
| 1910 | int | ||
| 1911 | xsetcursor(int cursor) | ||
| 1912 | { | ||
| 1913 | if (!BETWEEN(cursor, 0, 7)) /* 7: st extension */ | ||
| 1914 | return 1; | ||
| 1915 | win.cursor = cursor; | ||
| 1916 | return 0; | ||
| 1917 | } | ||
| 1918 | |||
| 1919 | void | ||
| 1920 | xseturgency(int add) | ||
| 1921 | { | ||
| 1922 | XWMHints *h = XGetWMHints(xw.dpy, xw.win); | ||
| 1923 | |||
| 1924 | MODBIT(h->flags, add, XUrgencyHint); | ||
| 1925 | XSetWMHints(xw.dpy, xw.win, h); | ||
| 1926 | XFree(h); | ||
| 1927 | } | ||
| 1928 | |||
| 1929 | void | ||
| 1930 | xbell(void) | ||
| 1931 | { | ||
| 1932 | if (!(IS_SET(MODE_FOCUSED))) | ||
| 1933 | xseturgency(1); | ||
| 1934 | if (bellvolume) | ||
| 1935 | XkbBell(xw.dpy, xw.win, bellvolume, (Atom)NULL); | ||
| 1936 | } | ||
| 1937 | |||
| 1938 | void | ||
| 1939 | focus(XEvent *ev) | ||
| 1940 | { | ||
| 1941 | XFocusChangeEvent *e = &ev->xfocus; | ||
| 1942 | |||
| 1943 | if (e->mode == NotifyGrab) | ||
| 1944 | return; | ||
| 1945 | |||
| 1946 | if (ev->type == FocusIn) { | ||
| 1947 | if (xw.ime.xic) | ||
| 1948 | XSetICFocus(xw.ime.xic); | ||
| 1949 | win.mode |= MODE_FOCUSED; | ||
| 1950 | xseturgency(0); | ||
| 1951 | if (IS_SET(MODE_FOCUS)) | ||
| 1952 | ttywrite("\033[I", 3, 0); | ||
| 1953 | if (!focused) { | ||
| 1954 | focused = 1; | ||
| 1955 | xloadcols(); | ||
| 1956 | tfulldirt(); | ||
| 1957 | } | ||
| 1958 | } else { | ||
| 1959 | if (xw.ime.xic) | ||
| 1960 | XUnsetICFocus(xw.ime.xic); | ||
| 1961 | win.mode &= ~MODE_FOCUSED; | ||
| 1962 | if (IS_SET(MODE_FOCUS)) | ||
| 1963 | ttywrite("\033[O", 3, 0); | ||
| 1964 | if (focused) { | ||
| 1965 | focused = 0; | ||
| 1966 | xloadcols(); | ||
| 1967 | tfulldirt(); | ||
| 1968 | } | ||
| 1969 | } | ||
| 1970 | } | ||
| 1971 | |||
| 1972 | int | ||
| 1973 | match(uint mask, uint state) | ||
| 1974 | { | ||
| 1975 | return mask == XK_ANY_MOD || mask == (state & ~ignoremod); | ||
| 1976 | } | ||
| 1977 | |||
| 1978 | char* | ||
| 1979 | kmap(KeySym k, uint state) | ||
| 1980 | { | ||
| 1981 | Key *kp; | ||
| 1982 | int i; | ||
| 1983 | |||
| 1984 | /* Check for mapped keys out of X11 function keys. */ | ||
| 1985 | for (i = 0; i < LEN(mappedkeys); i++) { | ||
| 1986 | if (mappedkeys[i] == k) | ||
| 1987 | break; | ||
| 1988 | } | ||
| 1989 | if (i == LEN(mappedkeys)) { | ||
| 1990 | if ((k & 0xFFFF) < 0xFD00) | ||
| 1991 | return NULL; | ||
| 1992 | } | ||
| 1993 | |||
| 1994 | for (kp = key; kp < key + LEN(key); kp++) { | ||
| 1995 | if (kp->k != k) | ||
| 1996 | continue; | ||
| 1997 | |||
| 1998 | if (!match(kp->mask, state)) | ||
| 1999 | continue; | ||
| 2000 | |||
| 2001 | if (IS_SET(MODE_APPKEYPAD) ? kp->appkey < 0 : kp->appkey > 0) | ||
| 2002 | continue; | ||
| 2003 | if (IS_SET(MODE_NUMLOCK) && kp->appkey == 2) | ||
| 2004 | continue; | ||
| 2005 | |||
| 2006 | if (IS_SET(MODE_APPCURSOR) ? kp->appcursor < 0 : kp->appcursor > 0) | ||
| 2007 | continue; | ||
| 2008 | |||
| 2009 | return kp->s; | ||
| 2010 | } | ||
| 2011 | |||
| 2012 | return NULL; | ||
| 2013 | } | ||
| 2014 | |||
| 2015 | void | ||
| 2016 | kpress(XEvent *ev) | ||
| 2017 | { | ||
| 2018 | XKeyEvent *e = &ev->xkey; | ||
| 2019 | KeySym ksym; | ||
| 2020 | char *buf = NULL, *customkey; | ||
| 2021 | int len = 0; | ||
| 2022 | int buf_size = 64; | ||
| 2023 | int critical = - 1; | ||
| 2024 | Rune c; | ||
| 2025 | Status status; | ||
| 2026 | Shortcut *bp; | ||
| 2027 | |||
| 2028 | if (IS_SET(MODE_KBDLOCK)) | ||
| 2029 | return; | ||
| 2030 | |||
| 2031 | reallocbuf: | ||
| 2032 | if (critical > 0) | ||
| 2033 | goto cleanup; | ||
| 2034 | if (buf) | ||
| 2035 | free(buf); | ||
| 2036 | |||
| 2037 | buf = xmalloc((buf_size) * sizeof(char)); | ||
| 2038 | critical += 1; | ||
| 2039 | |||
| 2040 | if (xw.ime.xic) { | ||
| 2041 | len = XmbLookupString(xw.ime.xic, e, buf, buf_size, &ksym, &status); | ||
| 2042 | if (status == XBufferOverflow) { | ||
| 2043 | buf_size = len; | ||
| 2044 | goto reallocbuf; | ||
| 2045 | } | ||
| 2046 | } else { | ||
| 2047 | // Not sure how to fix this and if it is fixable | ||
| 2048 | // but at least it does write something into the buffer | ||
| 2049 | // so it is not as critical | ||
| 2050 | len = XLookupString(e, buf, buf_size, &ksym, NULL); | ||
| 2051 | } | ||
| 2052 | /* 1. shortcuts */ | ||
| 2053 | for (bp = shortcuts; bp < shortcuts + LEN(shortcuts); bp++) { | ||
| 2054 | if (ksym == bp->keysym && match(bp->mod, e->state)) { | ||
| 2055 | bp->func(&(bp->arg)); | ||
| 2056 | goto cleanup; | ||
| 2057 | } | ||
| 2058 | } | ||
| 2059 | |||
| 2060 | /* 2. custom keys from config.h */ | ||
| 2061 | if ((customkey = kmap(ksym, e->state))) { | ||
| 2062 | ttywrite(customkey, strlen(customkey), 1); | ||
| 2063 | goto cleanup; | ||
| 2064 | } | ||
| 2065 | |||
| 2066 | /* 3. composed string from input method */ | ||
| 2067 | if (len == 0) | ||
| 2068 | goto cleanup; | ||
| 2069 | if (len == 1 && e->state & Mod1Mask) { | ||
| 2070 | if (IS_SET(MODE_8BIT)) { | ||
| 2071 | if (*buf < 0177) { | ||
| 2072 | c = *buf | 0x80; | ||
| 2073 | len = utf8encode(c, buf); | ||
| 2074 | } | ||
| 2075 | } else { | ||
| 2076 | buf[1] = buf[0]; | ||
| 2077 | buf[0] = '\033'; | ||
| 2078 | len = 2; | ||
| 2079 | } | ||
| 2080 | } | ||
| 2081 | if (len <= buf_size) | ||
| 2082 | ttywrite(buf, len, 1); | ||
| 2083 | cleanup: | ||
| 2084 | if (buf) | ||
| 2085 | free(buf); | ||
| 2086 | } | ||
| 2087 | |||
| 2088 | void | ||
| 2089 | cmessage(XEvent *e) | ||
| 2090 | { | ||
| 2091 | /* | ||
| 2092 | * See xembed specs | ||
| 2093 | * http://standards.freedesktop.org/xembed-spec/xembed-spec-latest.html | ||
| 2094 | */ | ||
| 2095 | if (e->xclient.message_type == xw.xembed && e->xclient.format == 32) { | ||
| 2096 | if (e->xclient.data.l[1] == XEMBED_FOCUS_IN) { | ||
| 2097 | win.mode |= MODE_FOCUSED; | ||
| 2098 | xseturgency(0); | ||
| 2099 | } else if (e->xclient.data.l[1] == XEMBED_FOCUS_OUT) { | ||
| 2100 | win.mode &= ~MODE_FOCUSED; | ||
| 2101 | } | ||
| 2102 | } else if (e->xclient.data.l[0] == xw.wmdeletewin) { | ||
| 2103 | ttyhangup(); | ||
| 2104 | exit(0); | ||
| 2105 | } | ||
| 2106 | } | ||
| 2107 | |||
| 2108 | void | ||
| 2109 | resize(XEvent *e) | ||
| 2110 | { | ||
| 2111 | if (e->xconfigure.width == win.w && e->xconfigure.height == win.h) | ||
| 2112 | return; | ||
| 2113 | |||
| 2114 | cresize(e->xconfigure.width, e->xconfigure.height); | ||
| 2115 | } | ||
| 2116 | |||
| 2117 | void | ||
| 2118 | run(void) | ||
| 2119 | { | ||
| 2120 | XEvent ev; | ||
| 2121 | int w = win.w, h = win.h; | ||
| 2122 | fd_set rfd; | ||
| 2123 | int xfd = XConnectionNumber(xw.dpy), ttyfd, xev, drawing; | ||
| 2124 | struct timespec seltv, *tv, now, lastblink, trigger; | ||
| 2125 | double timeout; | ||
| 2126 | |||
| 2127 | /* Waiting for window mapping */ | ||
| 2128 | do { | ||
| 2129 | XNextEvent(xw.dpy, &ev); | ||
| 2130 | /* | ||
| 2131 | * This XFilterEvent call is required because of XOpenIM. It | ||
| 2132 | * does filter out the key event and some client message for | ||
| 2133 | * the input method too. | ||
| 2134 | */ | ||
| 2135 | if (XFilterEvent(&ev, None)) | ||
| 2136 | continue; | ||
| 2137 | if (ev.type == ConfigureNotify) { | ||
| 2138 | w = ev.xconfigure.width; | ||
| 2139 | h = ev.xconfigure.height; | ||
| 2140 | } | ||
| 2141 | } while (ev.type != MapNotify); | ||
| 2142 | |||
| 2143 | ttyfd = ttynew(opt_line, shell, opt_io, opt_cmd); | ||
| 2144 | cresize(w, h); | ||
| 2145 | |||
| 2146 | for (timeout = -1, drawing = 0, lastblink = (struct timespec){0};;) { | ||
| 2147 | FD_ZERO(&rfd); | ||
| 2148 | FD_SET(ttyfd, &rfd); | ||
| 2149 | FD_SET(xfd, &rfd); | ||
| 2150 | |||
| 2151 | if (XPending(xw.dpy)) | ||
| 2152 | timeout = 0; /* existing events might not set xfd */ | ||
| 2153 | |||
| 2154 | seltv.tv_sec = timeout / 1E3; | ||
| 2155 | seltv.tv_nsec = 1E6 * (timeout - 1E3 * seltv.tv_sec); | ||
| 2156 | tv = timeout >= 0 ? &seltv : NULL; | ||
| 2157 | |||
| 2158 | if (pselect(MAX(xfd, ttyfd)+1, &rfd, NULL, NULL, tv, NULL) < 0) { | ||
| 2159 | if (errno == EINTR) | ||
| 2160 | continue; | ||
| 2161 | die("select failed: %s\n", strerror(errno)); | ||
| 2162 | } | ||
| 2163 | clock_gettime(CLOCK_MONOTONIC, &now); | ||
| 2164 | |||
| 2165 | if (FD_ISSET(ttyfd, &rfd)) | ||
| 2166 | ttyread(); | ||
| 2167 | |||
| 2168 | xev = 0; | ||
| 2169 | while (XPending(xw.dpy)) { | ||
| 2170 | xev = 1; | ||
| 2171 | XNextEvent(xw.dpy, &ev); | ||
| 2172 | if (XFilterEvent(&ev, None)) | ||
| 2173 | continue; | ||
| 2174 | if (handler[ev.type]) | ||
| 2175 | (handler[ev.type])(&ev); | ||
| 2176 | } | ||
| 2177 | |||
| 2178 | /* | ||
| 2179 | * To reduce flicker and tearing, when new content or event | ||
| 2180 | * triggers drawing, we first wait a bit to ensure we got | ||
| 2181 | * everything, and if nothing new arrives - we draw. | ||
| 2182 | * We start with trying to wait minlatency ms. If more content | ||
| 2183 | * arrives sooner, we retry with shorter and shorter periods, | ||
| 2184 | * and eventually draw even without idle after maxlatency ms. | ||
| 2185 | * Typically this results in low latency while interacting, | ||
| 2186 | * maximum latency intervals during `cat huge.txt`, and perfect | ||
| 2187 | * sync with periodic updates from animations/key-repeats/etc. | ||
| 2188 | */ | ||
| 2189 | if (FD_ISSET(ttyfd, &rfd) || xev) { | ||
| 2190 | if (!drawing) { | ||
| 2191 | trigger = now; | ||
| 2192 | drawing = 1; | ||
| 2193 | } | ||
| 2194 | timeout = (maxlatency - TIMEDIFF(now, trigger)) \ | ||
| 2195 | / maxlatency * minlatency; | ||
| 2196 | if (timeout > 0) | ||
| 2197 | continue; /* we have time, try to find idle */ | ||
| 2198 | } | ||
| 2199 | |||
| 2200 | /* idle detected or maxlatency exhausted -> draw */ | ||
| 2201 | timeout = -1; | ||
| 2202 | if (blinktimeout && tattrset(ATTR_BLINK)) { | ||
| 2203 | timeout = blinktimeout - TIMEDIFF(now, lastblink); | ||
| 2204 | if (timeout <= 0) { | ||
| 2205 | if (-timeout > blinktimeout) /* start visible */ | ||
| 2206 | win.mode |= MODE_BLINK; | ||
| 2207 | win.mode ^= MODE_BLINK; | ||
| 2208 | tsetdirtattr(ATTR_BLINK); | ||
| 2209 | lastblink = now; | ||
| 2210 | timeout = blinktimeout; | ||
| 2211 | } | ||
| 2212 | } | ||
| 2213 | |||
| 2214 | draw(); | ||
| 2215 | XFlush(xw.dpy); | ||
| 2216 | drawing = 0; | ||
| 2217 | } | ||
| 2218 | } | ||
| 2219 | |||
| 2220 | int | ||
| 2221 | resource_load(XrmDatabase db, char *name, enum resource_type rtype, void *dst) | ||
| 2222 | { | ||
| 2223 | char **sdst = dst; | ||
| 2224 | int *idst = dst; | ||
| 2225 | float *fdst = dst; | ||
| 2226 | |||
| 2227 | char fullname[256]; | ||
| 2228 | char fullclass[256]; | ||
| 2229 | char *type; | ||
| 2230 | XrmValue ret; | ||
| 2231 | |||
| 2232 | snprintf(fullname, sizeof(fullname), "%s.%s", | ||
| 2233 | opt_name ? opt_name : "st", name); | ||
| 2234 | snprintf(fullclass, sizeof(fullclass), "%s.%s", | ||
| 2235 | opt_class ? opt_class : "St", name); | ||
| 2236 | fullname[sizeof(fullname) - 1] = fullclass[sizeof(fullclass) - 1] = '\0'; | ||
| 2237 | |||
| 2238 | XrmGetResource(db, fullname, fullclass, &type, &ret); | ||
| 2239 | if (ret.addr == NULL || strncmp("String", type, 64)) | ||
| 2240 | return 1; | ||
| 2241 | |||
| 2242 | switch (rtype) { | ||
| 2243 | case STRING: | ||
| 2244 | *sdst = ret.addr; | ||
| 2245 | break; | ||
| 2246 | case INTEGER: | ||
| 2247 | *idst = strtoul(ret.addr, NULL, 10); | ||
| 2248 | break; | ||
| 2249 | case FLOAT: | ||
| 2250 | *fdst = strtof(ret.addr, NULL); | ||
| 2251 | break; | ||
| 2252 | } | ||
| 2253 | return 0; | ||
| 2254 | } | ||
| 2255 | |||
| 2256 | void | ||
| 2257 | config_init(void) | ||
| 2258 | { | ||
| 2259 | char *resm; | ||
| 2260 | XrmDatabase db; | ||
| 2261 | ResourcePref *p; | ||
| 2262 | |||
| 2263 | XrmInitialize(); | ||
| 2264 | resm = XResourceManagerString(xw.dpy); | ||
| 2265 | if (!resm) | ||
| 2266 | return; | ||
| 2267 | |||
| 2268 | db = XrmGetStringDatabase(resm); | ||
| 2269 | for (p = resources; p < resources + LEN(resources); p++) | ||
| 2270 | resource_load(db, p->name, p->type, p->dst); | ||
| 2271 | } | ||
| 2272 | |||
| 2273 | void | ||
| 2274 | usage(void) | ||
| 2275 | { | ||
| 2276 | die("usage: %s [-aiv] [-c class] [-f font] [-g geometry]" | ||
| 2277 | " [-n name] [-o file]\n" | ||
| 2278 | " [-T title] [-t title] [-w windowid]" | ||
| 2279 | " [[-e] command [args ...]]\n" | ||
| 2280 | " %s [-aiv] [-c class] [-f font] [-g geometry]" | ||
| 2281 | " [-n name] [-o file]\n" | ||
| 2282 | " [-T title] [-t title] [-w windowid] -l line" | ||
| 2283 | " [stty_args ...]\n", argv0, argv0); | ||
| 2284 | } | ||
| 2285 | |||
| 2286 | int | ||
| 2287 | main(int argc, char *argv[]) | ||
| 2288 | { | ||
| 2289 | xw.l = xw.t = 0; | ||
| 2290 | xw.isfixed = False; | ||
| 2291 | xsetcursor(cursorshape); | ||
| 2292 | |||
| 2293 | ARGBEGIN { | ||
| 2294 | case 'a': | ||
| 2295 | allowaltscreen = 0; | ||
| 2296 | break; | ||
| 2297 | case 'A': | ||
| 2298 | opt_alpha = EARGF(usage()); | ||
| 2299 | break; | ||
| 2300 | case 'c': | ||
| 2301 | opt_class = EARGF(usage()); | ||
| 2302 | break; | ||
| 2303 | case 'e': | ||
| 2304 | if (argc > 0) | ||
| 2305 | --argc, ++argv; | ||
| 2306 | goto run; | ||
| 2307 | case 'f': | ||
| 2308 | opt_font = EARGF(usage()); | ||
| 2309 | break; | ||
| 2310 | case 'g': | ||
| 2311 | xw.gm = XParseGeometry(EARGF(usage()), | ||
| 2312 | &xw.l, &xw.t, &cols, &rows); | ||
| 2313 | break; | ||
| 2314 | case 'i': | ||
| 2315 | xw.isfixed = 1; | ||
| 2316 | break; | ||
| 2317 | case 'o': | ||
| 2318 | opt_io = EARGF(usage()); | ||
| 2319 | break; | ||
| 2320 | case 'l': | ||
| 2321 | opt_line = EARGF(usage()); | ||
| 2322 | break; | ||
| 2323 | case 'n': | ||
| 2324 | opt_name = EARGF(usage()); | ||
| 2325 | break; | ||
| 2326 | case 't': | ||
| 2327 | case 'T': | ||
| 2328 | opt_title = EARGF(usage()); | ||
| 2329 | break; | ||
| 2330 | case 'w': | ||
| 2331 | opt_embed = EARGF(usage()); | ||
| 2332 | break; | ||
| 2333 | case 'v': | ||
| 2334 | die("%s " VERSION "\n", argv0); | ||
| 2335 | break; | ||
| 2336 | default: | ||
| 2337 | usage(); | ||
| 2338 | } ARGEND; | ||
| 2339 | |||
| 2340 | run: | ||
| 2341 | if (argc > 0) /* eat all remaining arguments */ | ||
| 2342 | opt_cmd = argv; | ||
| 2343 | |||
| 2344 | if (!opt_title) | ||
| 2345 | opt_title = (opt_line || !opt_cmd) ? "st" : opt_cmd[0]; | ||
| 2346 | |||
| 2347 | setlocale(LC_CTYPE, ""); | ||
| 2348 | XSetLocaleModifiers(""); | ||
| 2349 | |||
| 2350 | if(!(xw.dpy = XOpenDisplay(NULL))) | ||
| 2351 | die("Can't open display\n"); | ||
| 2352 | |||
| 2353 | config_init(); | ||
| 2354 | cols = MAX(cols, 1); | ||
| 2355 | rows = MAX(rows, 1); | ||
| 2356 | defaultbg = MAX(LEN(colorname), 256); | ||
| 2357 | alphaUnfocus = alpha-alphaOffset; | ||
| 2358 | tnew(cols, rows); | ||
| 2359 | xinit(cols, rows); | ||
| 2360 | xsetenv(); | ||
| 2361 | selinit(); | ||
| 2362 | run(); | ||
| 2363 | |||
| 2364 | return 0; | ||
| 2365 | } | ||
| 2366 | |||
