├── FAQ ├── LEGACY ├── LICENSE ├── Makefile ├── README ├── TODO ├── arg.h ├── config.def.h ├── config.mk ├── st.1 ├── st.c └── st.info /FAQ: -------------------------------------------------------------------------------- 1 | ## Why does st not handle utmp entries? 2 | 3 | Use the excellent tool of [utmp](http://git.suckless.org/utmp/) for this task. 4 | 5 | ## Some _random program_ complains that st is unknown/not recognised/unsupported/whatever! 6 | 7 | It means that st doesn’t have any terminfo entry on your system. Chances are 8 | you did not `make install`. If you just want to test it without installing it, 9 | you can manualy run `tic -sx st.info`. 10 | 11 | ## Nothing works, and nothing is said about an unknown terminal! 12 | 13 | * Some programs just assume they’re running in xterm i.e. they don’t rely on 14 | terminfo. What you see is the current state of the “xterm compliance”. 15 | * Some programs don’t complain about the lacking st description and default to 16 | another terminal. In that case see the question about terminfo. 17 | 18 | ## I get some weird glitches/visual bug on _random program_! 19 | 20 | Try launching it with a different TERM: $ TERM=xterm myapp. toe(1) will give 21 | you a list of available terminals, but you’ll most likely switch between xterm, 22 | st or st-256color. The default value for TERM can be changed in config.h 23 | (TNAME). 24 | 25 | ## How do I scroll back up? 26 | 27 | Using a terminal multiplexer. 28 | 29 | * `st -e tmux` using C-b [ 30 | * `st -e screen` using C-a ESC 31 | 32 | ## Why doesn't the Del key work in some programs? 33 | 34 | Taken from the terminfo manpage: 35 | 36 | If the terminal has a keypad that transmits codes when the keys 37 | are pressed, this information can be given. Note that it is not 38 | possible to handle terminals where the keypad only works in 39 | local (this applies, for example, to the unshifted HP 2621 keys). 40 | If the keypad can be set to transmit or not transmit, give these 41 | codes as smkx and rmkx. Otherwise the keypad is assumed to 42 | always transmit. 43 | 44 | In the st case smkx=E[?1hE= and rmkx=E[?1lE>, so it is mandatory that 45 | applications which want to test against keypad keys send these 46 | sequences. 47 | 48 | But buggy applications (like bash and irssi, for example) don't do this. A fast 49 | solution for them is to use the following command: 50 | 51 | $ printf '\033[?1h\033=' >/dev/tty 52 | 53 | or 54 | $ tput smkx 55 | 56 | In the case of bash, readline is used. Readline has a different note in its 57 | manpage about this issue: 58 | 59 | enable-keypad (Off) 60 | When set to On, readline will try to enable the 61 | application keypad when it is called. Some systems 62 | need this to enable arrow keys. 63 | 64 | Adding this option to your .inputrc will fix the keypad problem for all 65 | applications using readline. 66 | 67 | If you are using zsh, then read the zsh FAQ 68 | : 69 | 70 | It should be noted that the O / [ confusion can occur with other keys 71 | such as Home and End. Some systems let you query the key sequences 72 | sent by these keys from the system's terminal database, terminfo. 73 | Unfortunately, the key sequences given there typically apply to the 74 | mode that is not the one zsh uses by default (it's the "application" 75 | mode rather than the "raw" mode). Explaining the use of terminfo is 76 | outside of the scope of this FAQ, but if you wish to use the key 77 | sequences given there you can tell the line editor to turn on 78 | "application" mode when it starts and turn it off when it stops: 79 | 80 | function zle-line-init () { echoti smkx } 81 | function zle-line-finish () { echoti rmkx } 82 | zle -N zle-line-init 83 | zle -N zle-line-finish 84 | 85 | Putting these lines into your .zshrc will fix the problems. 86 | 87 | ## How can I use meta in 8bit mode? 88 | 89 | St supports meta in 8bit mode, but the default terminfo entry doesn't 90 | use this capability. If you want it, you have to use the 'st-meta' value 91 | in TERM. 92 | 93 | ## I cannot compile st in OpenBSD 94 | 95 | OpenBSD lacks librt, despite it being mandatory in POSIX 96 | . 97 | If you want to compile st for OpenBSD you have to remove -lrt from config.mk, and 98 | st will compile without any loss of functionality, because all the functions are 99 | included in libc on this platform. 100 | 101 | ## The Backspace Case 102 | 103 | St is emulating the Linux way of handling backspace being delete and delete being 104 | backspace. 105 | 106 | This is an issue that was discussed in suckless mailing list 107 | . Here is why some old grumpy 108 | terminal users wants its backspace to be how he feels it: 109 | 110 | Well, I am going to comment why I want to change the behaviour 111 | of this key. When ASCII was defined in 1968, communication 112 | with computers was done using punched cards, or hardcopy 113 | terminals (basically a typewriter machine connected with the 114 | computer using a serial port). ASCII defines DELETE as 7F, 115 | because, in punched-card terms, it means all the holes of the 116 | card punched; it is thus a kind of 'physical delete'. In the 117 | same way, the BACKSPACE key was a non-destructive backspace, 118 | as on a typewriter. So, if you wanted to delete a character, 119 | you had to BACKSPACE and then DELETE. Another use of BACKSPACE 120 | was to type accented characters, for example 'a BACKSPACE `'. 121 | The VT100 had no BACKSPACE key; it was generated using the 122 | CONTROL key as another control character (CONTROL key sets to 123 | 0 b7 b6 b5, so it converts H (code 0x48) into BACKSPACE (code 124 | 0x08)), but it had a DELETE key in a similar position where 125 | the BACKSPACE key is located today on common PC keyboards. 126 | All the terminal emulators emulated the difference between 127 | these keys correctly: the backspace key generated a BACKSPACE 128 | (^H) and delete key generated a DELETE (^?). 129 | 130 | But a problem arose when Linus Torvalds wrote Linux. Unlike 131 | earlier terminals, the Linux virtual terminal (the terminal 132 | emulator integrated in the kernel) returned a DELETE when 133 | backspace was pressed, due to the VT100 having a DELETE key in 134 | the same position. This created a lot of problems (see [1] 135 | and [2]). Since Linux has become the king, a lot of terminal 136 | emulators today generate a DELETE when the backspace key is 137 | pressed in order to avoid problems with Linux. The result is 138 | that the only way of generating a BACKSPACE on these systems 139 | is by using CONTROL + H. (I also think that emacs had an 140 | important point here because the CONTROL + H prefix is used 141 | in emacs in some commands (help commands).) 142 | 143 | From point of view of the kernel, you can change the key 144 | for deleting a previous character with stty erase. When you 145 | connect a real terminal into a machine you describe the type 146 | of terminal, so getty configures the correct value of stty 147 | erase for this terminal. In the case of terminal emulators, 148 | however, you don't have any getty that can set the correct 149 | value of stty erase, so you always get the default value. 150 | For this reason, it is necessary to add 'stty erase ^H' to your 151 | profile if you have changed the value of the backspace key. 152 | Of course, another solution is for st itself to modify the 153 | value of stty erase. I usually have the inverse problem: 154 | when I connect to non-Unix machines, I have to press CONTROL + 155 | h to get a BACKSPACE. The inverse problem occurs when a user 156 | connects to my Unix machines from a different system with a 157 | correct backspace key. 158 | 159 | [1] http://www.ibb.net/~anne/keyboard.html 160 | [2] http://www.tldp.org/HOWTO/Keyboard-and-Console-HOWTO-5.html 161 | 162 | ## But I really want the old grumpy behaviour of my terminal 163 | 164 | Apply [1]. 165 | 166 | [1] http://st.suckless.org/patches/delkey 167 | 168 | -------------------------------------------------------------------------------- /LEGACY: -------------------------------------------------------------------------------- 1 | A STATEMENT ON LEGACY SUPPORT 2 | 3 | In the terminal world there is much cruft that comes from old and unsup‐ 4 | ported terminals that inherit incompatible modes and escape sequences 5 | which noone is able to know, except when he/she comes from that time and 6 | developed a graphical vt100 emulator at that time. 7 | 8 | One goal of st is to only support what is really needed. When you en‐ 9 | counter a sequence which you really need, implement it. But while you 10 | are at it, do not add the other cruft you might encounter while sneek‐ 11 | ing at other terminal emulators. History has bloated them and there is 12 | no real evidence that most of the sequences are used today. 13 | 14 | 15 | Christoph Lohmann <20h@r-36.net> 16 | 2012-09-13T07:00:36.081271045+02:00 17 | 18 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT/X Consortium License 2 | 3 | © 2009-2012 Aurélien APTEL 4 | © 2009 Anselm R Garbe 5 | © 2012-2016 Roberto E. Vargas Caballero 6 | © 2012-2016 Christoph Lohmann <20h at r-36 dot net> 7 | © 2013 Eon S. Jeon 8 | © 2013 Alexander Sedov 9 | © 2013 Mark Edgar 10 | © 2013 Eric Pruitt 11 | © 2013 Michael Forney 12 | © 2013-2014 Markus Teich 13 | © 2014-2015 Laslo Hunhold 14 | 15 | Permission is hereby granted, free of charge, to any person obtaining a 16 | copy of this software and associated documentation files (the "Software"), 17 | to deal in the Software without restriction, including without limitation 18 | the rights to use, copy, modify, merge, publish, distribute, sublicense, 19 | and/or sell copies of the Software, and to permit persons to whom the 20 | Software is furnished to do so, subject to the following conditions: 21 | 22 | The above copyright notice and this permission notice shall be included in 23 | all copies or substantial portions of the Software. 24 | 25 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 26 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 27 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL 28 | THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 29 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 30 | FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 31 | DEALINGS IN THE SOFTWARE. 32 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | # st - simple terminal 2 | # See LICENSE file for copyright and license details. 3 | 4 | include config.mk 5 | 6 | SRC = st.c xdg-shell-protocol.c 7 | OBJ = ${SRC:.c=.o} 8 | 9 | all: options st 10 | 11 | options: 12 | @echo st build options: 13 | @echo "CFLAGS = ${CFLAGS}" 14 | @echo "LDFLAGS = ${LDFLAGS}" 15 | @echo "CC = ${CC}" 16 | 17 | config.h: 18 | cp config.def.h config.h 19 | 20 | xdg-shell-protocol.c: 21 | @echo GEN $@ 22 | @wayland-scanner code ${XDG_SHELL_PROTO} $@ 23 | 24 | xdg-shell-client-protocol.h: 25 | @echo GEN $@ 26 | @wayland-scanner client-header ${XDG_SHELL_PROTO} $@ 27 | 28 | st.o: xdg-shell-client-protocol.h 29 | 30 | .c.o: 31 | @echo CC $< 32 | @${CC} -c ${CFLAGS} $< 33 | 34 | ${OBJ}: config.h config.mk 35 | 36 | st: ${OBJ} 37 | @echo CC -o $@ 38 | @${CC} -o $@ ${OBJ} ${LDFLAGS} 39 | 40 | clean: 41 | @echo cleaning 42 | @rm -f st ${OBJ} st-${VERSION}.tar.gz 43 | 44 | dist: clean 45 | @echo creating dist tarball 46 | @mkdir -p st-${VERSION} 47 | @cp -R LICENSE Makefile README config.mk config.def.h st.info st.1 arg.h ${SRC} st-${VERSION} 48 | @tar -cf st-${VERSION}.tar st-${VERSION} 49 | @gzip st-${VERSION}.tar 50 | @rm -rf st-${VERSION} 51 | 52 | install: all 53 | @echo installing executable file to ${DESTDIR}${PREFIX}/bin 54 | @mkdir -p ${DESTDIR}${PREFIX}/bin 55 | @cp -f st ${DESTDIR}${PREFIX}/bin 56 | @chmod 755 ${DESTDIR}${PREFIX}/bin/st 57 | @echo installing manual page to ${DESTDIR}${MANPREFIX}/man1 58 | @mkdir -p ${DESTDIR}${MANPREFIX}/man1 59 | @sed "s/VERSION/${VERSION}/g" < st.1 > ${DESTDIR}${MANPREFIX}/man1/st.1 60 | @chmod 644 ${DESTDIR}${MANPREFIX}/man1/st.1 61 | @echo Please see the README file regarding the terminfo entry of st. 62 | @tic -sx st.info 63 | 64 | uninstall: 65 | @echo removing executable file from ${DESTDIR}${PREFIX}/bin 66 | @rm -f ${DESTDIR}${PREFIX}/bin/st 67 | @echo removing manual page from ${DESTDIR}${MANPREFIX}/man1 68 | @rm -f ${DESTDIR}${MANPREFIX}/man1/st.1 69 | 70 | .PHONY: all options clean dist install uninstall 71 | -------------------------------------------------------------------------------- /README: -------------------------------------------------------------------------------- 1 | st - simple terminal 2 | -------------------- 3 | st is a simple terminal emulator for X which sucks less. 4 | 5 | 6 | Requirements 7 | ------------ 8 | In order to build st you need the Xlib header files. 9 | 10 | 11 | Installation 12 | ------------ 13 | Edit config.mk to match your local setup (st is installed into 14 | the /usr/local namespace by default). 15 | 16 | Afterwards enter the following command to build and install st (if 17 | necessary as root): 18 | 19 | make clean install 20 | 21 | 22 | Running st 23 | ---------- 24 | If you did not install st with make clean install, you must compile 25 | the st terminfo entry with the following command: 26 | 27 | tic -sx st.info 28 | 29 | See the man page for additional details. 30 | 31 | Credits 32 | ------- 33 | Based on Aurélien APTEL bt source code. 34 | 35 | -------------------------------------------------------------------------------- /TODO: -------------------------------------------------------------------------------- 1 | vt emulation 2 | ------------ 3 | 4 | * double-height support 5 | 6 | code & interface 7 | ---------------- 8 | 9 | * add a simple way to do multiplexing 10 | 11 | drawing 12 | ------- 13 | * add diacritics support to xdraws() 14 | * switch to a suckless font drawing library 15 | * make the font cache simpler 16 | * add better support for brightening of the upper colors 17 | 18 | bugs 19 | ---- 20 | 21 | * fix shift up/down (shift selection in emacs) 22 | * remove DEC test sequence when appropriate 23 | 24 | misc 25 | ---- 26 | 27 | $ grep -nE 'XXX|TODO' st.c 28 | 29 | -------------------------------------------------------------------------------- /arg.h: -------------------------------------------------------------------------------- 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 | for (brk_ = 0, argv[0]++, argv_ = argv;\ 25 | argv[0][0] && !brk_;\ 26 | argv[0]++) {\ 27 | if (argv_ != argv)\ 28 | break;\ 29 | argc_ = argv[0][0];\ 30 | switch (argc_) 31 | #define ARGEND }\ 32 | } 33 | 34 | #define ARGC() argc_ 35 | 36 | #define EARGF(x) ((argv[0][1] == '\0' && argv[1] == NULL)?\ 37 | ((x), abort(), (char *)0) :\ 38 | (brk_ = 1, (argv[0][1] != '\0')?\ 39 | (&argv[0][1]) :\ 40 | (argc--, argv++, argv[0]))) 41 | 42 | #define ARGF() ((argv[0][1] == '\0' && argv[1] == NULL)?\ 43 | (char *)0 :\ 44 | (brk_ = 1, (argv[0][1] != '\0')?\ 45 | (&argv[0][1]) :\ 46 | (argc--, argv++, argv[0]))) 47 | 48 | #endif 49 | -------------------------------------------------------------------------------- /config.def.h: -------------------------------------------------------------------------------- 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[] = "Liberation Mono:pixelsize=12:antialias=true:autohint=true"; 9 | static int borderpx = 2; 10 | 11 | /* 12 | * What program is execed by st depends of these precedence rules: 13 | * 1: program passed with -e 14 | * 2: utmp option 15 | * 3: SHELL environment variable 16 | * 4: value of shell in /etc/passwd 17 | * 5: value of shell in config.h 18 | */ 19 | static char shell[] = "/bin/sh"; 20 | static char *utmp = NULL; 21 | static char stty_args[] = "stty raw pass8 nl -echo -iexten -cstopb 38400"; 22 | 23 | /* identification sequence returned in DA and DECID */ 24 | static char vtiden[] = "\033[?6c"; 25 | 26 | /* Kerning / character bounding-box multipliers */ 27 | static float cwscale = 1.0; 28 | static float chscale = 1.0; 29 | 30 | /* 31 | * word delimiter string 32 | * 33 | * More advanced example: " `'\"()[]{}" 34 | */ 35 | static char worddelimiters[] = " "; 36 | 37 | /* selection timeouts (in milliseconds) */ 38 | static unsigned int doubleclicktimeout = 300; 39 | static unsigned int tripleclicktimeout = 600; 40 | 41 | /* key repeat timeouts (in milliseconds) */ 42 | static unsigned int keyrepeatdelay = 500; 43 | static unsigned int keyrepeatinterval = 25; 44 | 45 | /* alt screens */ 46 | static int allowaltscreen = 1; 47 | 48 | /* 49 | * blinking timeout (set to 0 to disable blinking) for the terminal blinking 50 | * attribute. 51 | */ 52 | static unsigned int blinktimeout = 800; 53 | 54 | /* 55 | * thickness of underline and bar cursors 56 | */ 57 | static unsigned int cursorthickness = 2; 58 | 59 | /* 60 | * bell volume. It must be a value between -100 and 100. Use 0 for disabling 61 | * it 62 | */ 63 | /* XXX: Wayland does not have a bell. 64 | * static int bellvolume = 0; 65 | */ 66 | 67 | /* default TERM value */ 68 | static char termname[] = "st-256color"; 69 | 70 | /* 71 | * spaces per tab 72 | * 73 | * When you are changing this value, don't forget to adapt the »it« value in 74 | * the st.info and appropriately install the st.info in the environment where 75 | * you use this st version. 76 | * 77 | * it#$tabspaces, 78 | * 79 | * Secondly make sure your kernel is not expanding tabs. When running `stty 80 | * -a` »tab0« should appear. You can tell the terminal to not expand tabs by 81 | * running following command: 82 | * 83 | * stty tabs 84 | */ 85 | static unsigned int tabspaces = 8; 86 | 87 | /* Terminal colors (16 first used in escape sequence) */ 88 | static const char *colorname[] = { 89 | /* 8 normal colors */ 90 | "black", 91 | "red3", 92 | "green3", 93 | "yellow3", 94 | "blue2", 95 | "magenta3", 96 | "cyan3", 97 | "gray90", 98 | 99 | /* 8 bright colors */ 100 | "gray50", 101 | "red", 102 | "green", 103 | "yellow", 104 | "#5c5cff", 105 | "magenta", 106 | "cyan", 107 | "white", 108 | 109 | [255] = 0, 110 | 111 | /* more colors can be added after 255 to use with DefaultXX */ 112 | "#cccccc", 113 | "#555555", 114 | }; 115 | 116 | 117 | /* 118 | * Default colors (colorname index) 119 | * foreground, background, cursor, reverse cursor 120 | */ 121 | static unsigned int defaultfg = 7; 122 | static unsigned int defaultbg = 0; 123 | static unsigned int defaultcs = 256; 124 | static unsigned int defaultrcs = 257; 125 | 126 | /* 127 | * Default shape of cursor 128 | * 2: Block ("█") 129 | * 4: Underline ("_") 130 | * 6: Bar ("|") 131 | * 7: Snowman ("☃") 132 | */ 133 | static unsigned int cursorshape = 2; 134 | 135 | /* 136 | * Default columns and rows numbers 137 | */ 138 | 139 | static unsigned int cols = 80; 140 | static unsigned int rows = 24; 141 | 142 | /* 143 | * Default shape of the mouse cursor 144 | */ 145 | static char mouseshape[] = "xterm"; 146 | 147 | /* 148 | * Color used to display font attributes when fontconfig selected a font which 149 | * doesn't match the ones requested. 150 | */ 151 | static unsigned int defaultattr = 11; 152 | 153 | /* 154 | * Internal mouse shortcuts. 155 | * Beware that overloading Button1 will disable the selection. 156 | */ 157 | static MouseShortcut mshortcuts[] = { 158 | /* button mask string */ 159 | { -1, MOD_MASK_NONE, "" } 160 | }; 161 | 162 | static Axiskey ashortcuts[] = { 163 | /* axis direction mask string */ 164 | { AXIS_VERTICAL, +1, MOD_MASK_ANY, "\031"}, 165 | { AXIS_VERTICAL, -1, MOD_MASK_ANY, "\005"}, 166 | }; 167 | 168 | /* Internal keyboard shortcuts. */ 169 | #define MODKEY MOD_MASK_ALT 170 | 171 | static Shortcut shortcuts[] = { 172 | /* modifier key function argument */ 173 | { MOD_MASK_ANY, XKB_KEY_Break, sendbreak, {.i = 0} }, 174 | { MOD_MASK_CTRL, XKB_KEY_Print, toggleprinter, {.i = 0} }, 175 | { MOD_MASK_SHIFT, XKB_KEY_Print, printscreen, {.i = 0} }, 176 | { MOD_MASK_ANY, XKB_KEY_Print, printsel, {.i = 0} }, 177 | { MODKEY|MOD_MASK_SHIFT, XKB_KEY_Prior, wlzoom, {.f = +1} }, 178 | { MODKEY|MOD_MASK_SHIFT, XKB_KEY_Next, wlzoom, {.f = -1} }, 179 | { MODKEY|MOD_MASK_SHIFT, XKB_KEY_Home, wlzoomreset, {.f = 0} }, 180 | { MOD_MASK_SHIFT, XKB_KEY_Insert, selpaste, {.i = 0} }, 181 | { MODKEY, XKB_KEY_Num_Lock, numlock, {.i = 0} }, 182 | { MODKEY, XKB_KEY_Control_L, iso14755, {.i = 0} }, 183 | }; 184 | 185 | /* 186 | * Special keys (change & recompile st.info accordingly) 187 | * 188 | * Mask value: 189 | * * Use MOD_MASK_ANY to match the key no matter modifiers state 190 | * * Use MOD_MASK_NONE to match the key alone (no modifiers) 191 | * appkey value: 192 | * * 0: no value 193 | * * > 0: keypad application mode enabled 194 | * * = 2: term.numlock = 1 195 | * * < 0: keypad application mode disabled 196 | * appcursor value: 197 | * * 0: no value 198 | * * > 0: cursor application mode enabled 199 | * * < 0: cursor application mode disabled 200 | * crlf value 201 | * * 0: no value 202 | * * > 0: crlf mode is enabled 203 | * * < 0: crlf mode is disabled 204 | * 205 | * Be careful with the order of the definitions because st searches in 206 | * this table sequentially, so any MOD_MASK_ANY must be in the last 207 | * position for a key. 208 | */ 209 | 210 | /* 211 | * If you want keys other than the X11 function keys (0xFD00 - 0xFFFF) 212 | * to be mapped below, add them to this array. 213 | */ 214 | static xkb_keysym_t mappedkeys[] = { -1 }; 215 | 216 | /* State bits to ignore when matching key or button events. */ 217 | static uint ignoremod = 0; 218 | 219 | /* 220 | * Override mouse-select while mask is active (when MODE_MOUSE is set). 221 | * Note that if you want to use MOD_MASK_SHIFT with selmasks, set this to an 222 | * other modifier, set to 0 to not use it. 223 | */ 224 | static uint forceselmod = MOD_MASK_SHIFT; 225 | 226 | /* 227 | * This is the huge key array which defines all compatibility to the Linux 228 | * world. Please decide about changes wisely. 229 | */ 230 | static Key key[] = { 231 | /* keysym mask string appkey appcursor crlf */ 232 | { XKB_KEY_KP_Home, MOD_MASK_SHIFT, "\033[2J", 0, -1, 0}, 233 | { XKB_KEY_KP_Home, MOD_MASK_SHIFT, "\033[1;2H", 0, +1, 0}, 234 | { XKB_KEY_KP_Home, MOD_MASK_ANY, "\033[H", 0, -1, 0}, 235 | { XKB_KEY_KP_Home, MOD_MASK_ANY, "\033[1~", 0, +1, 0}, 236 | { XKB_KEY_KP_Up, MOD_MASK_ANY, "\033Ox", +1, 0, 0}, 237 | { XKB_KEY_KP_Up, MOD_MASK_ANY, "\033[A", 0, -1, 0}, 238 | { XKB_KEY_KP_Up, MOD_MASK_ANY, "\033OA", 0, +1, 0}, 239 | { XKB_KEY_KP_Down, MOD_MASK_ANY, "\033Or", +1, 0, 0}, 240 | { XKB_KEY_KP_Down, MOD_MASK_ANY, "\033[B", 0, -1, 0}, 241 | { XKB_KEY_KP_Down, MOD_MASK_ANY, "\033OB", 0, +1, 0}, 242 | { XKB_KEY_KP_Left, MOD_MASK_ANY, "\033Ot", +1, 0, 0}, 243 | { XKB_KEY_KP_Left, MOD_MASK_ANY, "\033[D", 0, -1, 0}, 244 | { XKB_KEY_KP_Left, MOD_MASK_ANY, "\033OD", 0, +1, 0}, 245 | { XKB_KEY_KP_Right, MOD_MASK_ANY, "\033Ov", +1, 0, 0}, 246 | { XKB_KEY_KP_Right, MOD_MASK_ANY, "\033[C", 0, -1, 0}, 247 | { XKB_KEY_KP_Right, MOD_MASK_ANY, "\033OC", 0, +1, 0}, 248 | { XKB_KEY_KP_Prior, MOD_MASK_SHIFT, "\033[5;2~", 0, 0, 0}, 249 | { XKB_KEY_KP_Prior, MOD_MASK_ANY, "\033[5~", 0, 0, 0}, 250 | { XKB_KEY_KP_Begin, MOD_MASK_ANY, "\033[E", 0, 0, 0}, 251 | { XKB_KEY_KP_End, MOD_MASK_CTRL, "\033[J", -1, 0, 0}, 252 | { XKB_KEY_KP_End, MOD_MASK_CTRL, "\033[1;5F", +1, 0, 0}, 253 | { XKB_KEY_KP_End, MOD_MASK_SHIFT, "\033[K", -1, 0, 0}, 254 | { XKB_KEY_KP_End, MOD_MASK_SHIFT, "\033[1;2F", +1, 0, 0}, 255 | { XKB_KEY_KP_End, MOD_MASK_ANY, "\033[4~", 0, 0, 0}, 256 | { XKB_KEY_KP_Next, MOD_MASK_SHIFT, "\033[6;2~", 0, 0, 0}, 257 | { XKB_KEY_KP_Next, MOD_MASK_ANY, "\033[6~", 0, 0, 0}, 258 | { XKB_KEY_KP_Insert, MOD_MASK_SHIFT, "\033[2;2~", +1, 0, 0}, 259 | { XKB_KEY_KP_Insert, MOD_MASK_SHIFT, "\033[4l", -1, 0, 0}, 260 | { XKB_KEY_KP_Insert, MOD_MASK_CTRL, "\033[L", -1, 0, 0}, 261 | { XKB_KEY_KP_Insert, MOD_MASK_CTRL, "\033[2;5~", +1, 0, 0}, 262 | { XKB_KEY_KP_Insert, MOD_MASK_ANY, "\033[4h", -1, 0, 0}, 263 | { XKB_KEY_KP_Insert, MOD_MASK_ANY, "\033[2~", +1, 0, 0}, 264 | { XKB_KEY_KP_Delete, MOD_MASK_CTRL, "\033[M", -1, 0, 0}, 265 | { XKB_KEY_KP_Delete, MOD_MASK_CTRL, "\033[3;5~", +1, 0, 0}, 266 | { XKB_KEY_KP_Delete, MOD_MASK_SHIFT, "\033[2K", -1, 0, 0}, 267 | { XKB_KEY_KP_Delete, MOD_MASK_SHIFT, "\033[3;2~", +1, 0, 0}, 268 | { XKB_KEY_KP_Delete, MOD_MASK_ANY, "\033[P", -1, 0, 0}, 269 | { XKB_KEY_KP_Delete, MOD_MASK_ANY, "\033[3~", +1, 0, 0}, 270 | { XKB_KEY_KP_Multiply, MOD_MASK_ANY, "\033Oj", +2, 0, 0}, 271 | { XKB_KEY_KP_Add, MOD_MASK_ANY, "\033Ok", +2, 0, 0}, 272 | { XKB_KEY_KP_Enter, MOD_MASK_ANY, "\033OM", +2, 0, 0}, 273 | { XKB_KEY_KP_Enter, MOD_MASK_ANY, "\r", -1, 0, -1}, 274 | { XKB_KEY_KP_Enter, MOD_MASK_ANY, "\r\n", -1, 0, +1}, 275 | { XKB_KEY_KP_Subtract, MOD_MASK_ANY, "\033Om", +2, 0, 0}, 276 | { XKB_KEY_KP_Decimal, MOD_MASK_ANY, "\033On", +2, 0, 0}, 277 | { XKB_KEY_KP_Divide, MOD_MASK_ANY, "\033Oo", +2, 0, 0}, 278 | { XKB_KEY_KP_0, MOD_MASK_ANY, "\033Op", +2, 0, 0}, 279 | { XKB_KEY_KP_1, MOD_MASK_ANY, "\033Oq", +2, 0, 0}, 280 | { XKB_KEY_KP_2, MOD_MASK_ANY, "\033Or", +2, 0, 0}, 281 | { XKB_KEY_KP_3, MOD_MASK_ANY, "\033Os", +2, 0, 0}, 282 | { XKB_KEY_KP_4, MOD_MASK_ANY, "\033Ot", +2, 0, 0}, 283 | { XKB_KEY_KP_5, MOD_MASK_ANY, "\033Ou", +2, 0, 0}, 284 | { XKB_KEY_KP_6, MOD_MASK_ANY, "\033Ov", +2, 0, 0}, 285 | { XKB_KEY_KP_7, MOD_MASK_ANY, "\033Ow", +2, 0, 0}, 286 | { XKB_KEY_KP_8, MOD_MASK_ANY, "\033Ox", +2, 0, 0}, 287 | { XKB_KEY_KP_9, MOD_MASK_ANY, "\033Oy", +2, 0, 0}, 288 | { XKB_KEY_Up, MOD_MASK_SHIFT, "\033[1;2A", 0, 0, 0}, 289 | { XKB_KEY_Up, MOD_MASK_ALT, "\033[1;3A", 0, 0, 0}, 290 | { XKB_KEY_Up, MOD_MASK_SHIFT|MOD_MASK_ALT,"\033[1;4A", 0, 0, 0}, 291 | { XKB_KEY_Up, MOD_MASK_CTRL, "\033[1;5A", 0, 0, 0}, 292 | { XKB_KEY_Up,MOD_MASK_SHIFT|MOD_MASK_CTRL,"\033[1;6A", 0, 0, 0}, 293 | { XKB_KEY_Up, MOD_MASK_CTRL|MOD_MASK_ALT,"\033[1;7A", 0, 0, 0}, 294 | { XKB_KEY_Up,MOD_MASK_SHIFT|MOD_MASK_CTRL|MOD_MASK_ALT,"\033[1;8A",0,0,0}, 295 | { XKB_KEY_Up, MOD_MASK_ANY, "\033[A", 0, -1, 0}, 296 | { XKB_KEY_Up, MOD_MASK_ANY, "\033OA", 0, +1, 0}, 297 | { XKB_KEY_Down, MOD_MASK_SHIFT, "\033[1;2B", 0, 0, 0}, 298 | { XKB_KEY_Down, MOD_MASK_ALT, "\033[1;3B", 0, 0, 0}, 299 | { XKB_KEY_Down,MOD_MASK_SHIFT|MOD_MASK_ALT,"\033[1;4B", 0, 0, 0}, 300 | { XKB_KEY_Down, MOD_MASK_CTRL, "\033[1;5B", 0, 0, 0}, 301 | { XKB_KEY_Down,MOD_MASK_SHIFT|MOD_MASK_CTRL,"\033[1;6B", 0, 0, 0}, 302 | { XKB_KEY_Down,MOD_MASK_CTRL|MOD_MASK_ALT,"\033[1;7B", 0, 0, 0}, 303 | { XKB_KEY_Down,MOD_MASK_SHIFT|MOD_MASK_CTRL|MOD_MASK_ALT,"\033[1;8B",0,0,0}, 304 | { XKB_KEY_Down, MOD_MASK_ANY, "\033[B", 0, -1, 0}, 305 | { XKB_KEY_Down, MOD_MASK_ANY, "\033OB", 0, +1, 0}, 306 | { XKB_KEY_Left, MOD_MASK_SHIFT, "\033[1;2D", 0, 0, 0}, 307 | { XKB_KEY_Left, MOD_MASK_ALT, "\033[1;3D", 0, 0, 0}, 308 | { XKB_KEY_Left,MOD_MASK_SHIFT|MOD_MASK_ALT,"\033[1;4D", 0, 0, 0}, 309 | { XKB_KEY_Left, MOD_MASK_CTRL, "\033[1;5D", 0, 0, 0}, 310 | { XKB_KEY_Left,MOD_MASK_SHIFT|MOD_MASK_CTRL,"\033[1;6D", 0, 0, 0}, 311 | { XKB_KEY_Left,MOD_MASK_CTRL|MOD_MASK_ALT,"\033[1;7D", 0, 0, 0}, 312 | { XKB_KEY_Left,MOD_MASK_SHIFT|MOD_MASK_CTRL|MOD_MASK_ALT,"\033[1;8D",0,0,0}, 313 | { XKB_KEY_Left, MOD_MASK_ANY, "\033[D", 0, -1, 0}, 314 | { XKB_KEY_Left, MOD_MASK_ANY, "\033OD", 0, +1, 0}, 315 | { XKB_KEY_Right, MOD_MASK_SHIFT, "\033[1;2C", 0, 0, 0}, 316 | { XKB_KEY_Right, MOD_MASK_ALT, "\033[1;3C", 0, 0, 0}, 317 | { XKB_KEY_Right,MOD_MASK_SHIFT|MOD_MASK_ALT,"\033[1;4C", 0, 0, 0}, 318 | { XKB_KEY_Right, MOD_MASK_CTRL, "\033[1;5C", 0, 0, 0}, 319 | { XKB_KEY_Right,MOD_MASK_SHIFT|MOD_MASK_CTRL,"\033[1;6C", 0, 0, 0}, 320 | { XKB_KEY_Right,MOD_MASK_CTRL|MOD_MASK_ALT,"\033[1;7C", 0, 0, 0}, 321 | { XKB_KEY_Right,MOD_MASK_SHIFT|MOD_MASK_CTRL|MOD_MASK_ALT,"\033[1;8C",0,0,0}, 322 | { XKB_KEY_Right, MOD_MASK_ANY, "\033[C", 0, -1, 0}, 323 | { XKB_KEY_Right, MOD_MASK_ANY, "\033OC", 0, +1, 0}, 324 | { XKB_KEY_ISO_Left_Tab, MOD_MASK_SHIFT, "\033[Z", 0, 0, 0}, 325 | { XKB_KEY_Return, MOD_MASK_ALT, "\033\r", 0, 0, -1}, 326 | { XKB_KEY_Return, MOD_MASK_ALT, "\033\r\n", 0, 0, +1}, 327 | { XKB_KEY_Return, MOD_MASK_ANY, "\r", 0, 0, -1}, 328 | { XKB_KEY_Return, MOD_MASK_ANY, "\r\n", 0, 0, +1}, 329 | { XKB_KEY_Insert, MOD_MASK_SHIFT, "\033[4l", -1, 0, 0}, 330 | { XKB_KEY_Insert, MOD_MASK_SHIFT, "\033[2;2~", +1, 0, 0}, 331 | { XKB_KEY_Insert, MOD_MASK_CTRL, "\033[L", -1, 0, 0}, 332 | { XKB_KEY_Insert, MOD_MASK_CTRL, "\033[2;5~", +1, 0, 0}, 333 | { XKB_KEY_Insert, MOD_MASK_ANY, "\033[4h", -1, 0, 0}, 334 | { XKB_KEY_Insert, MOD_MASK_ANY, "\033[2~", +1, 0, 0}, 335 | { XKB_KEY_Delete, MOD_MASK_CTRL, "\033[M", -1, 0, 0}, 336 | { XKB_KEY_Delete, MOD_MASK_CTRL, "\033[3;5~", +1, 0, 0}, 337 | { XKB_KEY_Delete, MOD_MASK_SHIFT, "\033[2K", -1, 0, 0}, 338 | { XKB_KEY_Delete, MOD_MASK_SHIFT, "\033[3;2~", +1, 0, 0}, 339 | { XKB_KEY_Delete, MOD_MASK_ANY, "\033[P", -1, 0, 0}, 340 | { XKB_KEY_Delete, MOD_MASK_ANY, "\033[3~", +1, 0, 0}, 341 | { XKB_KEY_BackSpace, MOD_MASK_NONE, "\177", 0, 0, 0}, 342 | { XKB_KEY_BackSpace, MOD_MASK_CTRL, "\033\177", 0, 0, 0}, 343 | { XKB_KEY_Home, MOD_MASK_SHIFT, "\033[2J", 0, -1, 0}, 344 | { XKB_KEY_Home, MOD_MASK_SHIFT, "\033[1;2H", 0, +1, 0}, 345 | { XKB_KEY_Home, MOD_MASK_ANY, "\033[H", 0, -1, 0}, 346 | { XKB_KEY_Home, MOD_MASK_ANY, "\033[1~", 0, +1, 0}, 347 | { XKB_KEY_End, MOD_MASK_CTRL, "\033[J", -1, 0, 0}, 348 | { XKB_KEY_End, MOD_MASK_CTRL, "\033[1;5F", +1, 0, 0}, 349 | { XKB_KEY_End, MOD_MASK_SHIFT, "\033[K", -1, 0, 0}, 350 | { XKB_KEY_End, MOD_MASK_SHIFT, "\033[1;2F", +1, 0, 0}, 351 | { XKB_KEY_End, MOD_MASK_ANY, "\033[4~", 0, 0, 0}, 352 | { XKB_KEY_Prior, MOD_MASK_CTRL, "\033[5;5~", 0, 0, 0}, 353 | { XKB_KEY_Prior, MOD_MASK_SHIFT, "\033[5;2~", 0, 0, 0}, 354 | { XKB_KEY_Prior, MOD_MASK_ANY, "\033[5~", 0, 0, 0}, 355 | { XKB_KEY_Next, MOD_MASK_CTRL, "\033[6;5~", 0, 0, 0}, 356 | { XKB_KEY_Next, MOD_MASK_SHIFT, "\033[6;2~", 0, 0, 0}, 357 | { XKB_KEY_Next, MOD_MASK_ANY, "\033[6~", 0, 0, 0}, 358 | { XKB_KEY_F1, MOD_MASK_NONE, "\033OP" , 0, 0, 0}, 359 | { XKB_KEY_F1, /* F13 */ MOD_MASK_SHIFT, "\033[1;2P", 0, 0, 0}, 360 | { XKB_KEY_F1, /* F25 */ MOD_MASK_CTRL, "\033[1;5P", 0, 0, 0}, 361 | { XKB_KEY_F1, /* F37 */ MOD_MASK_LOGO, "\033[1;6P", 0, 0, 0}, 362 | { XKB_KEY_F1, /* F49 */ MOD_MASK_ALT, "\033[1;3P", 0, 0, 0}, 363 | { XKB_KEY_F2, MOD_MASK_NONE, "\033OQ" , 0, 0, 0}, 364 | { XKB_KEY_F2, /* F14 */ MOD_MASK_SHIFT, "\033[1;2Q", 0, 0, 0}, 365 | { XKB_KEY_F2, /* F26 */ MOD_MASK_CTRL, "\033[1;5Q", 0, 0, 0}, 366 | { XKB_KEY_F2, /* F38 */ MOD_MASK_LOGO, "\033[1;6Q", 0, 0, 0}, 367 | { XKB_KEY_F2, /* F50 */ MOD_MASK_ALT, "\033[1;3Q", 0, 0, 0}, 368 | { XKB_KEY_F3, MOD_MASK_NONE, "\033OR" , 0, 0, 0}, 369 | { XKB_KEY_F3, /* F15 */ MOD_MASK_SHIFT, "\033[1;2R", 0, 0, 0}, 370 | { XKB_KEY_F3, /* F27 */ MOD_MASK_CTRL, "\033[1;5R", 0, 0, 0}, 371 | { XKB_KEY_F3, /* F39 */ MOD_MASK_LOGO, "\033[1;6R", 0, 0, 0}, 372 | { XKB_KEY_F3, /* F51 */ MOD_MASK_ALT, "\033[1;3R", 0, 0, 0}, 373 | { XKB_KEY_F4, MOD_MASK_NONE, "\033OS" , 0, 0, 0}, 374 | { XKB_KEY_F4, /* F16 */ MOD_MASK_SHIFT, "\033[1;2S", 0, 0, 0}, 375 | { XKB_KEY_F4, /* F28 */ MOD_MASK_CTRL, "\033[1;5S", 0, 0, 0}, 376 | { XKB_KEY_F4, /* F40 */ MOD_MASK_LOGO, "\033[1;6S", 0, 0, 0}, 377 | { XKB_KEY_F4, /* F52 */ MOD_MASK_ALT, "\033[1;3S", 0, 0, 0}, 378 | { XKB_KEY_F5, MOD_MASK_NONE, "\033[15~", 0, 0, 0}, 379 | { XKB_KEY_F5, /* F17 */ MOD_MASK_SHIFT, "\033[15;2~", 0, 0, 0}, 380 | { XKB_KEY_F5, /* F29 */ MOD_MASK_CTRL, "\033[15;5~", 0, 0, 0}, 381 | { XKB_KEY_F5, /* F41 */ MOD_MASK_LOGO, "\033[15;6~", 0, 0, 0}, 382 | { XKB_KEY_F5, /* F53 */ MOD_MASK_ALT, "\033[15;3~", 0, 0, 0}, 383 | { XKB_KEY_F6, MOD_MASK_NONE, "\033[17~", 0, 0, 0}, 384 | { XKB_KEY_F6, /* F18 */ MOD_MASK_SHIFT, "\033[17;2~", 0, 0, 0}, 385 | { XKB_KEY_F6, /* F30 */ MOD_MASK_CTRL, "\033[17;5~", 0, 0, 0}, 386 | { XKB_KEY_F6, /* F42 */ MOD_MASK_LOGO, "\033[17;6~", 0, 0, 0}, 387 | { XKB_KEY_F6, /* F54 */ MOD_MASK_ALT, "\033[17;3~", 0, 0, 0}, 388 | { XKB_KEY_F7, MOD_MASK_NONE, "\033[18~", 0, 0, 0}, 389 | { XKB_KEY_F7, /* F19 */ MOD_MASK_SHIFT, "\033[18;2~", 0, 0, 0}, 390 | { XKB_KEY_F7, /* F31 */ MOD_MASK_CTRL, "\033[18;5~", 0, 0, 0}, 391 | { XKB_KEY_F7, /* F43 */ MOD_MASK_LOGO, "\033[18;6~", 0, 0, 0}, 392 | { XKB_KEY_F7, /* F55 */ MOD_MASK_ALT, "\033[18;3~", 0, 0, 0}, 393 | { XKB_KEY_F8, MOD_MASK_NONE, "\033[19~", 0, 0, 0}, 394 | { XKB_KEY_F8, /* F20 */ MOD_MASK_SHIFT, "\033[19;2~", 0, 0, 0}, 395 | { XKB_KEY_F8, /* F32 */ MOD_MASK_CTRL, "\033[19;5~", 0, 0, 0}, 396 | { XKB_KEY_F8, /* F44 */ MOD_MASK_LOGO, "\033[19;6~", 0, 0, 0}, 397 | { XKB_KEY_F8, /* F56 */ MOD_MASK_ALT, "\033[19;3~", 0, 0, 0}, 398 | { XKB_KEY_F9, MOD_MASK_NONE, "\033[20~", 0, 0, 0}, 399 | { XKB_KEY_F9, /* F21 */ MOD_MASK_SHIFT, "\033[20;2~", 0, 0, 0}, 400 | { XKB_KEY_F9, /* F33 */ MOD_MASK_CTRL, "\033[20;5~", 0, 0, 0}, 401 | { XKB_KEY_F9, /* F45 */ MOD_MASK_LOGO, "\033[20;6~", 0, 0, 0}, 402 | { XKB_KEY_F9, /* F57 */ MOD_MASK_ALT, "\033[20;3~", 0, 0, 0}, 403 | { XKB_KEY_F10, MOD_MASK_NONE, "\033[21~", 0, 0, 0}, 404 | { XKB_KEY_F10, /* F22 */ MOD_MASK_SHIFT, "\033[21;2~", 0, 0, 0}, 405 | { XKB_KEY_F10, /* F34 */ MOD_MASK_CTRL, "\033[21;5~", 0, 0, 0}, 406 | { XKB_KEY_F10, /* F46 */ MOD_MASK_LOGO, "\033[21;6~", 0, 0, 0}, 407 | { XKB_KEY_F10, /* F58 */ MOD_MASK_ALT, "\033[21;3~", 0, 0, 0}, 408 | { XKB_KEY_F11, MOD_MASK_NONE, "\033[23~", 0, 0, 0}, 409 | { XKB_KEY_F11, /* F23 */ MOD_MASK_SHIFT, "\033[23;2~", 0, 0, 0}, 410 | { XKB_KEY_F11, /* F35 */ MOD_MASK_CTRL, "\033[23;5~", 0, 0, 0}, 411 | { XKB_KEY_F11, /* F47 */ MOD_MASK_LOGO, "\033[23;6~", 0, 0, 0}, 412 | { XKB_KEY_F11, /* F59 */ MOD_MASK_ALT, "\033[23;3~", 0, 0, 0}, 413 | { XKB_KEY_F12, MOD_MASK_NONE, "\033[24~", 0, 0, 0}, 414 | { XKB_KEY_F12, /* F24 */ MOD_MASK_SHIFT, "\033[24;2~", 0, 0, 0}, 415 | { XKB_KEY_F12, /* F36 */ MOD_MASK_CTRL, "\033[24;5~", 0, 0, 0}, 416 | { XKB_KEY_F12, /* F48 */ MOD_MASK_LOGO, "\033[24;6~", 0, 0, 0}, 417 | { XKB_KEY_F12, /* F60 */ MOD_MASK_ALT, "\033[24;3~", 0, 0, 0}, 418 | { XKB_KEY_F13, MOD_MASK_NONE, "\033[1;2P", 0, 0, 0}, 419 | { XKB_KEY_F14, MOD_MASK_NONE, "\033[1;2Q", 0, 0, 0}, 420 | { XKB_KEY_F15, MOD_MASK_NONE, "\033[1;2R", 0, 0, 0}, 421 | { XKB_KEY_F16, MOD_MASK_NONE, "\033[1;2S", 0, 0, 0}, 422 | { XKB_KEY_F17, MOD_MASK_NONE, "\033[15;2~", 0, 0, 0}, 423 | { XKB_KEY_F18, MOD_MASK_NONE, "\033[17;2~", 0, 0, 0}, 424 | { XKB_KEY_F19, MOD_MASK_NONE, "\033[18;2~", 0, 0, 0}, 425 | { XKB_KEY_F20, MOD_MASK_NONE, "\033[19;2~", 0, 0, 0}, 426 | { XKB_KEY_F21, MOD_MASK_NONE, "\033[20;2~", 0, 0, 0}, 427 | { XKB_KEY_F22, MOD_MASK_NONE, "\033[21;2~", 0, 0, 0}, 428 | { XKB_KEY_F23, MOD_MASK_NONE, "\033[23;2~", 0, 0, 0}, 429 | { XKB_KEY_F24, MOD_MASK_NONE, "\033[24;2~", 0, 0, 0}, 430 | { XKB_KEY_F25, MOD_MASK_NONE, "\033[1;5P", 0, 0, 0}, 431 | { XKB_KEY_F26, MOD_MASK_NONE, "\033[1;5Q", 0, 0, 0}, 432 | { XKB_KEY_F27, MOD_MASK_NONE, "\033[1;5R", 0, 0, 0}, 433 | { XKB_KEY_F28, MOD_MASK_NONE, "\033[1;5S", 0, 0, 0}, 434 | { XKB_KEY_F29, MOD_MASK_NONE, "\033[15;5~", 0, 0, 0}, 435 | { XKB_KEY_F30, MOD_MASK_NONE, "\033[17;5~", 0, 0, 0}, 436 | { XKB_KEY_F31, MOD_MASK_NONE, "\033[18;5~", 0, 0, 0}, 437 | { XKB_KEY_F32, MOD_MASK_NONE, "\033[19;5~", 0, 0, 0}, 438 | { XKB_KEY_F33, MOD_MASK_NONE, "\033[20;5~", 0, 0, 0}, 439 | { XKB_KEY_F34, MOD_MASK_NONE, "\033[21;5~", 0, 0, 0}, 440 | { XKB_KEY_F35, MOD_MASK_NONE, "\033[23;5~", 0, 0, 0}, 441 | }; 442 | 443 | /* 444 | * Selection types' masks. 445 | * Use the same masks as usual. 446 | * Button1Mask is always unset, to make masks match between ButtonPress. 447 | * ButtonRelease and MotionNotify. 448 | * If no match is found, regular selection is used. 449 | */ 450 | static uint selmasks[] = { 451 | [SEL_RECTANGULAR] = MOD_MASK_ALT, 452 | }; 453 | 454 | /* 455 | * Printable characters in ASCII, used to estimate the advance width 456 | * of single wide characters. 457 | */ 458 | static char ascii_printable[] = 459 | " !\"#$%&'()*+,-./0123456789:;<=>?" 460 | "@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_" 461 | "`abcdefghijklmnopqrstuvwxyz{|}~"; 462 | 463 | -------------------------------------------------------------------------------- /config.mk: -------------------------------------------------------------------------------- 1 | # st version 2 | VERSION = 0.7 3 | 4 | # Customize below to fit your system 5 | 6 | # paths 7 | PREFIX = /usr/local 8 | MANPREFIX = ${PREFIX}/share/man 9 | 10 | PKGCFG = wayland-client wayland-cursor xkbcommon wld 11 | XDG_SHELL_PROTO = `pkg-config --variable=pkgdatadir wayland-protocols`/stable/xdg-shell/xdg-shell.xml 12 | 13 | # includes and libs 14 | INCS = -I. -I/usr/include `pkg-config --cflags ${PKGCFG}` 15 | LIBS = -L/usr/lib -lc -lm -lrt -lutil `pkg-config --libs ${PKGCFG}` 16 | 17 | # flags 18 | CPPFLAGS = -DVERSION=\"${VERSION}\" -D_XOPEN_SOURCE=600 19 | CFLAGS += -g -std=c99 -pedantic -Wall -Wvariadic-macros -Os ${INCS} ${CPPFLAGS} 20 | LDFLAGS += -g ${LIBS} 21 | 22 | # compiler and linker 23 | # CC = cc 24 | 25 | -------------------------------------------------------------------------------- /st.1: -------------------------------------------------------------------------------- 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 [=][{xX}][{+-}{+-}]. 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 compability, 124 | and all the remaining arguments are used as a command 125 | even without it. 126 | .SH SHORTCUTS 127 | .TP 128 | .B Break 129 | Send a break in the serial line. 130 | Break key is obtained in PC keyboards 131 | pressing at the same time control and pause. 132 | .TP 133 | .B Ctrl-Print Screen 134 | Toggle if st should print to the 135 | .I iofile. 136 | .TP 137 | .B Shift-Print Screen 138 | Print the full screen to the 139 | .I iofile. 140 | .TP 141 | .B Print Screen 142 | Print the selection to the 143 | .I iofile. 144 | .TP 145 | .B Alt-Shift-Page Up 146 | Increase font size. 147 | .TP 148 | .B Alt-Shift-Page Down 149 | Decrease font size. 150 | .TP 151 | .B Alt-Shift-Home 152 | Reset to default font size. 153 | .TP 154 | .B Shift-Insert 155 | Paste from primary selection (middle mouse button). 156 | .TP 157 | .B Alt-Shift-Insert 158 | Paste from clipboard selection. 159 | .TP 160 | .B Alt-Shift-c 161 | Copy the selected text to the clipboard selection. 162 | .TP 163 | .B Alt-Shift-v 164 | Paste from the clipboard selection. 165 | .TP 166 | .B Alt-Ctrl 167 | Launch dmenu to enter a unicode codepoint and send the corresponding glyph 168 | to st. 169 | .SH CUSTOMIZATION 170 | .B st 171 | can be customized by creating a custom config.h and (re)compiling the source 172 | code. This keeps it fast, secure and simple. 173 | .SH AUTHORS 174 | See the LICENSE file for the authors. 175 | .SH LICENSE 176 | See the LICENSE file for the terms of redistribution. 177 | .SH SEE ALSO 178 | .BR tabbed (1), 179 | .BR utmp (1), 180 | .BR stty (1) 181 | .SH BUGS 182 | See the TODO file in the distribution. 183 | 184 | -------------------------------------------------------------------------------- /st.c: -------------------------------------------------------------------------------- 1 | /* See LICENSE for license details. */ 2 | #include 3 | #include 4 | #include 5 | #include 6 | /* for BTN_* definitions */ 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include 15 | #include 16 | #include 17 | #include 18 | #include 19 | #include 20 | #include 21 | #include 22 | #include 23 | #include 24 | #include 25 | #include 26 | #include 27 | #include 28 | #include 29 | #include 30 | #include 31 | #include 32 | #include 33 | #include 34 | 35 | #include "arg.h" 36 | #include "xdg-shell-client-protocol.h" 37 | 38 | char *argv0; 39 | 40 | #if defined(__linux) 41 | #include 42 | #elif defined(__OpenBSD__) || defined(__NetBSD__) || defined(__APPLE__) 43 | #include 44 | #elif defined(__FreeBSD__) || defined(__DragonFly__) 45 | #include 46 | #endif 47 | 48 | 49 | /* Arbitrary sizes */ 50 | #define UTF_INVALID 0xFFFD 51 | #define UTF_SIZ 4 52 | #define ESC_BUF_SIZ (128*UTF_SIZ) 53 | #define ESC_ARG_SIZ 16 54 | #define STR_BUF_SIZ ESC_BUF_SIZ 55 | #define STR_ARG_SIZ ESC_ARG_SIZ 56 | #define DRAW_BUF_SIZ 20*1024 57 | #define XK_ANY_MOD UINT_MAX 58 | #define XK_NO_MOD 0 59 | #define XK_SWITCH_MOD (1<<13) 60 | 61 | #define MOD_MASK_ANY UINT_MAX 62 | #define MOD_MASK_NONE 0 63 | #define MOD_MASK_CTRL (1<<0) 64 | #define MOD_MASK_ALT (1<<1) 65 | #define MOD_MASK_SHIFT (1<<2) 66 | #define MOD_MASK_LOGO (1<<3) 67 | 68 | #define AXIS_VERTICAL WL_POINTER_AXIS_VERTICAL_SCROLL 69 | #define AXIS_HORIZONTAL WL_POINTER_AXIS_HORIZONTAL_SCROLL 70 | 71 | /* macros */ 72 | #define MIN(a, b) ((a) < (b) ? (a) : (b)) 73 | #define MAX(a, b) ((a) < (b) ? (b) : (a)) 74 | #define LEN(a) (sizeof(a) / sizeof(a)[0]) 75 | #define DEFAULT(a, b) (a) = (a) ? (a) : (b) 76 | #define BETWEEN(x, a, b) ((a) <= (x) && (x) <= (b)) 77 | #define DIVCEIL(n, d) (((n) + ((d) - 1)) / (d)) 78 | #define ISCONTROLC0(c) (BETWEEN(c, 0, 0x1f) || (c) == '\177') 79 | #define ISCONTROLC1(c) (BETWEEN(c, 0x80, 0x9f)) 80 | #define ISCONTROL(c) (ISCONTROLC0(c) || ISCONTROLC1(c)) 81 | #define ISDELIM(u) (utf8strchr(worddelimiters, u) != NULL) 82 | #define LIMIT(x, a, b) (x) = (x) < (a) ? (a) : (x) > (b) ? (b) : (x) 83 | #define ATTRCMP(a, b) ((a).mode != (b).mode || (a).fg != (b).fg || \ 84 | (a).bg != (b).bg) 85 | #define IS_SET(flag) ((term.mode & (flag)) != 0) 86 | #define TIMEDIFF(t1, t2) ((t1.tv_sec-t2.tv_sec)*1000 + \ 87 | (t1.tv_nsec-t2.tv_nsec)/1E6) 88 | #define MODBIT(x, set, bit) ((set) ? ((x) |= (bit)) : ((x) &= ~(bit))) 89 | 90 | #define TRUECOLOR(r,g,b) (1 << 24 | (r) << 16 | (g) << 8 | (b)) 91 | #define IS_TRUECOL(x) (1 << 24 & (x)) 92 | #define TRUERED(x) (((x) & 0xff0000) >> 8) 93 | #define TRUEGREEN(x) (((x) & 0xff00)) 94 | #define TRUEBLUE(x) (((x) & 0xff) << 8) 95 | 96 | /* constants */ 97 | #define ISO14755CMD "dmenu -p codepoint: ] [;]] []] */ 221 | typedef struct { 222 | char buf[ESC_BUF_SIZ]; /* raw string */ 223 | int len; /* raw string length */ 224 | char priv; 225 | int arg[ESC_ARG_SIZ]; 226 | int narg; /* nb of args */ 227 | char mode[2]; 228 | } CSIEscape; 229 | 230 | /* STR Escape sequence structs */ 231 | /* ESC type [[ [] [;]] ] ESC '\' */ 232 | typedef struct { 233 | char type; /* ESC type ... */ 234 | char buf[STR_BUF_SIZ]; /* raw string */ 235 | int len; /* raw string length */ 236 | char *args[STR_ARG_SIZ]; 237 | int narg; /* nb of args */ 238 | } STREscape; 239 | 240 | /* Internal representation of the screen */ 241 | typedef struct { 242 | int row; /* nb row */ 243 | int col; /* nb col */ 244 | Line *line; /* screen */ 245 | Line *alt; /* alternate screen */ 246 | int *dirty; /* dirtyness of lines */ 247 | TCursor c; /* cursor */ 248 | int top; /* top scroll limit */ 249 | int bot; /* bottom scroll limit */ 250 | int mode; /* terminal mode flags */ 251 | int esc; /* escape state flags */ 252 | char trantbl[4]; /* charset table translation */ 253 | int charset; /* current charset */ 254 | int icharset; /* selected charset for sequence */ 255 | int numlock; /* lock numbers in keyboard */ 256 | int *tabs; 257 | } Term; 258 | 259 | typedef struct { 260 | struct xkb_context *ctx; 261 | struct xkb_keymap *keymap; 262 | struct xkb_state *state; 263 | xkb_mod_index_t ctrl, alt, shift, logo; 264 | unsigned int mods; 265 | } XKB; 266 | 267 | typedef struct { 268 | struct wl_display *dpy; 269 | struct wl_compositor *cmp; 270 | struct wl_shm *shm; 271 | struct wl_seat *seat; 272 | struct wl_keyboard *keyboard; 273 | struct wl_pointer *pointer; 274 | struct wl_data_device_manager *datadevmanager; 275 | struct wl_data_device *datadev; 276 | struct wl_data_offer *seloffer; 277 | struct wl_surface *surface; 278 | struct wl_buffer *buffer; 279 | struct xdg_wm_base *wm; 280 | struct xdg_surface *xdgsurface; 281 | struct xdg_toplevel *toplevel; 282 | XKB xkb; 283 | bool configured; 284 | int px, py; /* pointer x and y */ 285 | int tw, th; /* tty width and height */ 286 | int w, h; /* window width and height */ 287 | int ch; /* char height */ 288 | int cw; /* char width */ 289 | int vis; 290 | char state; /* focus, redraw, visible */ 291 | int cursor; /* cursor style */ 292 | struct wl_callback * framecb; 293 | } Wayland; 294 | 295 | typedef struct { 296 | struct wld_context *ctx; 297 | struct wld_font_context *fontctx; 298 | struct wld_renderer *renderer; 299 | struct wld_buffer *buffer, *oldbuffer; 300 | } WLD; 301 | 302 | typedef struct { 303 | struct wl_cursor_theme *theme; 304 | struct wl_cursor *cursor; 305 | struct wl_surface *surface; 306 | } Cursor; 307 | 308 | typedef struct { 309 | uint b; 310 | uint mask; 311 | char *s; 312 | } MouseShortcut; 313 | 314 | typedef struct { 315 | int axis; 316 | int dir; 317 | uint mask; 318 | char s[ESC_BUF_SIZ]; 319 | } Axiskey; 320 | 321 | typedef struct { 322 | xkb_keysym_t k; 323 | uint mask; 324 | char *s; 325 | /* three valued logic variables: 0 indifferent, 1 on, -1 off */ 326 | signed char appkey; /* application keypad */ 327 | signed char appcursor; /* application cursor */ 328 | signed char crlf; /* crlf mode */ 329 | } Key; 330 | 331 | typedef struct { 332 | int mode; 333 | int type; 334 | int snap; 335 | /* 336 | * Selection variables: 337 | * nb – normalized coordinates of the beginning of the selection 338 | * ne – normalized coordinates of the end of the selection 339 | * ob – original coordinates of the beginning of the selection 340 | * oe – original coordinates of the end of the selection 341 | */ 342 | struct { 343 | int x, y; 344 | } nb, ne, ob, oe; 345 | 346 | char *primary; 347 | struct wl_data_source *source; 348 | int alt; 349 | uint32_t tclick1, tclick2; 350 | } Selection; 351 | 352 | typedef union { 353 | int i; 354 | uint ui; 355 | float f; 356 | const void *v; 357 | } Arg; 358 | 359 | typedef struct { 360 | uint mod; 361 | xkb_keysym_t keysym; 362 | void (*func)(const Arg *); 363 | const Arg arg; 364 | } Shortcut; 365 | 366 | typedef struct { 367 | char str[32]; 368 | uint32_t key; 369 | int len; 370 | bool started; 371 | struct timespec last; 372 | } Repeat; 373 | 374 | /* function definitions used in config.h */ 375 | static void numlock(const Arg *); 376 | static void selpaste(const Arg *); 377 | static void wlzoom(const Arg *); 378 | static void wlzoomabs(const Arg *); 379 | static void wlzoomreset(const Arg *); 380 | static void printsel(const Arg *); 381 | static void printscreen(const Arg *) ; 382 | static void iso14755(const Arg *); 383 | static void toggleprinter(const Arg *); 384 | static void sendbreak(const Arg *); 385 | 386 | /* Config.h for applying patches and the configuration. */ 387 | #include "config.h" 388 | 389 | /* Font structure */ 390 | typedef struct { 391 | int height; 392 | int width; 393 | int ascent; 394 | int descent; 395 | int badslant; 396 | int badweight; 397 | short lbearing; 398 | short rbearing; 399 | struct wld_font *match; 400 | FcFontSet *set; 401 | FcPattern *pattern; 402 | } Font; 403 | 404 | /* Drawing Context */ 405 | typedef struct { 406 | uint32_t col[MAX(LEN(colorname), 256)]; 407 | Font font, bfont, ifont, ibfont; 408 | } DC; 409 | 410 | static void die(const char *, ...); 411 | static void draw(void); 412 | static void redraw(void); 413 | static void drawregion(int, int, int, int); 414 | static void execsh(void); 415 | static void stty(void); 416 | static void sigchld(int); 417 | static void run(void); 418 | static void cresize(int, int); 419 | 420 | static void csidump(void); 421 | static void csihandle(void); 422 | static void csiparse(void); 423 | static void csireset(void); 424 | static int eschandle(uchar); 425 | static void strdump(void); 426 | static void strhandle(void); 427 | static void strparse(void); 428 | static void strreset(void); 429 | 430 | static int tattrset(int); 431 | static void tprinter(char *, size_t); 432 | static void tdumpsel(void); 433 | static void tdumpline(int); 434 | static void tdump(void); 435 | static void tclearregion(int, int, int, int); 436 | static void tcursor(int); 437 | static void tdeletechar(int); 438 | static void tdeleteline(int); 439 | static void tinsertblank(int); 440 | static void tinsertblankline(int); 441 | static int tlinelen(int); 442 | static void tmoveto(int, int); 443 | static void tmoveato(int, int); 444 | static void tnew(int, int); 445 | static void tnewline(int); 446 | static void tputtab(int); 447 | static void tputc(Rune); 448 | static void treset(void); 449 | static void tresize(int, int); 450 | static void tscrollup(int, int); 451 | static void tscrolldown(int, int); 452 | static void tsetattr(int *, int); 453 | static void tsetchar(Rune, Glyph *, int, int); 454 | static void tsetscroll(int, int); 455 | static void tswapscreen(void); 456 | static void tsetdirt(int, int); 457 | static void tsetdirtattr(int); 458 | static void tsetmode(int, int, int *, int); 459 | static void tfulldirt(void); 460 | static void techo(Rune); 461 | static void tcontrolcode(uchar ); 462 | static void tdectest(char ); 463 | static void tdefutf8(char); 464 | static int32_t tdefcolor(int *, int *, int); 465 | static void tdeftran(char); 466 | static inline int match(uint, uint); 467 | static void ttynew(void); 468 | static size_t ttyread(void); 469 | static void ttyresize(void); 470 | static void ttysend(char *, size_t); 471 | static void ttywrite(const char *, size_t); 472 | static void tstrsequence(uchar); 473 | 474 | static inline uchar sixd_to_8bit(int); 475 | static void wldraws(char *, Glyph, int, int, int, int); 476 | static void wldrawglyph(Glyph, int, int); 477 | static void wlclear(int, int, int, int); 478 | static void wldrawcursor(void); 479 | static void wlinit(void); 480 | static void wlloadcols(void); 481 | static int wlsetcolorname(int, const char *); 482 | static void wlloadcursor(void); 483 | static int wlloadfont(Font *, FcPattern *); 484 | static void wlloadfonts(char *, double); 485 | static void wlsettitle(char *); 486 | static void wlresettitle(void); 487 | static void wlseturgency(int); 488 | static void wlsetsel(char*, uint32_t); 489 | static void wlunloadfont(Font *f); 490 | static void wlunloadfonts(void); 491 | static void wlresize(int, int); 492 | 493 | static void regglobal(void *, struct wl_registry *, uint32_t, const char *, 494 | uint32_t); 495 | static void regglobalremove(void *, struct wl_registry *, uint32_t); 496 | static void surfenter(void *, struct wl_surface *, struct wl_output *); 497 | static void surfleave(void *, struct wl_surface *, struct wl_output *); 498 | static void framedone(void *, struct wl_callback *, uint32_t); 499 | static void kbdkeymap(void *, struct wl_keyboard *, uint32_t, int32_t, uint32_t); 500 | static void kbdenter(void *, struct wl_keyboard *, uint32_t, 501 | struct wl_surface *, struct wl_array *); 502 | static void kbdleave(void *, struct wl_keyboard *, uint32_t, 503 | struct wl_surface *); 504 | static void kbdkey(void *, struct wl_keyboard *, uint32_t, uint32_t, uint32_t, 505 | uint32_t); 506 | static void kbdmodifiers(void *, struct wl_keyboard *, uint32_t, uint32_t, 507 | uint32_t, uint32_t, uint32_t); 508 | static void kbdrepeatinfo(void *, struct wl_keyboard *, int32_t, int32_t); 509 | static void ptrenter(void *, struct wl_pointer *, uint32_t, struct wl_surface *, 510 | wl_fixed_t, wl_fixed_t); 511 | static void ptrleave(void *, struct wl_pointer *, uint32_t, 512 | struct wl_surface *); 513 | static void ptrmotion(void *, struct wl_pointer *, uint32_t, 514 | wl_fixed_t, wl_fixed_t); 515 | static void ptrbutton(void *, struct wl_pointer *, uint32_t, uint32_t, 516 | uint32_t, uint32_t); 517 | static void ptraxis(void *, struct wl_pointer *, uint32_t, uint32_t, 518 | wl_fixed_t); 519 | static void wmping(void *, struct xdg_wm_base *, uint32_t); 520 | static void xdgsurfconfigure(void *, struct xdg_surface *, uint32_t); 521 | static void toplevelconfigure(void *, struct xdg_toplevel *, 522 | int32_t, int32_t, struct wl_array *); 523 | static void toplevelclose(void *, struct xdg_toplevel *); 524 | static void datadevoffer(void *, struct wl_data_device *, 525 | struct wl_data_offer *); 526 | static void datadeventer(void *, struct wl_data_device *, uint32_t, 527 | struct wl_surface *, wl_fixed_t, wl_fixed_t, struct wl_data_offer *); 528 | static void datadevleave(void *, struct wl_data_device *); 529 | static void datadevmotion(void *, struct wl_data_device *, uint32_t, 530 | wl_fixed_t x, wl_fixed_t y); 531 | static void datadevdrop(void *, struct wl_data_device *); 532 | static void datadevselection(void *, struct wl_data_device *, 533 | struct wl_data_offer *); 534 | static void dataofferoffer(void *, struct wl_data_offer *, const char *); 535 | static void datasrctarget(void *, struct wl_data_source *, const char *); 536 | static void datasrcsend(void *, struct wl_data_source *, const char *, int32_t); 537 | static void datasrccancelled(void *, struct wl_data_source *); 538 | 539 | static void selinit(void); 540 | static void selnormalize(void); 541 | static inline int selected(int, int); 542 | static char *getsel(void); 543 | static void selcopy(uint32_t); 544 | static void selscroll(int, int); 545 | static void selsnap(int *, int *, int); 546 | static int x2col(int); 547 | static int y2row(int); 548 | 549 | static size_t utf8decode(char *, Rune *, size_t); 550 | static Rune utf8decodebyte(char, size_t *); 551 | static size_t utf8encode(Rune, char *); 552 | static char utf8encodebyte(Rune, size_t); 553 | static char *utf8strchr(char *s, Rune u); 554 | static size_t utf8validate(Rune *, size_t); 555 | 556 | static ssize_t xwrite(int, const char *, size_t); 557 | static void *xmalloc(size_t); 558 | static void *xrealloc(void *, size_t); 559 | static char *xstrdup(char *); 560 | 561 | static void usage(void); 562 | 563 | static struct wl_registry_listener reglistener = { regglobal, regglobalremove }; 564 | static struct wl_surface_listener surflistener = { surfenter, surfleave }; 565 | static struct wl_callback_listener framelistener = { framedone }; 566 | static struct wl_keyboard_listener kbdlistener = 567 | { kbdkeymap, kbdenter, kbdleave, kbdkey, kbdmodifiers, kbdrepeatinfo }; 568 | static struct wl_pointer_listener ptrlistener = 569 | { ptrenter, ptrleave, ptrmotion, ptrbutton, ptraxis }; 570 | static struct xdg_wm_base_listener wmlistener = { wmping }; 571 | static struct xdg_surface_listener xdgsurflistener = { xdgsurfconfigure }; 572 | static struct xdg_toplevel_listener toplevellistener = 573 | { toplevelconfigure, toplevelclose }; 574 | static struct wl_data_device_listener datadevlistener = 575 | { datadevoffer, datadeventer, datadevleave, datadevmotion, datadevdrop, 576 | datadevselection }; 577 | static struct wl_data_offer_listener dataofferlistener = { dataofferoffer }; 578 | static struct wl_data_source_listener datasrclistener = 579 | { datasrctarget, datasrcsend, datasrccancelled }; 580 | 581 | /* Globals */ 582 | static DC dc; 583 | static Wayland wl; 584 | static WLD wld; 585 | static Cursor cursor; 586 | static Term term; 587 | static CSIEscape csiescseq; 588 | static STREscape strescseq; 589 | static int cmdfd; 590 | static pid_t pid; 591 | static Selection sel; 592 | static Repeat repeat; 593 | static bool needdraw = true; 594 | static int iofd = 1; 595 | static char **opt_cmd = NULL; 596 | static char *opt_class = NULL; 597 | static char *opt_embed = NULL; 598 | static char *opt_font = NULL; 599 | static char *opt_io = NULL; 600 | static char *opt_line = NULL; 601 | static char *opt_name = NULL; 602 | static char *opt_title = NULL; 603 | static int oldbutton = 3; /* button event on startup: 3 = release */ 604 | static int oldx, oldy; 605 | 606 | static char *usedfont = NULL; 607 | static double usedfontsize = 0; 608 | static double defaultfontsize = 0; 609 | 610 | static uchar utfbyte[UTF_SIZ + 1] = {0x80, 0, 0xC0, 0xE0, 0xF0}; 611 | static uchar utfmask[UTF_SIZ + 1] = {0xC0, 0x80, 0xE0, 0xF0, 0xF8}; 612 | static Rune utfmin[UTF_SIZ + 1] = { 0, 0, 0x80, 0x800, 0x10000}; 613 | static Rune utfmax[UTF_SIZ + 1] = {0x10FFFF, 0x7F, 0x7FF, 0xFFFF, 0x10FFFF}; 614 | 615 | /* Font Ring Cache */ 616 | enum { 617 | FRC_NORMAL, 618 | FRC_ITALIC, 619 | FRC_BOLD, 620 | FRC_ITALICBOLD 621 | }; 622 | 623 | typedef struct { 624 | struct wld_font *font; 625 | int flags; 626 | Rune unicodep; 627 | } Fontcache; 628 | 629 | /* Fontcache is an array now. A new font will be appended to the array. */ 630 | static Fontcache frc[16]; 631 | static int frclen = 0; 632 | 633 | ssize_t 634 | xwrite(int fd, const char *s, size_t len) 635 | { 636 | size_t aux = len; 637 | ssize_t r; 638 | 639 | while (len > 0) { 640 | r = write(fd, s, len); 641 | if (r < 0) 642 | return r; 643 | len -= r; 644 | s += r; 645 | } 646 | 647 | return aux; 648 | } 649 | 650 | void * 651 | xmalloc(size_t len) 652 | { 653 | void *p = malloc(len); 654 | 655 | if (!p) 656 | die("Out of memory\n"); 657 | 658 | return p; 659 | } 660 | 661 | void * 662 | xrealloc(void *p, size_t len) 663 | { 664 | if ((p = realloc(p, len)) == NULL) 665 | die("Out of memory\n"); 666 | 667 | return p; 668 | } 669 | 670 | char * 671 | xstrdup(char *s) 672 | { 673 | if ((s = strdup(s)) == NULL) 674 | die("Out of memory\n"); 675 | 676 | return s; 677 | } 678 | 679 | size_t 680 | utf8decode(char *c, Rune *u, size_t clen) 681 | { 682 | size_t i, j, len, type; 683 | Rune udecoded; 684 | 685 | *u = UTF_INVALID; 686 | if (!clen) 687 | return 0; 688 | udecoded = utf8decodebyte(c[0], &len); 689 | if (!BETWEEN(len, 1, UTF_SIZ)) 690 | return 1; 691 | for (i = 1, j = 1; i < clen && j < len; ++i, ++j) { 692 | udecoded = (udecoded << 6) | utf8decodebyte(c[i], &type); 693 | if (type != 0) 694 | return j; 695 | } 696 | if (j < len) 697 | return 0; 698 | *u = udecoded; 699 | utf8validate(u, len); 700 | 701 | return len; 702 | } 703 | 704 | Rune 705 | utf8decodebyte(char c, size_t *i) 706 | { 707 | for (*i = 0; *i < LEN(utfmask); ++(*i)) 708 | if (((uchar)c & utfmask[*i]) == utfbyte[*i]) 709 | return (uchar)c & ~utfmask[*i]; 710 | 711 | return 0; 712 | } 713 | 714 | size_t 715 | utf8encode(Rune u, char *c) 716 | { 717 | size_t len, i; 718 | 719 | len = utf8validate(&u, 0); 720 | if (len > UTF_SIZ) 721 | return 0; 722 | 723 | for (i = len - 1; i != 0; --i) { 724 | c[i] = utf8encodebyte(u, 0); 725 | u >>= 6; 726 | } 727 | c[0] = utf8encodebyte(u, len); 728 | 729 | return len; 730 | } 731 | 732 | char 733 | utf8encodebyte(Rune u, size_t i) 734 | { 735 | return utfbyte[i] | (u & ~utfmask[i]); 736 | } 737 | 738 | char * 739 | utf8strchr(char *s, Rune u) 740 | { 741 | Rune r; 742 | size_t i, j, len; 743 | 744 | len = strlen(s); 745 | for (i = 0, j = 0; i < len; i += j) { 746 | if (!(j = utf8decode(&s[i], &r, len - i))) 747 | break; 748 | if (r == u) 749 | return &(s[i]); 750 | } 751 | 752 | return NULL; 753 | } 754 | 755 | size_t 756 | utf8validate(Rune *u, size_t i) 757 | { 758 | if (!BETWEEN(*u, utfmin[i], utfmax[i]) || BETWEEN(*u, 0xD800, 0xDFFF)) 759 | *u = UTF_INVALID; 760 | for (i = 1; *u > utfmax[i]; ++i) 761 | ; 762 | 763 | return i; 764 | } 765 | 766 | void 767 | selinit(void) 768 | { 769 | sel.tclick1 = 0; 770 | sel.tclick2 = 0; 771 | sel.mode = SEL_IDLE; 772 | sel.snap = 0; 773 | sel.ob.x = -1; 774 | sel.primary = NULL; 775 | sel.source = NULL; 776 | } 777 | 778 | int 779 | x2col(int x) 780 | { 781 | x -= borderpx; 782 | x /= wl.cw; 783 | 784 | return LIMIT(x, 0, term.col-1); 785 | } 786 | 787 | int 788 | y2row(int y) 789 | { 790 | y -= borderpx; 791 | y /= wl.ch; 792 | 793 | return LIMIT(y, 0, term.row-1); 794 | } 795 | 796 | int 797 | tlinelen(int y) 798 | { 799 | int i = term.col; 800 | 801 | if (term.line[y][i - 1].mode & ATTR_WRAP) 802 | return i; 803 | 804 | while (i > 0 && term.line[y][i - 1].u == ' ') 805 | --i; 806 | 807 | return i; 808 | } 809 | 810 | void 811 | selnormalize(void) 812 | { 813 | int i; 814 | 815 | if (sel.type == SEL_REGULAR && sel.ob.y != sel.oe.y) { 816 | sel.nb.x = sel.ob.y < sel.oe.y ? sel.ob.x : sel.oe.x; 817 | sel.ne.x = sel.ob.y < sel.oe.y ? sel.oe.x : sel.ob.x; 818 | } else { 819 | sel.nb.x = MIN(sel.ob.x, sel.oe.x); 820 | sel.ne.x = MAX(sel.ob.x, sel.oe.x); 821 | } 822 | sel.nb.y = MIN(sel.ob.y, sel.oe.y); 823 | sel.ne.y = MAX(sel.ob.y, sel.oe.y); 824 | 825 | selsnap(&sel.nb.x, &sel.nb.y, -1); 826 | selsnap(&sel.ne.x, &sel.ne.y, +1); 827 | 828 | /* expand selection over line breaks */ 829 | if (sel.type == SEL_RECTANGULAR) 830 | return; 831 | i = tlinelen(sel.nb.y); 832 | if (i < sel.nb.x) 833 | sel.nb.x = i; 834 | if (tlinelen(sel.ne.y) <= sel.ne.x) 835 | sel.ne.x = term.col - 1; 836 | } 837 | 838 | int 839 | selected(int x, int y) 840 | { 841 | if (sel.mode == SEL_EMPTY) 842 | return 0; 843 | 844 | if (sel.type == SEL_RECTANGULAR) 845 | return BETWEEN(y, sel.nb.y, sel.ne.y) 846 | && BETWEEN(x, sel.nb.x, sel.ne.x); 847 | 848 | return BETWEEN(y, sel.nb.y, sel.ne.y) 849 | && (y != sel.nb.y || x >= sel.nb.x) 850 | && (y != sel.ne.y || x <= sel.ne.x); 851 | } 852 | 853 | void 854 | selsnap(int *x, int *y, int direction) 855 | { 856 | int newx, newy, xt, yt; 857 | int delim, prevdelim; 858 | Glyph *gp, *prevgp; 859 | 860 | switch (sel.snap) { 861 | case SNAP_WORD: 862 | /* 863 | * Snap around if the word wraps around at the end or 864 | * beginning of a line. 865 | */ 866 | prevgp = &term.line[*y][*x]; 867 | prevdelim = ISDELIM(prevgp->u); 868 | for (;;) { 869 | newx = *x + direction; 870 | newy = *y; 871 | if (!BETWEEN(newx, 0, term.col - 1)) { 872 | newy += direction; 873 | newx = (newx + term.col) % term.col; 874 | if (!BETWEEN(newy, 0, term.row - 1)) 875 | break; 876 | 877 | if (direction > 0) 878 | yt = *y, xt = *x; 879 | else 880 | yt = newy, xt = newx; 881 | if (!(term.line[yt][xt].mode & ATTR_WRAP)) 882 | break; 883 | } 884 | 885 | if (newx >= tlinelen(newy)) 886 | break; 887 | 888 | gp = &term.line[newy][newx]; 889 | delim = ISDELIM(gp->u); 890 | if (!(gp->mode & ATTR_WDUMMY) && (delim != prevdelim 891 | || (delim && gp->u != prevgp->u))) 892 | break; 893 | 894 | *x = newx; 895 | *y = newy; 896 | prevgp = gp; 897 | prevdelim = delim; 898 | } 899 | break; 900 | case SNAP_LINE: 901 | /* 902 | * Snap around if the the previous line or the current one 903 | * has set ATTR_WRAP at its end. Then the whole next or 904 | * previous line will be selected. 905 | */ 906 | *x = (direction < 0) ? 0 : term.col - 1; 907 | if (direction < 0) { 908 | for (; *y > 0; *y += direction) { 909 | if (!(term.line[*y-1][term.col-1].mode 910 | & ATTR_WRAP)) { 911 | break; 912 | } 913 | } 914 | } else if (direction > 0) { 915 | for (; *y < term.row-1; *y += direction) { 916 | if (!(term.line[*y][term.col-1].mode 917 | & ATTR_WRAP)) { 918 | break; 919 | } 920 | } 921 | } 922 | break; 923 | } 924 | } 925 | 926 | void 927 | getbuttoninfo(void) 928 | { 929 | int type; 930 | uint state = wl.xkb.mods & ~forceselmod; 931 | 932 | sel.alt = IS_SET(MODE_ALTSCREEN); 933 | 934 | sel.oe.x = x2col(wl.px); 935 | sel.oe.y = y2row(wl.py); 936 | selnormalize(); 937 | 938 | sel.type = SEL_REGULAR; 939 | for (type = 1; type < LEN(selmasks); ++type) { 940 | if (match(selmasks[type], state)) { 941 | sel.type = type; 942 | break; 943 | } 944 | } 945 | } 946 | 947 | void 948 | wlmousereport(int button, bool release, int x, int y) 949 | { 950 | int len; 951 | char buf[40]; 952 | 953 | if (!IS_SET(MODE_MOUSEX10)) { 954 | button += ((wl.xkb.mods & MOD_MASK_SHIFT) ? 4 : 0) 955 | + ((wl.xkb.mods & MOD_MASK_LOGO ) ? 8 : 0) 956 | + ((wl.xkb.mods & MOD_MASK_CTRL ) ? 16 : 0); 957 | } 958 | 959 | if (IS_SET(MODE_MOUSESGR)) { 960 | len = snprintf(buf, sizeof(buf), "\033[<%d;%d;%d%c", 961 | button, x+1, y+1, release ? 'm' : 'M'); 962 | } else if (x < 223 && y < 223) { 963 | len = snprintf(buf, sizeof(buf), "\033[M%c%c%c", 964 | 32+button, 32+x+1, 32+y+1); 965 | } else { 966 | return; 967 | } 968 | 969 | ttywrite(buf, len); 970 | } 971 | 972 | void 973 | wlmousereportbutton(uint32_t button, uint32_t state) 974 | { 975 | bool release = state == WL_POINTER_BUTTON_STATE_RELEASED; 976 | 977 | if (!IS_SET(MODE_MOUSESGR) && release) { 978 | button = 3; 979 | } else { 980 | switch (button) { 981 | case BTN_LEFT: 982 | button = 0; 983 | break; 984 | case BTN_MIDDLE: 985 | button = 1; 986 | break; 987 | case BTN_RIGHT: 988 | button = 2; 989 | break; 990 | } 991 | } 992 | 993 | oldbutton = release ? 3 : button; 994 | 995 | /* don't report release events when in X10 mode */ 996 | if (IS_SET(MODE_MOUSEX10) && release) { 997 | return; 998 | } 999 | 1000 | wlmousereport(button, release, oldx, oldy); 1001 | } 1002 | 1003 | void 1004 | wlmousereportmotion(wl_fixed_t fx, wl_fixed_t fy) 1005 | { 1006 | int x = x2col(wl_fixed_to_int(fx)), y = y2row(wl_fixed_to_int(fy)); 1007 | 1008 | if (x == oldx && y == oldy) 1009 | return; 1010 | if (!IS_SET(MODE_MOUSEMOTION) && !IS_SET(MODE_MOUSEMANY)) 1011 | return; 1012 | /* MOUSE_MOTION: no reporting if no button is pressed */ 1013 | if (IS_SET(MODE_MOUSEMOTION) && oldbutton == 3) 1014 | return; 1015 | 1016 | oldx = x; 1017 | oldy = y; 1018 | wlmousereport(oldbutton + 32, false, x, y); 1019 | } 1020 | 1021 | void 1022 | wlmousereportaxis(uint32_t axis, wl_fixed_t amount) 1023 | { 1024 | wlmousereport(64 + (axis == AXIS_VERTICAL ? 4 : 6) 1025 | + (amount > 0 ? 1 : 0), false, oldx, oldy); 1026 | } 1027 | 1028 | char * 1029 | getsel(void) 1030 | { 1031 | char *str, *ptr; 1032 | int y, bufsize, lastx, linelen; 1033 | Glyph *gp, *last; 1034 | 1035 | if (sel.ob.x == -1) 1036 | return NULL; 1037 | 1038 | bufsize = (term.col+1) * (sel.ne.y-sel.nb.y+1) * UTF_SIZ; 1039 | ptr = str = xmalloc(bufsize); 1040 | 1041 | /* append every set & selected glyph to the selection */ 1042 | for (y = sel.nb.y; y <= sel.ne.y; y++) { 1043 | if ((linelen = tlinelen(y)) == 0) { 1044 | *ptr++ = '\n'; 1045 | continue; 1046 | } 1047 | 1048 | if (sel.type == SEL_RECTANGULAR) { 1049 | gp = &term.line[y][sel.nb.x]; 1050 | lastx = sel.ne.x; 1051 | } else { 1052 | gp = &term.line[y][sel.nb.y == y ? sel.nb.x : 0]; 1053 | lastx = (sel.ne.y == y) ? sel.ne.x : term.col-1; 1054 | } 1055 | last = &term.line[y][MIN(lastx, linelen-1)]; 1056 | while (last >= gp && last->u == ' ') 1057 | --last; 1058 | 1059 | for ( ; gp <= last; ++gp) { 1060 | if (gp->mode & ATTR_WDUMMY) 1061 | continue; 1062 | 1063 | ptr += utf8encode(gp->u, ptr); 1064 | } 1065 | 1066 | /* 1067 | * Copy and pasting of line endings is inconsistent 1068 | * in the inconsistent terminal and GUI world. 1069 | * The best solution seems like to produce '\n' when 1070 | * something is copied from st and convert '\n' to 1071 | * '\r', when something to be pasted is received by 1072 | * st. 1073 | * FIXME: Fix the computer world. 1074 | */ 1075 | if ((y < sel.ne.y || lastx >= linelen) && !(last->mode & ATTR_WRAP)) 1076 | *ptr++ = '\n'; 1077 | } 1078 | *ptr = 0; 1079 | return str; 1080 | } 1081 | 1082 | void 1083 | selcopy(uint32_t serial) 1084 | { 1085 | wlsetsel(getsel(), serial); 1086 | } 1087 | 1088 | static inline void 1089 | selwritebuf(char *buf, int len) 1090 | { 1091 | char *repl = buf; 1092 | 1093 | /* 1094 | * As seen in getsel: 1095 | * Line endings are inconsistent in the terminal and GUI world 1096 | * copy and pasting. When receiving some selection data, 1097 | * replace all '\n' with '\r'. 1098 | * FIXME: Fix the computer world. 1099 | */ 1100 | while ((repl = memchr(repl, '\n', len))) { 1101 | *repl++ = '\r'; 1102 | } 1103 | 1104 | ttysend(buf, len); 1105 | } 1106 | 1107 | void 1108 | selpaste(const Arg *dummy) 1109 | { 1110 | int fds[2], len, left; 1111 | char buf[BUFSIZ], *str; 1112 | 1113 | if (wl.seloffer) { 1114 | if (IS_SET(MODE_BRCKTPASTE)) 1115 | ttywrite("\033[200~", 6); 1116 | /* check if we are pasting from ourselves */ 1117 | if (sel.source) { 1118 | str = sel.primary; 1119 | left = strlen(sel.primary); 1120 | while (left > 0) { 1121 | len = MIN(sizeof buf, left); 1122 | memcpy(buf, str, len); 1123 | selwritebuf(buf, len); 1124 | left -= len; 1125 | str += len; 1126 | } 1127 | } else { 1128 | pipe(fds); 1129 | wl_data_offer_receive(wl.seloffer, "text/plain", fds[1]); 1130 | wl_display_flush(wl.dpy); 1131 | close(fds[1]); 1132 | while ((len = read(fds[0], buf, sizeof buf)) > 0) { 1133 | selwritebuf(buf, len); 1134 | } 1135 | close(fds[0]); 1136 | } 1137 | if (IS_SET(MODE_BRCKTPASTE)) 1138 | ttywrite("\033[201~", 6); 1139 | } 1140 | } 1141 | 1142 | void 1143 | selclear(void) 1144 | { 1145 | if (sel.ob.x == -1) 1146 | return; 1147 | sel.mode = SEL_IDLE; 1148 | sel.ob.x = -1; 1149 | tsetdirt(sel.nb.y, sel.ne.y); 1150 | } 1151 | 1152 | void 1153 | wlsetsel(char *str, uint32_t serial) 1154 | { 1155 | free(sel.primary); 1156 | sel.primary = str; 1157 | 1158 | if (str) { 1159 | sel.source = wl_data_device_manager_create_data_source(wl.datadevmanager); 1160 | wl_data_source_add_listener(sel.source, &datasrclistener, NULL); 1161 | wl_data_source_offer(sel.source, "text/plain; charset=utf-8"); 1162 | } else { 1163 | sel.source = NULL; 1164 | } 1165 | wl_data_device_set_selection(wl.datadev, sel.source, serial); 1166 | } 1167 | 1168 | void 1169 | die(const char *errstr, ...) 1170 | { 1171 | va_list ap; 1172 | 1173 | va_start(ap, errstr); 1174 | vfprintf(stderr, errstr, ap); 1175 | va_end(ap); 1176 | exit(1); 1177 | } 1178 | 1179 | void 1180 | execsh(void) 1181 | { 1182 | char **args, *sh, *prog; 1183 | const struct passwd *pw; 1184 | 1185 | errno = 0; 1186 | if ((pw = getpwuid(getuid())) == NULL) { 1187 | if (errno) 1188 | die("getpwuid:%s\n", strerror(errno)); 1189 | else 1190 | die("who are you?\n"); 1191 | } 1192 | 1193 | if ((sh = getenv("SHELL")) == NULL) 1194 | sh = (pw->pw_shell[0]) ? pw->pw_shell : shell; 1195 | 1196 | if (opt_cmd) 1197 | prog = opt_cmd[0]; 1198 | else if (utmp) 1199 | prog = utmp; 1200 | else 1201 | prog = sh; 1202 | args = (opt_cmd) ? opt_cmd : (char *[]) {prog, NULL}; 1203 | 1204 | unsetenv("COLUMNS"); 1205 | unsetenv("LINES"); 1206 | unsetenv("TERMCAP"); 1207 | setenv("LOGNAME", pw->pw_name, 1); 1208 | setenv("USER", pw->pw_name, 1); 1209 | setenv("SHELL", sh, 1); 1210 | setenv("HOME", pw->pw_dir, 1); 1211 | setenv("TERM", termname, 1); 1212 | 1213 | signal(SIGCHLD, SIG_DFL); 1214 | signal(SIGHUP, SIG_DFL); 1215 | signal(SIGINT, SIG_DFL); 1216 | signal(SIGQUIT, SIG_DFL); 1217 | signal(SIGTERM, SIG_DFL); 1218 | signal(SIGALRM, SIG_DFL); 1219 | 1220 | execvp(prog, args); 1221 | _exit(1); 1222 | } 1223 | 1224 | void 1225 | sigchld(int a) 1226 | { 1227 | int stat; 1228 | pid_t p; 1229 | 1230 | if ((p = waitpid(pid, &stat, WNOHANG)) < 0) 1231 | die("Waiting for pid %hd failed: %s\n", pid, strerror(errno)); 1232 | 1233 | if (pid != p) 1234 | return; 1235 | 1236 | if (!WIFEXITED(stat) || WEXITSTATUS(stat)) 1237 | die("child finished with error '%d'\n", stat); 1238 | exit(0); 1239 | } 1240 | 1241 | 1242 | void 1243 | stty(void) 1244 | { 1245 | char cmd[_POSIX_ARG_MAX], **p, *q, *s; 1246 | size_t n, siz; 1247 | 1248 | if ((n = strlen(stty_args)) > sizeof(cmd)-1) 1249 | die("incorrect stty parameters\n"); 1250 | memcpy(cmd, stty_args, n); 1251 | q = cmd + n; 1252 | siz = sizeof(cmd) - n; 1253 | for (p = opt_cmd; p && (s = *p); ++p) { 1254 | if ((n = strlen(s)) > siz-1) 1255 | die("stty parameter length too long\n"); 1256 | *q++ = ' '; 1257 | memcpy(q, s, n); 1258 | q += n; 1259 | siz -= n + 1; 1260 | } 1261 | *q = '\0'; 1262 | if (system(cmd) != 0) 1263 | perror("Couldn't call stty"); 1264 | } 1265 | 1266 | void 1267 | ttynew(void) 1268 | { 1269 | int m, s; 1270 | struct winsize w = {term.row, term.col, 0, 0}; 1271 | 1272 | if (opt_io) { 1273 | term.mode |= MODE_PRINT; 1274 | iofd = (!strcmp(opt_io, "-")) ? 1275 | 1 : open(opt_io, O_WRONLY | O_CREAT, 0666); 1276 | if (iofd < 0) { 1277 | fprintf(stderr, "Error opening %s:%s\n", 1278 | opt_io, strerror(errno)); 1279 | } 1280 | } 1281 | 1282 | if (opt_line) { 1283 | if ((cmdfd = open(opt_line, O_RDWR)) < 0) 1284 | die("open line failed: %s\n", strerror(errno)); 1285 | dup2(cmdfd, 0); 1286 | stty(); 1287 | return; 1288 | } 1289 | 1290 | /* seems to work fine on linux, openbsd and freebsd */ 1291 | if (openpty(&m, &s, NULL, NULL, &w) < 0) 1292 | die("openpty failed: %s\n", strerror(errno)); 1293 | 1294 | switch (pid = fork()) { 1295 | case -1: 1296 | die("fork failed\n"); 1297 | break; 1298 | case 0: 1299 | close(iofd); 1300 | setsid(); /* create a new process group */ 1301 | dup2(s, 0); 1302 | dup2(s, 1); 1303 | dup2(s, 2); 1304 | if (ioctl(s, TIOCSCTTY, NULL) < 0) 1305 | die("ioctl TIOCSCTTY failed: %s\n", strerror(errno)); 1306 | close(s); 1307 | close(m); 1308 | execsh(); 1309 | break; 1310 | default: 1311 | close(s); 1312 | cmdfd = m; 1313 | signal(SIGCHLD, sigchld); 1314 | break; 1315 | } 1316 | } 1317 | 1318 | size_t 1319 | ttyread(void) 1320 | { 1321 | static char buf[BUFSIZ]; 1322 | static int buflen = 0; 1323 | char *ptr; 1324 | int charsize; /* size of utf8 char in bytes */ 1325 | Rune unicodep; 1326 | int ret; 1327 | 1328 | /* append read bytes to unprocessed bytes */ 1329 | if ((ret = read(cmdfd, buf+buflen, LEN(buf)-buflen)) < 0) 1330 | die("Couldn't read from shell: %s\n", strerror(errno)); 1331 | 1332 | buflen += ret; 1333 | ptr = buf; 1334 | 1335 | for (;;) { 1336 | if (IS_SET(MODE_UTF8) && !IS_SET(MODE_SIXEL)) { 1337 | /* process a complete utf8 char */ 1338 | charsize = utf8decode(ptr, &unicodep, buflen); 1339 | if (charsize == 0) 1340 | break; 1341 | tputc(unicodep); 1342 | ptr += charsize; 1343 | buflen -= charsize; 1344 | 1345 | } else { 1346 | if (buflen <= 0) 1347 | break; 1348 | tputc(*ptr++ & 0xFF); 1349 | buflen--; 1350 | } 1351 | } 1352 | /* keep any uncomplete utf8 char for the next call */ 1353 | if (buflen > 0) 1354 | memmove(buf, ptr, buflen); 1355 | 1356 | needdraw = true; 1357 | return ret; 1358 | } 1359 | 1360 | void 1361 | ttywrite(const char *s, size_t n) 1362 | { 1363 | fd_set wfd, rfd; 1364 | ssize_t r; 1365 | size_t lim = 256; 1366 | 1367 | /* 1368 | * Remember that we are using a pty, which might be a modem line. 1369 | * Writing too much will clog the line. That's why we are doing this 1370 | * dance. 1371 | * FIXME: Migrate the world to Plan 9. 1372 | */ 1373 | while (n > 0) { 1374 | FD_ZERO(&wfd); 1375 | FD_ZERO(&rfd); 1376 | FD_SET(cmdfd, &wfd); 1377 | FD_SET(cmdfd, &rfd); 1378 | 1379 | /* Check if we can write. */ 1380 | if (pselect(cmdfd+1, &rfd, &wfd, NULL, NULL, NULL) < 0) { 1381 | if (errno == EINTR) 1382 | continue; 1383 | die("select failed: %s\n", strerror(errno)); 1384 | } 1385 | if (FD_ISSET(cmdfd, &wfd)) { 1386 | /* 1387 | * Only write the bytes written by ttywrite() or the 1388 | * default of 256. This seems to be a reasonable value 1389 | * for a serial line. Bigger values might clog the I/O. 1390 | */ 1391 | if ((r = write(cmdfd, s, (n < lim)? n : lim)) < 0) 1392 | goto write_error; 1393 | if (r < n) { 1394 | /* 1395 | * We weren't able to write out everything. 1396 | * This means the buffer is getting full 1397 | * again. Empty it. 1398 | */ 1399 | if (n < lim) 1400 | lim = ttyread(); 1401 | n -= r; 1402 | s += r; 1403 | } else { 1404 | /* All bytes have been written. */ 1405 | break; 1406 | } 1407 | } 1408 | if (FD_ISSET(cmdfd, &rfd)) 1409 | lim = ttyread(); 1410 | } 1411 | return; 1412 | 1413 | write_error: 1414 | die("write error on tty: %s\n", strerror(errno)); 1415 | } 1416 | 1417 | void 1418 | ttysend(char *s, size_t n) 1419 | { 1420 | int len; 1421 | char *t, *lim; 1422 | Rune u; 1423 | 1424 | ttywrite(s, n); 1425 | if (!IS_SET(MODE_ECHO)) 1426 | return; 1427 | 1428 | lim = &s[n]; 1429 | for (t = s; t < lim; t += len) { 1430 | if (IS_SET(MODE_UTF8) && !IS_SET(MODE_SIXEL)) { 1431 | len = utf8decode(t, &u, n); 1432 | } else { 1433 | u = *t & 0xFF; 1434 | len = 1; 1435 | } 1436 | if (len <= 0) 1437 | break; 1438 | techo(u); 1439 | n -= len; 1440 | } 1441 | } 1442 | 1443 | void 1444 | ttyresize(void) 1445 | { 1446 | struct winsize w; 1447 | 1448 | w.ws_row = term.row; 1449 | w.ws_col = term.col; 1450 | w.ws_xpixel = wl.tw; 1451 | w.ws_ypixel = wl.th; 1452 | if (ioctl(cmdfd, TIOCSWINSZ, &w) < 0) 1453 | fprintf(stderr, "Couldn't set window size: %s\n", strerror(errno)); 1454 | } 1455 | 1456 | int 1457 | tattrset(int attr) 1458 | { 1459 | int i, j; 1460 | 1461 | for (i = 0; i < term.row-1; i++) { 1462 | for (j = 0; j < term.col-1; j++) { 1463 | if (term.line[i][j].mode & attr) 1464 | return 1; 1465 | } 1466 | } 1467 | 1468 | return 0; 1469 | } 1470 | 1471 | void 1472 | tsetdirt(int top, int bot) 1473 | { 1474 | int i; 1475 | 1476 | LIMIT(top, 0, term.row-1); 1477 | LIMIT(bot, 0, term.row-1); 1478 | 1479 | for (i = top; i <= bot; i++) 1480 | term.dirty[i] = 1; 1481 | 1482 | needdraw = true; 1483 | } 1484 | 1485 | void 1486 | tsetdirtattr(int attr) 1487 | { 1488 | int i, j; 1489 | 1490 | for (i = 0; i < term.row-1; i++) { 1491 | for (j = 0; j < term.col-1; j++) { 1492 | if (term.line[i][j].mode & attr) { 1493 | tsetdirt(i, i); 1494 | break; 1495 | } 1496 | } 1497 | } 1498 | } 1499 | 1500 | void 1501 | tfulldirt(void) 1502 | { 1503 | tsetdirt(0, term.row-1); 1504 | } 1505 | 1506 | void 1507 | tcursor(int mode) 1508 | { 1509 | static TCursor c[2]; 1510 | int alt = IS_SET(MODE_ALTSCREEN); 1511 | 1512 | if (mode == CURSOR_SAVE) { 1513 | c[alt] = term.c; 1514 | } else if (mode == CURSOR_LOAD) { 1515 | term.c = c[alt]; 1516 | tmoveto(c[alt].x, c[alt].y); 1517 | } 1518 | } 1519 | 1520 | void 1521 | treset(void) 1522 | { 1523 | uint i; 1524 | 1525 | term.c = (TCursor){{ 1526 | .mode = ATTR_NULL, 1527 | .fg = defaultfg, 1528 | .bg = defaultbg 1529 | }, .x = 0, .y = 0, .state = CURSOR_DEFAULT}; 1530 | 1531 | memset(term.tabs, 0, term.col * sizeof(*term.tabs)); 1532 | for (i = tabspaces; i < term.col; i += tabspaces) 1533 | term.tabs[i] = 1; 1534 | term.top = 0; 1535 | term.bot = term.row - 1; 1536 | term.mode = MODE_WRAP|MODE_UTF8; 1537 | memset(term.trantbl, CS_USA, sizeof(term.trantbl)); 1538 | term.charset = 0; 1539 | 1540 | for (i = 0; i < 2; i++) { 1541 | tmoveto(0, 0); 1542 | tcursor(CURSOR_SAVE); 1543 | tclearregion(0, 0, term.col-1, term.row-1); 1544 | tswapscreen(); 1545 | } 1546 | } 1547 | 1548 | void 1549 | tnew(int col, int row) 1550 | { 1551 | term = (Term){ .c = { .attr = { .fg = defaultfg, .bg = defaultbg } } }; 1552 | tresize(col, row); 1553 | term.numlock = 1; 1554 | 1555 | treset(); 1556 | } 1557 | 1558 | void 1559 | tswapscreen(void) 1560 | { 1561 | Line *tmp = term.line; 1562 | 1563 | term.line = term.alt; 1564 | term.alt = tmp; 1565 | term.mode ^= MODE_ALTSCREEN; 1566 | tfulldirt(); 1567 | } 1568 | 1569 | void 1570 | tscrolldown(int orig, int n) 1571 | { 1572 | int i; 1573 | Line temp; 1574 | 1575 | LIMIT(n, 0, term.bot-orig+1); 1576 | 1577 | tsetdirt(orig, term.bot-n); 1578 | tclearregion(0, term.bot-n+1, term.col-1, term.bot); 1579 | 1580 | for (i = term.bot; i >= orig+n; i--) { 1581 | temp = term.line[i]; 1582 | term.line[i] = term.line[i-n]; 1583 | term.line[i-n] = temp; 1584 | } 1585 | 1586 | selscroll(orig, n); 1587 | } 1588 | 1589 | void 1590 | tscrollup(int orig, int n) 1591 | { 1592 | int i; 1593 | Line temp; 1594 | 1595 | LIMIT(n, 0, term.bot-orig+1); 1596 | 1597 | tclearregion(0, orig, term.col-1, orig+n-1); 1598 | tsetdirt(orig+n, term.bot); 1599 | 1600 | for (i = orig; i <= term.bot-n; i++) { 1601 | temp = term.line[i]; 1602 | term.line[i] = term.line[i+n]; 1603 | term.line[i+n] = temp; 1604 | } 1605 | 1606 | selscroll(orig, -n); 1607 | } 1608 | 1609 | void 1610 | selscroll(int orig, int n) 1611 | { 1612 | if (sel.ob.x == -1) 1613 | return; 1614 | 1615 | if (BETWEEN(sel.ob.y, orig, term.bot) || BETWEEN(sel.oe.y, orig, term.bot)) { 1616 | if ((sel.ob.y += n) > term.bot || (sel.oe.y += n) < term.top) { 1617 | selclear(); 1618 | return; 1619 | } 1620 | if (sel.type == SEL_RECTANGULAR) { 1621 | if (sel.ob.y < term.top) 1622 | sel.ob.y = term.top; 1623 | if (sel.oe.y > term.bot) 1624 | sel.oe.y = term.bot; 1625 | } else { 1626 | if (sel.ob.y < term.top) { 1627 | sel.ob.y = term.top; 1628 | sel.ob.x = 0; 1629 | } 1630 | if (sel.oe.y > term.bot) { 1631 | sel.oe.y = term.bot; 1632 | sel.oe.x = term.col; 1633 | } 1634 | } 1635 | selnormalize(); 1636 | } 1637 | } 1638 | 1639 | void 1640 | tnewline(int first_col) 1641 | { 1642 | int y = term.c.y; 1643 | 1644 | if (y == term.bot) { 1645 | tscrollup(term.top, 1); 1646 | } else { 1647 | y++; 1648 | } 1649 | tmoveto(first_col ? 0 : term.c.x, y); 1650 | } 1651 | 1652 | void 1653 | csiparse(void) 1654 | { 1655 | char *p = csiescseq.buf, *np; 1656 | long int v; 1657 | 1658 | csiescseq.narg = 0; 1659 | if (*p == '?') { 1660 | csiescseq.priv = 1; 1661 | p++; 1662 | } 1663 | 1664 | csiescseq.buf[csiescseq.len] = '\0'; 1665 | while (p < csiescseq.buf+csiescseq.len) { 1666 | np = NULL; 1667 | v = strtol(p, &np, 10); 1668 | if (np == p) 1669 | v = 0; 1670 | if (v == LONG_MAX || v == LONG_MIN) 1671 | v = -1; 1672 | csiescseq.arg[csiescseq.narg++] = v; 1673 | p = np; 1674 | if (*p != ';' || csiescseq.narg == ESC_ARG_SIZ) 1675 | break; 1676 | p++; 1677 | } 1678 | csiescseq.mode[0] = *p++; 1679 | csiescseq.mode[1] = (p < csiescseq.buf+csiescseq.len) ? *p : '\0'; 1680 | } 1681 | 1682 | /* for absolute user moves, when decom is set */ 1683 | void 1684 | tmoveato(int x, int y) 1685 | { 1686 | tmoveto(x, y + ((term.c.state & CURSOR_ORIGIN) ? term.top: 0)); 1687 | } 1688 | 1689 | void 1690 | tmoveto(int x, int y) 1691 | { 1692 | int miny, maxy; 1693 | 1694 | if (term.c.state & CURSOR_ORIGIN) { 1695 | miny = term.top; 1696 | maxy = term.bot; 1697 | } else { 1698 | miny = 0; 1699 | maxy = term.row - 1; 1700 | } 1701 | term.c.state &= ~CURSOR_WRAPNEXT; 1702 | term.c.x = LIMIT(x, 0, term.col-1); 1703 | term.c.y = LIMIT(y, miny, maxy); 1704 | } 1705 | 1706 | void 1707 | tsetchar(Rune u, Glyph *attr, int x, int y) 1708 | { 1709 | static char *vt100_0[62] = { /* 0x41 - 0x7e */ 1710 | "↑", "↓", "→", "←", "█", "▚", "☃", /* A - G */ 1711 | 0, 0, 0, 0, 0, 0, 0, 0, /* H - O */ 1712 | 0, 0, 0, 0, 0, 0, 0, 0, /* P - W */ 1713 | 0, 0, 0, 0, 0, 0, 0, " ", /* X - _ */ 1714 | "◆", "▒", "␉", "␌", "␍", "␊", "°", "±", /* ` - g */ 1715 | "␤", "␋", "┘", "┐", "┌", "└", "┼", "⎺", /* h - o */ 1716 | "⎻", "─", "⎼", "⎽", "├", "┤", "┴", "┬", /* p - w */ 1717 | "│", "≤", "≥", "π", "≠", "£", "·", /* x - ~ */ 1718 | }; 1719 | 1720 | /* 1721 | * The table is proudly stolen from rxvt. 1722 | */ 1723 | if (term.trantbl[term.charset] == CS_GRAPHIC0 && 1724 | BETWEEN(u, 0x41, 0x7e) && vt100_0[u - 0x41]) 1725 | utf8decode(vt100_0[u - 0x41], &u, UTF_SIZ); 1726 | 1727 | if (term.line[y][x].mode & ATTR_WIDE) { 1728 | if (x+1 < term.col) { 1729 | term.line[y][x+1].u = ' '; 1730 | term.line[y][x+1].mode &= ~ATTR_WDUMMY; 1731 | } 1732 | } else if (term.line[y][x].mode & ATTR_WDUMMY) { 1733 | term.line[y][x-1].u = ' '; 1734 | term.line[y][x-1].mode &= ~ATTR_WIDE; 1735 | } 1736 | 1737 | term.dirty[y] = 1; 1738 | term.line[y][x] = *attr; 1739 | term.line[y][x].u = u; 1740 | } 1741 | 1742 | void 1743 | tclearregion(int x1, int y1, int x2, int y2) 1744 | { 1745 | int x, y, temp; 1746 | Glyph *gp; 1747 | 1748 | if (x1 > x2) 1749 | temp = x1, x1 = x2, x2 = temp; 1750 | if (y1 > y2) 1751 | temp = y1, y1 = y2, y2 = temp; 1752 | 1753 | LIMIT(x1, 0, term.col-1); 1754 | LIMIT(x2, 0, term.col-1); 1755 | LIMIT(y1, 0, term.row-1); 1756 | LIMIT(y2, 0, term.row-1); 1757 | 1758 | for (y = y1; y <= y2; y++) { 1759 | term.dirty[y] = 1; 1760 | for (x = x1; x <= x2; x++) { 1761 | gp = &term.line[y][x]; 1762 | if (selected(x, y)) 1763 | selclear(); 1764 | gp->fg = term.c.attr.fg; 1765 | gp->bg = term.c.attr.bg; 1766 | gp->mode = 0; 1767 | gp->u = ' '; 1768 | } 1769 | } 1770 | } 1771 | 1772 | void 1773 | tdeletechar(int n) 1774 | { 1775 | int dst, src, size; 1776 | Glyph *line; 1777 | 1778 | LIMIT(n, 0, term.col - term.c.x); 1779 | 1780 | dst = term.c.x; 1781 | src = term.c.x + n; 1782 | size = term.col - src; 1783 | line = term.line[term.c.y]; 1784 | 1785 | memmove(&line[dst], &line[src], size * sizeof(Glyph)); 1786 | tclearregion(term.col-n, term.c.y, term.col-1, term.c.y); 1787 | } 1788 | 1789 | void 1790 | tinsertblank(int n) 1791 | { 1792 | int dst, src, size; 1793 | Glyph *line; 1794 | 1795 | LIMIT(n, 0, term.col - term.c.x); 1796 | 1797 | dst = term.c.x + n; 1798 | src = term.c.x; 1799 | size = term.col - dst; 1800 | line = term.line[term.c.y]; 1801 | 1802 | memmove(&line[dst], &line[src], size * sizeof(Glyph)); 1803 | tclearregion(src, term.c.y, dst - 1, term.c.y); 1804 | } 1805 | 1806 | void 1807 | tinsertblankline(int n) 1808 | { 1809 | if (BETWEEN(term.c.y, term.top, term.bot)) 1810 | tscrolldown(term.c.y, n); 1811 | } 1812 | 1813 | void 1814 | tdeleteline(int n) 1815 | { 1816 | if (BETWEEN(term.c.y, term.top, term.bot)) 1817 | tscrollup(term.c.y, n); 1818 | } 1819 | 1820 | int32_t 1821 | tdefcolor(int *attr, int *npar, int l) 1822 | { 1823 | int32_t idx = -1; 1824 | uint r, g, b; 1825 | 1826 | switch (attr[*npar + 1]) { 1827 | case 2: /* direct color in RGB space */ 1828 | if (*npar + 4 >= l) { 1829 | fprintf(stderr, 1830 | "erresc(38): Incorrect number of parameters (%d)\n", 1831 | *npar); 1832 | break; 1833 | } 1834 | r = attr[*npar + 2]; 1835 | g = attr[*npar + 3]; 1836 | b = attr[*npar + 4]; 1837 | *npar += 4; 1838 | if (!BETWEEN(r, 0, 255) || !BETWEEN(g, 0, 255) || !BETWEEN(b, 0, 255)) 1839 | fprintf(stderr, "erresc: bad rgb color (%u,%u,%u)\n", 1840 | r, g, b); 1841 | else 1842 | idx = TRUECOLOR(r, g, b); 1843 | break; 1844 | case 5: /* indexed color */ 1845 | if (*npar + 2 >= l) { 1846 | fprintf(stderr, 1847 | "erresc(38): Incorrect number of parameters (%d)\n", 1848 | *npar); 1849 | break; 1850 | } 1851 | *npar += 2; 1852 | if (!BETWEEN(attr[*npar], 0, 255)) 1853 | fprintf(stderr, "erresc: bad fgcolor %d\n", attr[*npar]); 1854 | else 1855 | idx = attr[*npar]; 1856 | break; 1857 | case 0: /* implemented defined (only foreground) */ 1858 | case 1: /* transparent */ 1859 | case 3: /* direct color in CMY space */ 1860 | case 4: /* direct color in CMYK space */ 1861 | default: 1862 | fprintf(stderr, 1863 | "erresc(38): gfx attr %d unknown\n", attr[*npar]); 1864 | break; 1865 | } 1866 | 1867 | return idx; 1868 | } 1869 | 1870 | void 1871 | tsetattr(int *attr, int l) 1872 | { 1873 | int i; 1874 | int32_t idx; 1875 | 1876 | for (i = 0; i < l; i++) { 1877 | switch (attr[i]) { 1878 | case 0: 1879 | term.c.attr.mode &= ~( 1880 | ATTR_BOLD | 1881 | ATTR_FAINT | 1882 | ATTR_ITALIC | 1883 | ATTR_UNDERLINE | 1884 | ATTR_BLINK | 1885 | ATTR_REVERSE | 1886 | ATTR_INVISIBLE | 1887 | ATTR_STRUCK ); 1888 | term.c.attr.fg = defaultfg; 1889 | term.c.attr.bg = defaultbg; 1890 | break; 1891 | case 1: 1892 | term.c.attr.mode |= ATTR_BOLD; 1893 | break; 1894 | case 2: 1895 | term.c.attr.mode |= ATTR_FAINT; 1896 | break; 1897 | case 3: 1898 | term.c.attr.mode |= ATTR_ITALIC; 1899 | break; 1900 | case 4: 1901 | term.c.attr.mode |= ATTR_UNDERLINE; 1902 | break; 1903 | case 5: /* slow blink */ 1904 | /* FALLTHROUGH */ 1905 | case 6: /* rapid blink */ 1906 | term.c.attr.mode |= ATTR_BLINK; 1907 | break; 1908 | case 7: 1909 | term.c.attr.mode |= ATTR_REVERSE; 1910 | break; 1911 | case 8: 1912 | term.c.attr.mode |= ATTR_INVISIBLE; 1913 | break; 1914 | case 9: 1915 | term.c.attr.mode |= ATTR_STRUCK; 1916 | break; 1917 | case 22: 1918 | term.c.attr.mode &= ~(ATTR_BOLD | ATTR_FAINT); 1919 | break; 1920 | case 23: 1921 | term.c.attr.mode &= ~ATTR_ITALIC; 1922 | break; 1923 | case 24: 1924 | term.c.attr.mode &= ~ATTR_UNDERLINE; 1925 | break; 1926 | case 25: 1927 | term.c.attr.mode &= ~ATTR_BLINK; 1928 | break; 1929 | case 27: 1930 | term.c.attr.mode &= ~ATTR_REVERSE; 1931 | break; 1932 | case 28: 1933 | term.c.attr.mode &= ~ATTR_INVISIBLE; 1934 | break; 1935 | case 29: 1936 | term.c.attr.mode &= ~ATTR_STRUCK; 1937 | break; 1938 | case 38: 1939 | if ((idx = tdefcolor(attr, &i, l)) >= 0) 1940 | term.c.attr.fg = idx; 1941 | break; 1942 | case 39: 1943 | term.c.attr.fg = defaultfg; 1944 | break; 1945 | case 48: 1946 | if ((idx = tdefcolor(attr, &i, l)) >= 0) 1947 | term.c.attr.bg = idx; 1948 | break; 1949 | case 49: 1950 | term.c.attr.bg = defaultbg; 1951 | break; 1952 | default: 1953 | if (BETWEEN(attr[i], 30, 37)) { 1954 | term.c.attr.fg = attr[i] - 30; 1955 | } else if (BETWEEN(attr[i], 40, 47)) { 1956 | term.c.attr.bg = attr[i] - 40; 1957 | } else if (BETWEEN(attr[i], 90, 97)) { 1958 | term.c.attr.fg = attr[i] - 90 + 8; 1959 | } else if (BETWEEN(attr[i], 100, 107)) { 1960 | term.c.attr.bg = attr[i] - 100 + 8; 1961 | } else { 1962 | fprintf(stderr, 1963 | "erresc(default): gfx attr %d unknown\n", 1964 | attr[i]), csidump(); 1965 | } 1966 | break; 1967 | } 1968 | } 1969 | } 1970 | 1971 | void 1972 | tsetscroll(int t, int b) 1973 | { 1974 | int temp; 1975 | 1976 | LIMIT(t, 0, term.row-1); 1977 | LIMIT(b, 0, term.row-1); 1978 | if (t > b) { 1979 | temp = t; 1980 | t = b; 1981 | b = temp; 1982 | } 1983 | term.top = t; 1984 | term.bot = b; 1985 | } 1986 | 1987 | void 1988 | tsetmode(int priv, int set, int *args, int narg) 1989 | { 1990 | int *lim, mode; 1991 | int alt; 1992 | 1993 | for (lim = args + narg; args < lim; ++args) { 1994 | if (priv) { 1995 | switch (*args) { 1996 | case 1: /* DECCKM -- Cursor key */ 1997 | MODBIT(term.mode, set, MODE_APPCURSOR); 1998 | break; 1999 | case 5: /* DECSCNM -- Reverse video */ 2000 | mode = term.mode; 2001 | MODBIT(term.mode, set, MODE_REVERSE); 2002 | if (mode != term.mode) 2003 | redraw(); 2004 | break; 2005 | case 6: /* DECOM -- Origin */ 2006 | MODBIT(term.c.state, set, CURSOR_ORIGIN); 2007 | tmoveato(0, 0); 2008 | break; 2009 | case 7: /* DECAWM -- Auto wrap */ 2010 | MODBIT(term.mode, set, MODE_WRAP); 2011 | break; 2012 | case 0: /* Error (IGNORED) */ 2013 | case 2: /* DECANM -- ANSI/VT52 (IGNORED) */ 2014 | case 3: /* DECCOLM -- Column (IGNORED) */ 2015 | case 4: /* DECSCLM -- Scroll (IGNORED) */ 2016 | case 8: /* DECARM -- Auto repeat (IGNORED) */ 2017 | case 18: /* DECPFF -- Printer feed (IGNORED) */ 2018 | case 19: /* DECPEX -- Printer extent (IGNORED) */ 2019 | case 42: /* DECNRCM -- National characters (IGNORED) */ 2020 | case 12: /* att610 -- Start blinking cursor (IGNORED) */ 2021 | break; 2022 | case 25: /* DECTCEM -- Text Cursor Enable Mode */ 2023 | MODBIT(term.mode, !set, MODE_HIDE); 2024 | break; 2025 | case 9: /* X10 mouse compatibility mode */ 2026 | MODBIT(term.mode, 0, MODE_MOUSE); 2027 | MODBIT(term.mode, set, MODE_MOUSEX10); 2028 | break; 2029 | case 1000: /* 1000: report button press */ 2030 | MODBIT(term.mode, 0, MODE_MOUSE); 2031 | MODBIT(term.mode, set, MODE_MOUSEBTN); 2032 | break; 2033 | case 1002: /* 1002: report motion on button press */ 2034 | MODBIT(term.mode, 0, MODE_MOUSE); 2035 | MODBIT(term.mode, set, MODE_MOUSEMOTION); 2036 | break; 2037 | case 1003: /* 1003: enable all mouse motions */ 2038 | MODBIT(term.mode, 0, MODE_MOUSE); 2039 | MODBIT(term.mode, set, MODE_MOUSEMANY); 2040 | break; 2041 | case 1004: /* 1004: send focus events to tty */ 2042 | MODBIT(term.mode, set, MODE_FOCUS); 2043 | break; 2044 | case 1006: /* 1006: extended reporting mode */ 2045 | MODBIT(term.mode, set, MODE_MOUSESGR); 2046 | break; 2047 | case 1034: 2048 | MODBIT(term.mode, set, MODE_8BIT); 2049 | break; 2050 | case 1049: /* swap screen & set/restore cursor as xterm */ 2051 | if (!allowaltscreen) 2052 | break; 2053 | tcursor((set) ? CURSOR_SAVE : CURSOR_LOAD); 2054 | /* FALLTHROUGH */ 2055 | case 47: /* swap screen */ 2056 | case 1047: 2057 | if (!allowaltscreen) 2058 | break; 2059 | alt = IS_SET(MODE_ALTSCREEN); 2060 | if (alt) { 2061 | tclearregion(0, 0, term.col-1, 2062 | term.row-1); 2063 | } 2064 | if (set ^ alt) /* set is always 1 or 0 */ 2065 | tswapscreen(); 2066 | if (*args != 1049) 2067 | break; 2068 | /* FALLTHROUGH */ 2069 | case 1048: 2070 | tcursor((set) ? CURSOR_SAVE : CURSOR_LOAD); 2071 | break; 2072 | case 2004: /* 2004: bracketed paste mode */ 2073 | MODBIT(term.mode, set, MODE_BRCKTPASTE); 2074 | break; 2075 | /* Not implemented mouse modes. See comments there. */ 2076 | case 1001: /* mouse highlight mode; can hang the 2077 | terminal by design when implemented. */ 2078 | case 1005: /* UTF-8 mouse mode; will confuse 2079 | applications not supporting UTF-8 2080 | and luit. */ 2081 | case 1015: /* urxvt mangled mouse mode; incompatible 2082 | and can be mistaken for other control 2083 | codes. */ 2084 | default: 2085 | fprintf(stderr, 2086 | "erresc: unknown private set/reset mode %d\n", 2087 | *args); 2088 | break; 2089 | } 2090 | } else { 2091 | switch (*args) { 2092 | case 0: /* Error (IGNORED) */ 2093 | break; 2094 | case 2: /* KAM -- keyboard action */ 2095 | MODBIT(term.mode, set, MODE_KBDLOCK); 2096 | break; 2097 | case 4: /* IRM -- Insertion-replacement */ 2098 | MODBIT(term.mode, set, MODE_INSERT); 2099 | break; 2100 | case 12: /* SRM -- Send/Receive */ 2101 | MODBIT(term.mode, !set, MODE_ECHO); 2102 | break; 2103 | case 20: /* LNM -- Linefeed/new line */ 2104 | MODBIT(term.mode, set, MODE_CRLF); 2105 | break; 2106 | default: 2107 | fprintf(stderr, 2108 | "erresc: unknown set/reset mode %d\n", 2109 | *args); 2110 | break; 2111 | } 2112 | } 2113 | } 2114 | } 2115 | 2116 | void 2117 | csihandle(void) 2118 | { 2119 | char buf[40]; 2120 | int len; 2121 | 2122 | switch (csiescseq.mode[0]) { 2123 | default: 2124 | unknown: 2125 | fprintf(stderr, "erresc: unknown csi "); 2126 | csidump(); 2127 | /* die(""); */ 2128 | break; 2129 | case '@': /* ICH -- Insert blank char */ 2130 | DEFAULT(csiescseq.arg[0], 1); 2131 | tinsertblank(csiescseq.arg[0]); 2132 | break; 2133 | case 'A': /* CUU -- Cursor Up */ 2134 | DEFAULT(csiescseq.arg[0], 1); 2135 | tmoveto(term.c.x, term.c.y-csiescseq.arg[0]); 2136 | break; 2137 | case 'B': /* CUD -- Cursor Down */ 2138 | case 'e': /* VPR --Cursor Down */ 2139 | DEFAULT(csiescseq.arg[0], 1); 2140 | tmoveto(term.c.x, term.c.y+csiescseq.arg[0]); 2141 | break; 2142 | case 'i': /* MC -- Media Copy */ 2143 | switch (csiescseq.arg[0]) { 2144 | case 0: 2145 | tdump(); 2146 | break; 2147 | case 1: 2148 | tdumpline(term.c.y); 2149 | break; 2150 | case 2: 2151 | tdumpsel(); 2152 | break; 2153 | case 4: 2154 | term.mode &= ~MODE_PRINT; 2155 | break; 2156 | case 5: 2157 | term.mode |= MODE_PRINT; 2158 | break; 2159 | } 2160 | break; 2161 | case 'c': /* DA -- Device Attributes */ 2162 | if (csiescseq.arg[0] == 0) 2163 | ttywrite(vtiden, sizeof(vtiden) - 1); 2164 | break; 2165 | case 'C': /* CUF -- Cursor Forward */ 2166 | case 'a': /* HPR -- Cursor Forward */ 2167 | DEFAULT(csiescseq.arg[0], 1); 2168 | tmoveto(term.c.x+csiescseq.arg[0], term.c.y); 2169 | break; 2170 | case 'D': /* CUB -- Cursor Backward */ 2171 | DEFAULT(csiescseq.arg[0], 1); 2172 | tmoveto(term.c.x-csiescseq.arg[0], term.c.y); 2173 | break; 2174 | case 'E': /* CNL -- Cursor Down and first col */ 2175 | DEFAULT(csiescseq.arg[0], 1); 2176 | tmoveto(0, term.c.y+csiescseq.arg[0]); 2177 | break; 2178 | case 'F': /* CPL -- Cursor Up and first col */ 2179 | DEFAULT(csiescseq.arg[0], 1); 2180 | tmoveto(0, term.c.y-csiescseq.arg[0]); 2181 | break; 2182 | case 'g': /* TBC -- Tabulation clear */ 2183 | switch (csiescseq.arg[0]) { 2184 | case 0: /* clear current tab stop */ 2185 | term.tabs[term.c.x] = 0; 2186 | break; 2187 | case 3: /* clear all the tabs */ 2188 | memset(term.tabs, 0, term.col * sizeof(*term.tabs)); 2189 | break; 2190 | default: 2191 | goto unknown; 2192 | } 2193 | break; 2194 | case 'G': /* CHA -- Move to */ 2195 | case '`': /* HPA */ 2196 | DEFAULT(csiescseq.arg[0], 1); 2197 | tmoveto(csiescseq.arg[0]-1, term.c.y); 2198 | break; 2199 | case 'H': /* CUP -- Move to */ 2200 | case 'f': /* HVP */ 2201 | DEFAULT(csiescseq.arg[0], 1); 2202 | DEFAULT(csiescseq.arg[1], 1); 2203 | tmoveato(csiescseq.arg[1]-1, csiescseq.arg[0]-1); 2204 | break; 2205 | case 'I': /* CHT -- Cursor Forward Tabulation tab stops */ 2206 | DEFAULT(csiescseq.arg[0], 1); 2207 | tputtab(csiescseq.arg[0]); 2208 | break; 2209 | case 'J': /* ED -- Clear screen */ 2210 | selclear(); 2211 | switch (csiescseq.arg[0]) { 2212 | case 0: /* below */ 2213 | tclearregion(term.c.x, term.c.y, term.col-1, term.c.y); 2214 | if (term.c.y < term.row-1) { 2215 | tclearregion(0, term.c.y+1, term.col-1, 2216 | term.row-1); 2217 | } 2218 | break; 2219 | case 1: /* above */ 2220 | if (term.c.y > 1) 2221 | tclearregion(0, 0, term.col-1, term.c.y-1); 2222 | tclearregion(0, term.c.y, term.c.x, term.c.y); 2223 | break; 2224 | case 2: /* all */ 2225 | tclearregion(0, 0, term.col-1, term.row-1); 2226 | break; 2227 | default: 2228 | goto unknown; 2229 | } 2230 | break; 2231 | case 'K': /* EL -- Clear line */ 2232 | switch (csiescseq.arg[0]) { 2233 | case 0: /* right */ 2234 | tclearregion(term.c.x, term.c.y, term.col-1, 2235 | term.c.y); 2236 | break; 2237 | case 1: /* left */ 2238 | tclearregion(0, term.c.y, term.c.x, term.c.y); 2239 | break; 2240 | case 2: /* all */ 2241 | tclearregion(0, term.c.y, term.col-1, term.c.y); 2242 | break; 2243 | } 2244 | break; 2245 | case 'S': /* SU -- Scroll line up */ 2246 | DEFAULT(csiescseq.arg[0], 1); 2247 | tscrollup(term.top, csiescseq.arg[0]); 2248 | break; 2249 | case 'T': /* SD -- Scroll line down */ 2250 | DEFAULT(csiescseq.arg[0], 1); 2251 | tscrolldown(term.top, csiescseq.arg[0]); 2252 | break; 2253 | case 'L': /* IL -- Insert blank lines */ 2254 | DEFAULT(csiescseq.arg[0], 1); 2255 | tinsertblankline(csiescseq.arg[0]); 2256 | break; 2257 | case 'l': /* RM -- Reset Mode */ 2258 | tsetmode(csiescseq.priv, 0, csiescseq.arg, csiescseq.narg); 2259 | break; 2260 | case 'M': /* DL -- Delete lines */ 2261 | DEFAULT(csiescseq.arg[0], 1); 2262 | tdeleteline(csiescseq.arg[0]); 2263 | break; 2264 | case 'X': /* ECH -- Erase char */ 2265 | DEFAULT(csiescseq.arg[0], 1); 2266 | tclearregion(term.c.x, term.c.y, 2267 | term.c.x + csiescseq.arg[0] - 1, term.c.y); 2268 | break; 2269 | case 'P': /* DCH -- Delete char */ 2270 | DEFAULT(csiescseq.arg[0], 1); 2271 | tdeletechar(csiescseq.arg[0]); 2272 | break; 2273 | case 'Z': /* CBT -- Cursor Backward Tabulation tab stops */ 2274 | DEFAULT(csiescseq.arg[0], 1); 2275 | tputtab(-csiescseq.arg[0]); 2276 | break; 2277 | case 'd': /* VPA -- Move to */ 2278 | DEFAULT(csiescseq.arg[0], 1); 2279 | tmoveato(term.c.x, csiescseq.arg[0]-1); 2280 | break; 2281 | case 'h': /* SM -- Set terminal mode */ 2282 | tsetmode(csiescseq.priv, 1, csiescseq.arg, csiescseq.narg); 2283 | break; 2284 | case 'm': /* SGR -- Terminal attribute (color) */ 2285 | tsetattr(csiescseq.arg, csiescseq.narg); 2286 | break; 2287 | case 'n': /* DSR – Device Status Report (cursor position) */ 2288 | if (csiescseq.arg[0] == 6) { 2289 | len = snprintf(buf, sizeof(buf),"\033[%i;%iR", 2290 | term.c.y+1, term.c.x+1); 2291 | ttywrite(buf, len); 2292 | } 2293 | break; 2294 | case 'r': /* DECSTBM -- Set Scrolling Region */ 2295 | if (csiescseq.priv) { 2296 | goto unknown; 2297 | } else { 2298 | DEFAULT(csiescseq.arg[0], 1); 2299 | DEFAULT(csiescseq.arg[1], term.row); 2300 | tsetscroll(csiescseq.arg[0]-1, csiescseq.arg[1]-1); 2301 | tmoveato(0, 0); 2302 | } 2303 | break; 2304 | case 's': /* DECSC -- Save cursor position (ANSI.SYS) */ 2305 | tcursor(CURSOR_SAVE); 2306 | break; 2307 | case 'u': /* DECRC -- Restore cursor position (ANSI.SYS) */ 2308 | tcursor(CURSOR_LOAD); 2309 | break; 2310 | case ' ': 2311 | switch (csiescseq.mode[1]) { 2312 | case 'q': /* DECSCUSR -- Set Cursor Style */ 2313 | DEFAULT(csiescseq.arg[0], 1); 2314 | if (!BETWEEN(csiescseq.arg[0], 0, 6)) { 2315 | goto unknown; 2316 | } 2317 | wl.cursor = csiescseq.arg[0]; 2318 | break; 2319 | default: 2320 | goto unknown; 2321 | } 2322 | break; 2323 | } 2324 | } 2325 | 2326 | void 2327 | csidump(void) 2328 | { 2329 | int i; 2330 | uint c; 2331 | 2332 | fprintf(stderr, "ESC["); 2333 | for (i = 0; i < csiescseq.len; i++) { 2334 | c = csiescseq.buf[i] & 0xff; 2335 | if (isprint(c)) { 2336 | putc(c, stderr); 2337 | } else if (c == '\n') { 2338 | fprintf(stderr, "(\\n)"); 2339 | } else if (c == '\r') { 2340 | fprintf(stderr, "(\\r)"); 2341 | } else if (c == 0x1b) { 2342 | fprintf(stderr, "(\\e)"); 2343 | } else { 2344 | fprintf(stderr, "(%02x)", c); 2345 | } 2346 | } 2347 | putc('\n', stderr); 2348 | } 2349 | 2350 | void 2351 | csireset(void) 2352 | { 2353 | memset(&csiescseq, 0, sizeof(csiescseq)); 2354 | } 2355 | 2356 | void 2357 | strhandle(void) 2358 | { 2359 | char *p = NULL; 2360 | int j, narg, par; 2361 | 2362 | term.esc &= ~(ESC_STR_END|ESC_STR); 2363 | strparse(); 2364 | par = (narg = strescseq.narg) ? atoi(strescseq.args[0]) : 0; 2365 | 2366 | switch (strescseq.type) { 2367 | case ']': /* OSC -- Operating System Command */ 2368 | switch (par) { 2369 | case 0: 2370 | case 1: 2371 | case 2: 2372 | if (narg > 1) 2373 | wlsettitle(strescseq.args[1]); 2374 | return; 2375 | case 4: /* color set */ 2376 | if (narg < 3) 2377 | break; 2378 | p = strescseq.args[2]; 2379 | /* FALLTHROUGH */ 2380 | case 104: /* color reset, here p = NULL */ 2381 | j = (narg > 1) ? atoi(strescseq.args[1]) : -1; 2382 | if (wlsetcolorname(j, p)) { 2383 | fprintf(stderr, "erresc: invalid color %s\n", p); 2384 | } else { 2385 | /* 2386 | * TODO if defaultbg color is changed, borders 2387 | * are dirty 2388 | */ 2389 | redraw(); 2390 | } 2391 | return; 2392 | } 2393 | break; 2394 | case 'k': /* old title set compatibility */ 2395 | wlsettitle(strescseq.args[0]); 2396 | return; 2397 | case 'P': /* DCS -- Device Control String */ 2398 | term.mode |= ESC_DCS; 2399 | case '_': /* APC -- Application Program Command */ 2400 | case '^': /* PM -- Privacy Message */ 2401 | return; 2402 | } 2403 | 2404 | fprintf(stderr, "erresc: unknown str "); 2405 | strdump(); 2406 | } 2407 | 2408 | void 2409 | strparse(void) 2410 | { 2411 | int c; 2412 | char *p = strescseq.buf; 2413 | 2414 | strescseq.narg = 0; 2415 | strescseq.buf[strescseq.len] = '\0'; 2416 | 2417 | if (*p == '\0') 2418 | return; 2419 | 2420 | while (strescseq.narg < STR_ARG_SIZ) { 2421 | strescseq.args[strescseq.narg++] = p; 2422 | while ((c = *p) != ';' && c != '\0') 2423 | ++p; 2424 | if (c == '\0') 2425 | return; 2426 | *p++ = '\0'; 2427 | } 2428 | } 2429 | 2430 | void 2431 | strdump(void) 2432 | { 2433 | int i; 2434 | uint c; 2435 | 2436 | fprintf(stderr, "ESC%c", strescseq.type); 2437 | for (i = 0; i < strescseq.len; i++) { 2438 | c = strescseq.buf[i] & 0xff; 2439 | if (c == '\0') { 2440 | putc('\n', stderr); 2441 | return; 2442 | } else if (isprint(c)) { 2443 | putc(c, stderr); 2444 | } else if (c == '\n') { 2445 | fprintf(stderr, "(\\n)"); 2446 | } else if (c == '\r') { 2447 | fprintf(stderr, "(\\r)"); 2448 | } else if (c == 0x1b) { 2449 | fprintf(stderr, "(\\e)"); 2450 | } else { 2451 | fprintf(stderr, "(%02x)", c); 2452 | } 2453 | } 2454 | fprintf(stderr, "ESC\\\n"); 2455 | } 2456 | 2457 | void 2458 | strreset(void) 2459 | { 2460 | memset(&strescseq, 0, sizeof(strescseq)); 2461 | } 2462 | 2463 | void 2464 | sendbreak(const Arg *arg) 2465 | { 2466 | if (tcsendbreak(cmdfd, 0)) 2467 | perror("Error sending break"); 2468 | } 2469 | 2470 | void 2471 | tprinter(char *s, size_t len) 2472 | { 2473 | if (iofd != -1 && xwrite(iofd, s, len) < 0) { 2474 | fprintf(stderr, "Error writing in %s:%s\n", 2475 | opt_io, strerror(errno)); 2476 | close(iofd); 2477 | iofd = -1; 2478 | } 2479 | } 2480 | 2481 | void 2482 | iso14755(const Arg *arg) 2483 | { 2484 | FILE *p; 2485 | char *us, *e, codepoint[9], uc[UTF_SIZ]; 2486 | unsigned long utf32; 2487 | 2488 | if (!(p = popen(ISO14755CMD, "r"))) 2489 | return; 2490 | 2491 | us = fgets(codepoint, sizeof(codepoint), p); 2492 | pclose(p); 2493 | 2494 | if (!us || *us == '\0' || *us == '-' || strlen(us) > 7) 2495 | return; 2496 | if ((utf32 = strtoul(us, &e, 16)) == ULONG_MAX || 2497 | (*e != '\n' && *e != '\0')) 2498 | return; 2499 | 2500 | ttysend(uc, utf8encode(utf32, uc)); 2501 | } 2502 | 2503 | void 2504 | toggleprinter(const Arg *arg) 2505 | { 2506 | term.mode ^= MODE_PRINT; 2507 | } 2508 | 2509 | void 2510 | printscreen(const Arg *arg) 2511 | { 2512 | tdump(); 2513 | } 2514 | 2515 | void 2516 | printsel(const Arg *arg) 2517 | { 2518 | tdumpsel(); 2519 | } 2520 | 2521 | void 2522 | tdumpsel(void) 2523 | { 2524 | char *ptr; 2525 | 2526 | if ((ptr = getsel())) { 2527 | tprinter(ptr, strlen(ptr)); 2528 | free(ptr); 2529 | } 2530 | } 2531 | 2532 | void 2533 | tdumpline(int n) 2534 | { 2535 | char buf[UTF_SIZ]; 2536 | Glyph *bp, *end; 2537 | 2538 | bp = &term.line[n][0]; 2539 | end = &bp[MIN(tlinelen(n), term.col) - 1]; 2540 | if (bp != end || bp->u != ' ') { 2541 | for ( ;bp <= end; ++bp) 2542 | tprinter(buf, utf8encode(bp->u, buf)); 2543 | } 2544 | tprinter("\n", 1); 2545 | } 2546 | 2547 | void 2548 | tdump(void) 2549 | { 2550 | int i; 2551 | 2552 | for (i = 0; i < term.row; ++i) 2553 | tdumpline(i); 2554 | } 2555 | 2556 | void 2557 | tputtab(int n) 2558 | { 2559 | uint x = term.c.x; 2560 | 2561 | if (n > 0) { 2562 | while (x < term.col && n--) 2563 | for (++x; x < term.col && !term.tabs[x]; ++x) 2564 | /* nothing */ ; 2565 | } else if (n < 0) { 2566 | while (x > 0 && n++) 2567 | for (--x; x > 0 && !term.tabs[x]; --x) 2568 | /* nothing */ ; 2569 | } 2570 | term.c.x = LIMIT(x, 0, term.col-1); 2571 | } 2572 | 2573 | void 2574 | techo(Rune u) 2575 | { 2576 | if (ISCONTROL(u)) { /* control code */ 2577 | if (u & 0x80) { 2578 | u &= 0x7f; 2579 | tputc('^'); 2580 | tputc('['); 2581 | } else if (u != '\n' && u != '\r' && u != '\t') { 2582 | u ^= 0x40; 2583 | tputc('^'); 2584 | } 2585 | } 2586 | tputc(u); 2587 | needdraw = true; 2588 | } 2589 | 2590 | void 2591 | tdefutf8(char ascii) 2592 | { 2593 | if (ascii == 'G') 2594 | term.mode |= MODE_UTF8; 2595 | else if (ascii == '@') 2596 | term.mode &= ~MODE_UTF8; 2597 | } 2598 | 2599 | void 2600 | tdeftran(char ascii) 2601 | { 2602 | static char cs[] = "0B"; 2603 | static int vcs[] = {CS_GRAPHIC0, CS_USA}; 2604 | char *p; 2605 | 2606 | if ((p = strchr(cs, ascii)) == NULL) { 2607 | fprintf(stderr, "esc unhandled charset: ESC ( %c\n", ascii); 2608 | } else { 2609 | term.trantbl[term.icharset] = vcs[p - cs]; 2610 | } 2611 | } 2612 | 2613 | void 2614 | tdectest(char c) 2615 | { 2616 | int x, y; 2617 | 2618 | if (c == '8') { /* DEC screen alignment test. */ 2619 | for (x = 0; x < term.col; ++x) { 2620 | for (y = 0; y < term.row; ++y) 2621 | tsetchar('E', &term.c.attr, x, y); 2622 | } 2623 | } 2624 | } 2625 | 2626 | void 2627 | tstrsequence(uchar c) 2628 | { 2629 | strreset(); 2630 | 2631 | switch (c) { 2632 | case 0x90: /* DCS -- Device Control String */ 2633 | c = 'P'; 2634 | term.esc |= ESC_DCS; 2635 | break; 2636 | case 0x9f: /* APC -- Application Program Command */ 2637 | c = '_'; 2638 | break; 2639 | case 0x9e: /* PM -- Privacy Message */ 2640 | c = '^'; 2641 | break; 2642 | case 0x9d: /* OSC -- Operating System Command */ 2643 | c = ']'; 2644 | break; 2645 | } 2646 | strescseq.type = c; 2647 | term.esc |= ESC_STR; 2648 | } 2649 | 2650 | void 2651 | tcontrolcode(uchar ascii) 2652 | { 2653 | switch (ascii) { 2654 | case '\t': /* HT */ 2655 | tputtab(1); 2656 | return; 2657 | case '\b': /* BS */ 2658 | tmoveto(term.c.x-1, term.c.y); 2659 | return; 2660 | case '\r': /* CR */ 2661 | tmoveto(0, term.c.y); 2662 | return; 2663 | case '\f': /* LF */ 2664 | case '\v': /* VT */ 2665 | case '\n': /* LF */ 2666 | /* go to first col if the mode is set */ 2667 | tnewline(IS_SET(MODE_CRLF)); 2668 | return; 2669 | case '\a': /* BEL */ 2670 | if (term.esc & ESC_STR_END) { 2671 | /* backwards compatibility to xterm */ 2672 | strhandle(); 2673 | } else { 2674 | if (!(wl.state & WIN_FOCUSED)) 2675 | wlseturgency(1); 2676 | /* XXX: No bell on wayland 2677 | * if (bellvolume) 2678 | * XkbBell(xw.dpy, xw.win, bellvolume, (Atom)NULL); 2679 | */ 2680 | } 2681 | break; 2682 | case '\033': /* ESC */ 2683 | csireset(); 2684 | term.esc &= ~(ESC_CSI|ESC_ALTCHARSET|ESC_TEST); 2685 | term.esc |= ESC_START; 2686 | return; 2687 | case '\016': /* SO (LS1 -- Locking shift 1) */ 2688 | case '\017': /* SI (LS0 -- Locking shift 0) */ 2689 | term.charset = 1 - (ascii - '\016'); 2690 | return; 2691 | case '\032': /* SUB */ 2692 | tsetchar('?', &term.c.attr, term.c.x, term.c.y); 2693 | case '\030': /* CAN */ 2694 | csireset(); 2695 | break; 2696 | case '\005': /* ENQ (IGNORED) */ 2697 | case '\000': /* NUL (IGNORED) */ 2698 | case '\021': /* XON (IGNORED) */ 2699 | case '\023': /* XOFF (IGNORED) */ 2700 | case 0177: /* DEL (IGNORED) */ 2701 | return; 2702 | case 0x80: /* TODO: PAD */ 2703 | case 0x81: /* TODO: HOP */ 2704 | case 0x82: /* TODO: BPH */ 2705 | case 0x83: /* TODO: NBH */ 2706 | case 0x84: /* TODO: IND */ 2707 | break; 2708 | case 0x85: /* NEL -- Next line */ 2709 | tnewline(1); /* always go to first col */ 2710 | break; 2711 | case 0x86: /* TODO: SSA */ 2712 | case 0x87: /* TODO: ESA */ 2713 | break; 2714 | case 0x88: /* HTS -- Horizontal tab stop */ 2715 | term.tabs[term.c.x] = 1; 2716 | break; 2717 | case 0x89: /* TODO: HTJ */ 2718 | case 0x8a: /* TODO: VTS */ 2719 | case 0x8b: /* TODO: PLD */ 2720 | case 0x8c: /* TODO: PLU */ 2721 | case 0x8d: /* TODO: RI */ 2722 | case 0x8e: /* TODO: SS2 */ 2723 | case 0x8f: /* TODO: SS3 */ 2724 | case 0x91: /* TODO: PU1 */ 2725 | case 0x92: /* TODO: PU2 */ 2726 | case 0x93: /* TODO: STS */ 2727 | case 0x94: /* TODO: CCH */ 2728 | case 0x95: /* TODO: MW */ 2729 | case 0x96: /* TODO: SPA */ 2730 | case 0x97: /* TODO: EPA */ 2731 | case 0x98: /* TODO: SOS */ 2732 | case 0x99: /* TODO: SGCI */ 2733 | break; 2734 | case 0x9a: /* DECID -- Identify Terminal */ 2735 | ttywrite(vtiden, sizeof(vtiden) - 1); 2736 | break; 2737 | case 0x9b: /* TODO: CSI */ 2738 | case 0x9c: /* TODO: ST */ 2739 | break; 2740 | case 0x90: /* DCS -- Device Control String */ 2741 | case 0x9d: /* OSC -- Operating System Command */ 2742 | case 0x9e: /* PM -- Privacy Message */ 2743 | case 0x9f: /* APC -- Application Program Command */ 2744 | tstrsequence(ascii); 2745 | return; 2746 | } 2747 | /* only CAN, SUB, \a and C1 chars interrupt a sequence */ 2748 | term.esc &= ~(ESC_STR_END|ESC_STR); 2749 | } 2750 | 2751 | /* 2752 | * returns 1 when the sequence is finished and it hasn't to read 2753 | * more characters for this sequence, otherwise 0 2754 | */ 2755 | int 2756 | eschandle(uchar ascii) 2757 | { 2758 | switch (ascii) { 2759 | case '[': 2760 | term.esc |= ESC_CSI; 2761 | return 0; 2762 | case '#': 2763 | term.esc |= ESC_TEST; 2764 | return 0; 2765 | case '%': 2766 | term.esc |= ESC_UTF8; 2767 | return 0; 2768 | case 'P': /* DCS -- Device Control String */ 2769 | case '_': /* APC -- Application Program Command */ 2770 | case '^': /* PM -- Privacy Message */ 2771 | case ']': /* OSC -- Operating System Command */ 2772 | case 'k': /* old title set compatibility */ 2773 | tstrsequence(ascii); 2774 | return 0; 2775 | case 'n': /* LS2 -- Locking shift 2 */ 2776 | case 'o': /* LS3 -- Locking shift 3 */ 2777 | term.charset = 2 + (ascii - 'n'); 2778 | break; 2779 | case '(': /* GZD4 -- set primary charset G0 */ 2780 | case ')': /* G1D4 -- set secondary charset G1 */ 2781 | case '*': /* G2D4 -- set tertiary charset G2 */ 2782 | case '+': /* G3D4 -- set quaternary charset G3 */ 2783 | term.icharset = ascii - '('; 2784 | term.esc |= ESC_ALTCHARSET; 2785 | return 0; 2786 | case 'D': /* IND -- Linefeed */ 2787 | if (term.c.y == term.bot) { 2788 | tscrollup(term.top, 1); 2789 | } else { 2790 | tmoveto(term.c.x, term.c.y+1); 2791 | } 2792 | break; 2793 | case 'E': /* NEL -- Next line */ 2794 | tnewline(1); /* always go to first col */ 2795 | break; 2796 | case 'H': /* HTS -- Horizontal tab stop */ 2797 | term.tabs[term.c.x] = 1; 2798 | break; 2799 | case 'M': /* RI -- Reverse index */ 2800 | if (term.c.y == term.top) { 2801 | tscrolldown(term.top, 1); 2802 | } else { 2803 | tmoveto(term.c.x, term.c.y-1); 2804 | } 2805 | break; 2806 | case 'Z': /* DECID -- Identify Terminal */ 2807 | ttywrite(vtiden, sizeof(vtiden) - 1); 2808 | break; 2809 | case 'c': /* RIS -- Reset to inital state */ 2810 | treset(); 2811 | wlresettitle(); 2812 | wlloadcols(); 2813 | break; 2814 | case '=': /* DECPAM -- Application keypad */ 2815 | term.mode |= MODE_APPKEYPAD; 2816 | break; 2817 | case '>': /* DECPNM -- Normal keypad */ 2818 | term.mode &= ~MODE_APPKEYPAD; 2819 | break; 2820 | case '7': /* DECSC -- Save Cursor */ 2821 | tcursor(CURSOR_SAVE); 2822 | break; 2823 | case '8': /* DECRC -- Restore Cursor */ 2824 | tcursor(CURSOR_LOAD); 2825 | break; 2826 | case '\\': /* ST -- String Terminator */ 2827 | if (term.esc & ESC_STR_END) 2828 | strhandle(); 2829 | break; 2830 | default: 2831 | fprintf(stderr, "erresc: unknown sequence ESC 0x%02X '%c'\n", 2832 | (uchar) ascii, isprint(ascii)? ascii:'.'); 2833 | break; 2834 | } 2835 | return 1; 2836 | } 2837 | 2838 | void 2839 | tputc(Rune u) 2840 | { 2841 | char c[UTF_SIZ]; 2842 | int control; 2843 | int width, len; 2844 | Glyph *gp; 2845 | 2846 | control = ISCONTROL(u); 2847 | if (!IS_SET(MODE_UTF8) && !IS_SET(MODE_SIXEL)) { 2848 | c[0] = u; 2849 | width = len = 1; 2850 | } else { 2851 | len = utf8encode(u, c); 2852 | if (!control && (width = wcwidth(u)) == -1) { 2853 | memcpy(c, "\357\277\275", 4); /* UTF_INVALID */ 2854 | width = 1; 2855 | } 2856 | } 2857 | 2858 | if (IS_SET(MODE_PRINT)) 2859 | tprinter(c, len); 2860 | 2861 | /* 2862 | * STR sequence must be checked before anything else 2863 | * because it uses all following characters until it 2864 | * receives a ESC, a SUB, a ST or any other C1 control 2865 | * character. 2866 | */ 2867 | if (term.esc & ESC_STR) { 2868 | if (u == '\a' || u == 030 || u == 032 || u == 033 || 2869 | ISCONTROLC1(u)) { 2870 | term.esc &= ~(ESC_START|ESC_STR|ESC_DCS); 2871 | if (IS_SET(MODE_SIXEL)) { 2872 | /* TODO: render sixel */; 2873 | term.mode &= ~MODE_SIXEL; 2874 | return; 2875 | } 2876 | term.esc |= ESC_STR_END; 2877 | goto check_control_code; 2878 | } 2879 | 2880 | 2881 | if (IS_SET(MODE_SIXEL)) { 2882 | /* TODO: implement sixel mode */ 2883 | return; 2884 | } 2885 | if (term.esc&ESC_DCS && strescseq.len == 0 && u == 'q') 2886 | term.mode |= MODE_SIXEL; 2887 | 2888 | if (strescseq.len+len >= sizeof(strescseq.buf)-1) { 2889 | /* 2890 | * Here is a bug in terminals. If the user never sends 2891 | * some code to stop the str or esc command, then st 2892 | * will stop responding. But this is better than 2893 | * silently failing with unknown characters. At least 2894 | * then users will report back. 2895 | * 2896 | * In the case users ever get fixed, here is the code: 2897 | */ 2898 | /* 2899 | * term.esc = 0; 2900 | * strhandle(); 2901 | */ 2902 | return; 2903 | } 2904 | 2905 | memmove(&strescseq.buf[strescseq.len], c, len); 2906 | strescseq.len += len; 2907 | return; 2908 | } 2909 | 2910 | check_control_code: 2911 | /* 2912 | * Actions of control codes must be performed as soon they arrive 2913 | * because they can be embedded inside a control sequence, and 2914 | * they must not cause conflicts with sequences. 2915 | */ 2916 | if (control) { 2917 | tcontrolcode(u); 2918 | /* 2919 | * control codes are not shown ever 2920 | */ 2921 | return; 2922 | } else if (term.esc & ESC_START) { 2923 | if (term.esc & ESC_CSI) { 2924 | csiescseq.buf[csiescseq.len++] = u; 2925 | if (BETWEEN(u, 0x40, 0x7E) 2926 | || csiescseq.len >= \ 2927 | sizeof(csiescseq.buf)-1) { 2928 | term.esc = 0; 2929 | csiparse(); 2930 | csihandle(); 2931 | } 2932 | return; 2933 | } else if (term.esc & ESC_UTF8) { 2934 | tdefutf8(u); 2935 | } else if (term.esc & ESC_ALTCHARSET) { 2936 | tdeftran(u); 2937 | } else if (term.esc & ESC_TEST) { 2938 | tdectest(u); 2939 | } else { 2940 | if (!eschandle(u)) 2941 | return; 2942 | /* sequence already finished */ 2943 | } 2944 | term.esc = 0; 2945 | /* 2946 | * All characters which form part of a sequence are not 2947 | * printed 2948 | */ 2949 | return; 2950 | } 2951 | if (sel.ob.x != -1 && BETWEEN(term.c.y, sel.ob.y, sel.oe.y)) 2952 | selclear(); 2953 | 2954 | gp = &term.line[term.c.y][term.c.x]; 2955 | if (IS_SET(MODE_WRAP) && (term.c.state & CURSOR_WRAPNEXT)) { 2956 | gp->mode |= ATTR_WRAP; 2957 | tnewline(1); 2958 | gp = &term.line[term.c.y][term.c.x]; 2959 | } 2960 | 2961 | if (IS_SET(MODE_INSERT) && term.c.x+width < term.col) 2962 | memmove(gp+width, gp, (term.col - term.c.x - width) * sizeof(Glyph)); 2963 | 2964 | if (term.c.x+width > term.col) { 2965 | tnewline(1); 2966 | gp = &term.line[term.c.y][term.c.x]; 2967 | } 2968 | 2969 | tsetchar(u, &term.c.attr, term.c.x, term.c.y); 2970 | 2971 | if (width == 2) { 2972 | gp->mode |= ATTR_WIDE; 2973 | if (term.c.x+1 < term.col) { 2974 | gp[1].u = '\0'; 2975 | gp[1].mode = ATTR_WDUMMY; 2976 | } 2977 | } 2978 | if (term.c.x+width < term.col) { 2979 | tmoveto(term.c.x+width, term.c.y); 2980 | } else { 2981 | term.c.state |= CURSOR_WRAPNEXT; 2982 | } 2983 | } 2984 | 2985 | void 2986 | tresize(int col, int row) 2987 | { 2988 | int i; 2989 | int minrow = MIN(row, term.row); 2990 | int mincol = MIN(col, term.col); 2991 | int *bp; 2992 | TCursor c; 2993 | 2994 | if (col < 1 || row < 1) { 2995 | fprintf(stderr, 2996 | "tresize: error resizing to %dx%d\n", col, row); 2997 | return; 2998 | } 2999 | 3000 | /* 3001 | * slide screen to keep cursor where we expect it - 3002 | * tscrollup would work here, but we can optimize to 3003 | * memmove because we're freeing the earlier lines 3004 | */ 3005 | for (i = 0; i <= term.c.y - row; i++) { 3006 | free(term.line[i]); 3007 | free(term.alt[i]); 3008 | } 3009 | /* ensure that both src and dst are not NULL */ 3010 | if (i > 0) { 3011 | memmove(term.line, term.line + i, row * sizeof(Line)); 3012 | memmove(term.alt, term.alt + i, row * sizeof(Line)); 3013 | } 3014 | for (i += row; i < term.row; i++) { 3015 | free(term.line[i]); 3016 | free(term.alt[i]); 3017 | } 3018 | 3019 | /* resize to new height */ 3020 | term.line = xrealloc(term.line, row * sizeof(Line)); 3021 | term.alt = xrealloc(term.alt, row * sizeof(Line)); 3022 | term.dirty = xrealloc(term.dirty, row * sizeof(*term.dirty)); 3023 | term.tabs = xrealloc(term.tabs, col * sizeof(*term.tabs)); 3024 | 3025 | /* resize each row to new width, zero-pad if needed */ 3026 | for (i = 0; i < minrow; i++) { 3027 | term.line[i] = xrealloc(term.line[i], col * sizeof(Glyph)); 3028 | term.alt[i] = xrealloc(term.alt[i], col * sizeof(Glyph)); 3029 | } 3030 | 3031 | /* allocate any new rows */ 3032 | for (/* i == minrow */; i < row; i++) { 3033 | term.line[i] = xmalloc(col * sizeof(Glyph)); 3034 | term.alt[i] = xmalloc(col * sizeof(Glyph)); 3035 | } 3036 | if (col > term.col) { 3037 | bp = term.tabs + term.col; 3038 | 3039 | memset(bp, 0, sizeof(*term.tabs) * (col - term.col)); 3040 | while (--bp > term.tabs && !*bp) 3041 | /* nothing */ ; 3042 | for (bp += tabspaces; bp < term.tabs + col; bp += tabspaces) 3043 | *bp = 1; 3044 | } 3045 | /* update terminal size */ 3046 | term.col = col; 3047 | term.row = row; 3048 | /* reset scrolling region */ 3049 | tsetscroll(0, row-1); 3050 | /* make use of the LIMIT in tmoveto */ 3051 | tmoveto(term.c.x, term.c.y); 3052 | /* Clearing both screens (it makes dirty all lines) */ 3053 | c = term.c; 3054 | for (i = 0; i < 2; i++) { 3055 | if (mincol < col && 0 < minrow) { 3056 | tclearregion(mincol, 0, col - 1, minrow - 1); 3057 | } 3058 | if (0 < col && minrow < row) { 3059 | tclearregion(0, minrow, col - 1, row - 1); 3060 | } 3061 | tswapscreen(); 3062 | tcursor(CURSOR_LOAD); 3063 | } 3064 | term.c = c; 3065 | } 3066 | 3067 | void 3068 | wlresize(int col, int row) 3069 | { 3070 | union wld_object object; 3071 | 3072 | wl.tw = MAX(1, col * wl.cw); 3073 | wl.th = MAX(1, row * wl.ch); 3074 | 3075 | wld.oldbuffer = wld.buffer; 3076 | wld.buffer = wld_create_buffer(wld.ctx, wl.w, wl.h, 3077 | WLD_FORMAT_XRGB8888, 0); 3078 | wld_export(wld.buffer, WLD_WAYLAND_OBJECT_BUFFER, &object); 3079 | wl.buffer = object.ptr; 3080 | } 3081 | 3082 | uchar 3083 | sixd_to_8bit(int x) 3084 | { 3085 | return x == 0 ? 0 : 0x37 + 0x28 * x; 3086 | } 3087 | 3088 | int 3089 | wlloadcolor(int i, const char *name, uint32_t *color) 3090 | { 3091 | if (!name) { 3092 | if (BETWEEN(i, 16, 255)) { /* 256 color */ 3093 | if (i < 6*6*6+16) { /* same colors as xterm */ 3094 | *color = 0xff << 24 | sixd_to_8bit(((i-16)/36)%6) << 16 3095 | | sixd_to_8bit(((i-16)/6)%6) << 8 3096 | | sixd_to_8bit(((i-16)/1)%6); 3097 | } else { /* greyscale */ 3098 | *color = 0xff << 24 | (0x8 + 0xa * (i-(6*6*6+16))) * 0x10101; 3099 | } 3100 | return true; 3101 | } else 3102 | name = colorname[i]; 3103 | } 3104 | 3105 | return wld_lookup_named_color(name, color); 3106 | } 3107 | 3108 | void 3109 | wlloadcols(void) 3110 | { 3111 | int i; 3112 | 3113 | for (i = 0; i < LEN(dc.col); i++) 3114 | if (!wlloadcolor(i, NULL, &dc.col[i])) { 3115 | if (colorname[i]) 3116 | die("Could not allocate color '%s'\n", colorname[i]); 3117 | else 3118 | die("Could not allocate color %d\n", i); 3119 | } 3120 | } 3121 | 3122 | int 3123 | wlsetcolorname(int x, const char *name) 3124 | { 3125 | uint32_t color; 3126 | 3127 | if (!BETWEEN(x, 0, LEN(dc.col))) 3128 | return 1; 3129 | 3130 | if (!wlloadcolor(x, name, &color)) 3131 | return 1; 3132 | 3133 | dc.col[x] = color; 3134 | 3135 | return 0; 3136 | } 3137 | 3138 | static void wlloadcursor(void) 3139 | { 3140 | char *names[] = { mouseshape, "xterm", "ibeam", "text" }; 3141 | int i; 3142 | 3143 | cursor.theme = wl_cursor_theme_load(NULL, 32, wl.shm); 3144 | 3145 | for (i = 0; !cursor.cursor && i < LEN(names); i++) 3146 | cursor.cursor = wl_cursor_theme_get_cursor(cursor.theme, names[i]); 3147 | 3148 | cursor.surface = wl_compositor_create_surface(wl.cmp); 3149 | } 3150 | 3151 | /* 3152 | * Absolute coordinates. 3153 | */ 3154 | void 3155 | wlclear(int x1, int y1, int x2, int y2) 3156 | { 3157 | uint32_t color = dc.col[IS_SET(MODE_REVERSE) ? defaultfg : defaultbg]; 3158 | 3159 | wld_fill_rectangle(wld.renderer, color, x1, y1, x2 - x1, y2 - y1); 3160 | } 3161 | 3162 | int 3163 | wlloadfont(Font *f, FcPattern *pattern) 3164 | { 3165 | FcPattern *configured; 3166 | FcPattern *match; 3167 | FcResult result; 3168 | struct wld_extents extents; 3169 | int wantattr, haveattr; 3170 | 3171 | /* 3172 | * Manually configure instead of calling XftMatchFont 3173 | * so that we can use the configured pattern for 3174 | * "missing glyph" lookups. 3175 | */ 3176 | configured = FcPatternDuplicate(pattern); 3177 | if (!configured) 3178 | return 1; 3179 | 3180 | FcConfigSubstitute(NULL, configured, FcMatchPattern); 3181 | FcDefaultSubstitute(configured); 3182 | 3183 | match = FcFontMatch(NULL, configured, &result); 3184 | if (!match) { 3185 | FcPatternDestroy(configured); 3186 | return 1; 3187 | } 3188 | 3189 | if (!(f->match = wld_font_open_pattern(wld.fontctx, match))) { 3190 | FcPatternDestroy(configured); 3191 | FcPatternDestroy(match); 3192 | return 1; 3193 | } 3194 | 3195 | if ((FcPatternGetInteger(pattern, "slant", 0, &wantattr) == 3196 | FcResultMatch)) { 3197 | /* 3198 | * Check if xft was unable to find a font with the appropriate 3199 | * slant but gave us one anyway. Try to mitigate. 3200 | */ 3201 | if ((FcPatternGetInteger(match, "slant", 0, 3202 | &haveattr) != FcResultMatch) || haveattr < wantattr) { 3203 | f->badslant = 1; 3204 | fputs("st: font slant does not match\n", stderr); 3205 | } 3206 | } 3207 | 3208 | if ((FcPatternGetInteger(pattern, "weight", 0, &wantattr) == 3209 | FcResultMatch)) { 3210 | if ((FcPatternGetInteger(match, "weight", 0, 3211 | &haveattr) != FcResultMatch) || haveattr != wantattr) { 3212 | f->badweight = 1; 3213 | fputs("st: font weight does not match\n", stderr); 3214 | } 3215 | } 3216 | 3217 | 3218 | wld_font_text_extents(f->match, ascii_printable, &extents); 3219 | 3220 | f->set = NULL; 3221 | f->pattern = configured; 3222 | 3223 | f->ascent = f->match->ascent; 3224 | f->descent = f->match->descent; 3225 | f->lbearing = 0; 3226 | f->rbearing = f->match->max_advance; 3227 | 3228 | f->height = f->ascent + f->descent; 3229 | f->width = DIVCEIL(extents.advance, strlen(ascii_printable)); 3230 | 3231 | return 0; 3232 | } 3233 | 3234 | void 3235 | wlloadfonts(char *fontstr, double fontsize) 3236 | { 3237 | FcPattern *pattern; 3238 | double fontval; 3239 | float ceilf(float); 3240 | 3241 | if (fontstr[0] == '-') { 3242 | /* XXX: need XftXlfdParse equivalent */ 3243 | pattern = NULL; 3244 | } else { 3245 | pattern = FcNameParse((FcChar8 *)fontstr); 3246 | } 3247 | 3248 | if (!pattern) 3249 | die("st: can't open font %s\n", fontstr); 3250 | 3251 | if (fontsize > 1) { 3252 | FcPatternDel(pattern, FC_PIXEL_SIZE); 3253 | FcPatternDel(pattern, FC_SIZE); 3254 | FcPatternAddDouble(pattern, FC_PIXEL_SIZE, (double)fontsize); 3255 | usedfontsize = fontsize; 3256 | } else { 3257 | if (FcPatternGetDouble(pattern, FC_PIXEL_SIZE, 0, &fontval) == 3258 | FcResultMatch) { 3259 | usedfontsize = fontval; 3260 | } else if (FcPatternGetDouble(pattern, FC_SIZE, 0, &fontval) == 3261 | FcResultMatch) { 3262 | usedfontsize = -1; 3263 | } else { 3264 | /* 3265 | * Default font size is 12, if none given. This is to 3266 | * have a known usedfontsize value. 3267 | */ 3268 | FcPatternAddDouble(pattern, FC_PIXEL_SIZE, 12); 3269 | usedfontsize = 12; 3270 | } 3271 | defaultfontsize = usedfontsize; 3272 | } 3273 | 3274 | FcConfigSubstitute(0, pattern, FcMatchPattern); 3275 | FcDefaultSubstitute(pattern); 3276 | 3277 | if (wlloadfont(&dc.font, pattern)) 3278 | die("st: can't open font %s\n", fontstr); 3279 | 3280 | if (usedfontsize < 0) { 3281 | FcPatternGetDouble(dc.font.pattern, 3282 | FC_PIXEL_SIZE, 0, &fontval); 3283 | usedfontsize = fontval; 3284 | if (fontsize == 0) 3285 | defaultfontsize = fontval; 3286 | } 3287 | 3288 | /* Setting character width and height. */ 3289 | wl.cw = ceilf(dc.font.width * cwscale); 3290 | wl.ch = ceilf(dc.font.height * chscale); 3291 | 3292 | FcPatternDel(pattern, FC_SLANT); 3293 | FcPatternAddInteger(pattern, FC_SLANT, FC_SLANT_ITALIC); 3294 | if (wlloadfont(&dc.ifont, pattern)) 3295 | die("st: can't open font %s\n", fontstr); 3296 | 3297 | FcPatternDel(pattern, FC_WEIGHT); 3298 | FcPatternAddInteger(pattern, FC_WEIGHT, FC_WEIGHT_BOLD); 3299 | if (wlloadfont(&dc.ibfont, pattern)) 3300 | die("st: can't open font %s\n", fontstr); 3301 | 3302 | FcPatternDel(pattern, FC_SLANT); 3303 | FcPatternAddInteger(pattern, FC_SLANT, FC_SLANT_ROMAN); 3304 | if (wlloadfont(&dc.bfont, pattern)) 3305 | die("st: can't open font %s\n", fontstr); 3306 | 3307 | FcPatternDestroy(pattern); 3308 | } 3309 | 3310 | void 3311 | wlunloadfont(Font *f) 3312 | { 3313 | wld_font_close(f->match); 3314 | FcPatternDestroy(f->pattern); 3315 | if (f->set) 3316 | FcFontSetDestroy(f->set); 3317 | } 3318 | 3319 | void 3320 | wlunloadfonts(void) 3321 | { 3322 | /* Free the loaded fonts in the font cache. */ 3323 | while (frclen > 0) 3324 | wld_font_close(frc[--frclen].font); 3325 | 3326 | wlunloadfont(&dc.font); 3327 | wlunloadfont(&dc.bfont); 3328 | wlunloadfont(&dc.ifont); 3329 | wlunloadfont(&dc.ibfont); 3330 | } 3331 | 3332 | void 3333 | wlzoom(const Arg *arg) 3334 | { 3335 | Arg larg; 3336 | 3337 | larg.f = usedfontsize + arg->f; 3338 | wlzoomabs(&larg); 3339 | } 3340 | 3341 | void 3342 | wlzoomabs(const Arg *arg) 3343 | { 3344 | wlunloadfonts(); 3345 | wlloadfonts(usedfont, arg->f); 3346 | cresize(0, 0); 3347 | ttyresize(); 3348 | redraw(); 3349 | /* XXX: Should the window size be updated here because wayland doesn't 3350 | * have a notion of hints? 3351 | * xhints(); 3352 | */ 3353 | } 3354 | 3355 | void 3356 | wlzoomreset(const Arg *arg) 3357 | { 3358 | Arg larg; 3359 | 3360 | if (defaultfontsize > 0) { 3361 | larg.f = defaultfontsize; 3362 | wlzoomabs(&larg); 3363 | } 3364 | } 3365 | 3366 | void 3367 | wlinit(void) 3368 | { 3369 | struct wl_registry *registry; 3370 | 3371 | if (!(wl.dpy = wl_display_connect(NULL))) 3372 | die("Can't open display\n"); 3373 | 3374 | registry = wl_display_get_registry(wl.dpy); 3375 | wl_registry_add_listener(registry, ®listener, NULL); 3376 | wld.ctx = wld_wayland_create_context(wl.dpy, WLD_ANY); 3377 | wld.renderer = wld_create_renderer(wld.ctx); 3378 | 3379 | wl_display_roundtrip(wl.dpy); 3380 | 3381 | if (!wl.shm) 3382 | die("Display has no SHM\n"); 3383 | if (!wl.seat) 3384 | die("Display has no seat\n"); 3385 | if (!wl.datadevmanager) 3386 | die("Display has no data device manager\n"); 3387 | if (!wl.wm) 3388 | die("Display has no window manager\n"); 3389 | 3390 | wl.keyboard = wl_seat_get_keyboard(wl.seat); 3391 | wl_keyboard_add_listener(wl.keyboard, &kbdlistener, NULL); 3392 | wl.pointer = wl_seat_get_pointer(wl.seat); 3393 | wl_pointer_add_listener(wl.pointer, &ptrlistener, NULL); 3394 | wl.datadev = wl_data_device_manager_get_data_device(wl.datadevmanager, 3395 | wl.seat); 3396 | wl_data_device_add_listener(wl.datadev, &datadevlistener, NULL); 3397 | 3398 | /* font */ 3399 | if (!FcInit()) 3400 | die("Could not init fontconfig.\n"); 3401 | 3402 | usedfont = (opt_font == NULL)? font : opt_font; 3403 | wld.fontctx = wld_font_create_context(); 3404 | wlloadfonts(usedfont, 0); 3405 | 3406 | wlloadcols(); 3407 | wlloadcursor(); 3408 | 3409 | wl.vis = 0; 3410 | wl.h = 2 * borderpx + term.row * wl.ch; 3411 | wl.w = 2 * borderpx + term.col * wl.cw; 3412 | 3413 | wl.surface = wl_compositor_create_surface(wl.cmp); 3414 | wl_surface_add_listener(wl.surface, &surflistener, NULL); 3415 | 3416 | wl.xdgsurface = xdg_wm_base_get_xdg_surface(wl.wm, wl.surface); 3417 | xdg_surface_add_listener(wl.xdgsurface, &xdgsurflistener, NULL); 3418 | wl.toplevel = xdg_surface_get_toplevel(wl.xdgsurface); 3419 | xdg_toplevel_add_listener(wl.toplevel, &toplevellistener, NULL); 3420 | xdg_toplevel_set_app_id(wl.toplevel, opt_class ? opt_class : termname); 3421 | 3422 | wl.xkb.ctx = xkb_context_new(0); 3423 | wlresettitle(); 3424 | wl_surface_commit(wl.surface); 3425 | } 3426 | 3427 | /* 3428 | * TODO: Implement something like XftDrawGlyphFontSpec in wld, and then apply a 3429 | * similar patch to ae1923d27533ff46400d93765e971558201ca1ee 3430 | */ 3431 | 3432 | void 3433 | wldraws(char *s, Glyph base, int x, int y, int charlen, int bytelen) 3434 | { 3435 | int winx = borderpx + x * wl.cw, winy = borderpx + y * wl.ch, 3436 | width = charlen * wl.cw, xp, i; 3437 | int frcflags, charexists; 3438 | int u8fl, u8fblen, u8cblen, doesexist; 3439 | char *u8c, *u8fs; 3440 | Rune unicodep; 3441 | Font *font = &dc.font; 3442 | FcResult fcres; 3443 | FcPattern *fcpattern, *fontpattern; 3444 | FcFontSet *fcsets[] = { NULL }; 3445 | FcCharSet *fccharset; 3446 | uint32_t fg, bg, temp; 3447 | int oneatatime; 3448 | 3449 | frcflags = FRC_NORMAL; 3450 | 3451 | /* Fallback on color display for attributes not supported by the font */ 3452 | if (base.mode & ATTR_ITALIC && base.mode & ATTR_BOLD) { 3453 | if (dc.ibfont.badslant || dc.ibfont.badweight) 3454 | base.fg = defaultattr; 3455 | font = &dc.ibfont; 3456 | frcflags = FRC_ITALICBOLD; 3457 | } else if (base.mode & ATTR_ITALIC) { 3458 | if (dc.ifont.badslant) 3459 | base.fg = defaultattr; 3460 | font = &dc.ifont; 3461 | frcflags = FRC_ITALIC; 3462 | } else if (base.mode & ATTR_BOLD) { 3463 | if (dc.bfont.badweight) 3464 | base.fg = defaultattr; 3465 | font = &dc.ifont; 3466 | frcflags = FRC_BOLD; 3467 | } 3468 | 3469 | if (IS_TRUECOL(base.fg)) { 3470 | fg = base.fg | 0xff000000; 3471 | } else { 3472 | fg = dc.col[base.fg]; 3473 | } 3474 | 3475 | if (IS_TRUECOL(base.bg)) { 3476 | bg = base.bg | 0xff000000; 3477 | } else { 3478 | bg = dc.col[base.bg]; 3479 | } 3480 | 3481 | if (base.mode & ATTR_BOLD) { 3482 | /* 3483 | * change basic system colors [0-7] 3484 | * to bright system colors [8-15] 3485 | */ 3486 | if (BETWEEN(base.fg, 0, 7) && !(base.mode & ATTR_FAINT)) 3487 | fg = dc.col[base.fg + 8]; 3488 | 3489 | if (base.mode & ATTR_ITALIC) { 3490 | font = &dc.ibfont; 3491 | frcflags = FRC_ITALICBOLD; 3492 | } else { 3493 | font = &dc.bfont; 3494 | frcflags = FRC_BOLD; 3495 | } 3496 | } 3497 | 3498 | if (IS_SET(MODE_REVERSE)) { 3499 | if (fg == dc.col[defaultfg]) { 3500 | fg = dc.col[defaultbg]; 3501 | } else { 3502 | fg = ~(fg & 0xffffff); 3503 | } 3504 | 3505 | if (bg == dc.col[defaultbg]) { 3506 | bg = dc.col[defaultfg]; 3507 | } else { 3508 | bg = ~(bg & 0xffffff); 3509 | } 3510 | } 3511 | 3512 | if (base.mode & ATTR_REVERSE) { 3513 | temp = fg; 3514 | fg = bg; 3515 | bg = temp; 3516 | } 3517 | 3518 | if (base.mode & ATTR_FAINT && !(base.mode & ATTR_BOLD)) { 3519 | fg = (fg & (0xff << 24)) 3520 | | ((((fg >> 16) & 0xff) / 2) << 16) 3521 | | ((((fg >> 8) & 0xff) / 2) << 8) 3522 | | ((fg & 0xff) / 2); 3523 | } 3524 | 3525 | if (base.mode & ATTR_BLINK && term.mode & MODE_BLINK) 3526 | fg = bg; 3527 | 3528 | if (base.mode & ATTR_INVISIBLE) 3529 | fg = bg; 3530 | 3531 | /* Intelligent cleaning up of the borders. */ 3532 | if (x == 0) { 3533 | wlclear(0, (y == 0)? 0 : winy, borderpx, 3534 | ((y >= term.row-1)? wl.h : (winy + wl.ch))); 3535 | } 3536 | if (x + charlen >= term.col) { 3537 | wlclear(winx + width, (y == 0)? 0 : winy, wl.w, 3538 | ((y >= term.row-1)? wl.h : (winy + wl.ch))); 3539 | } 3540 | if (y == 0) 3541 | wlclear(winx, 0, winx + width, borderpx); 3542 | if (y == term.row-1) 3543 | wlclear(winx, winy + wl.ch, winx + width, wl.h); 3544 | 3545 | /* Clean up the region we want to draw to. */ 3546 | wld_fill_rectangle(wld.renderer, bg, winx, winy, width, wl.ch); 3547 | 3548 | for (xp = winx; bytelen > 0;) { 3549 | /* 3550 | * Search for the range in the to be printed string of glyphs 3551 | * that are in the main font. Then print that range. If 3552 | * some glyph is found that is not in the font, do the 3553 | * fallback dance. 3554 | */ 3555 | u8fs = s; 3556 | u8fblen = 0; 3557 | u8fl = 0; 3558 | oneatatime = font->width != wl.cw; 3559 | for (;;) { 3560 | u8c = s; 3561 | u8cblen = utf8decode(s, &unicodep, UTF_SIZ); 3562 | s += u8cblen; 3563 | bytelen -= u8cblen; 3564 | 3565 | doesexist = wld_font_ensure_char(font->match, unicodep); 3566 | if (doesexist) { 3567 | u8fl++; 3568 | u8fblen += u8cblen; 3569 | if (!oneatatime && bytelen > 0) 3570 | continue; 3571 | } 3572 | 3573 | if (u8fl > 0) { 3574 | wld_draw_text(wld.renderer, 3575 | font->match, fg, xp, 3576 | winy + font->ascent, 3577 | u8fs, u8fblen, NULL); 3578 | xp += wl.cw * u8fl; 3579 | } 3580 | break; 3581 | } 3582 | if (doesexist) { 3583 | if (oneatatime) 3584 | continue; 3585 | break; 3586 | } 3587 | 3588 | /* Search the font cache. */ 3589 | for (i = 0; i < frclen; i++) { 3590 | charexists = wld_font_ensure_char(frc[i].font, unicodep); 3591 | /* Everything correct. */ 3592 | if (charexists && frc[i].flags == frcflags) 3593 | break; 3594 | /* We got a default font for a not found glyph. */ 3595 | if (!charexists && frc[i].flags == frcflags \ 3596 | && frc[i].unicodep == unicodep) { 3597 | break; 3598 | } 3599 | } 3600 | 3601 | /* Nothing was found. */ 3602 | if (i >= frclen) { 3603 | if (!font->set) 3604 | font->set = FcFontSort(0, font->pattern, 3605 | 1, 0, &fcres); 3606 | fcsets[0] = font->set; 3607 | 3608 | /* 3609 | * Nothing was found in the cache. Now use 3610 | * some dozen of Fontconfig calls to get the 3611 | * font for one single character. 3612 | * 3613 | * Xft and fontconfig are design failures. 3614 | */ 3615 | fcpattern = FcPatternDuplicate(font->pattern); 3616 | fccharset = FcCharSetCreate(); 3617 | 3618 | FcCharSetAddChar(fccharset, unicodep); 3619 | FcPatternAddCharSet(fcpattern, FC_CHARSET, 3620 | fccharset); 3621 | FcPatternAddBool(fcpattern, FC_SCALABLE, 1); 3622 | 3623 | FcConfigSubstitute(0, fcpattern, 3624 | FcMatchPattern); 3625 | FcDefaultSubstitute(fcpattern); 3626 | 3627 | fontpattern = FcFontSetMatch(0, fcsets, 1, 3628 | fcpattern, &fcres); 3629 | 3630 | /* 3631 | * Overwrite or create the new cache entry. 3632 | */ 3633 | if (frclen >= LEN(frc)) { 3634 | frclen = LEN(frc) - 1; 3635 | wld_font_close(frc[frclen].font); 3636 | frc[frclen].unicodep = 0; 3637 | } 3638 | 3639 | frc[frclen].font = wld_font_open_pattern(wld.fontctx, 3640 | fontpattern); 3641 | frc[frclen].flags = frcflags; 3642 | frc[frclen].unicodep = unicodep; 3643 | 3644 | i = frclen; 3645 | frclen++; 3646 | 3647 | FcPatternDestroy(fcpattern); 3648 | FcCharSetDestroy(fccharset); 3649 | } 3650 | 3651 | wld_draw_text(wld.renderer, frc[i].font, fg, 3652 | xp, winy + frc[i].font->ascent, 3653 | u8c, u8cblen, NULL); 3654 | 3655 | xp += wl.cw * wcwidth(unicodep); 3656 | } 3657 | 3658 | if (base.mode & ATTR_UNDERLINE) { 3659 | wld_fill_rectangle(wld.renderer, fg, winx, winy + font->ascent + 1, 3660 | width, 1); 3661 | } 3662 | 3663 | if (base.mode & ATTR_STRUCK) { 3664 | wld_fill_rectangle(wld.renderer, fg, winx, winy + 2 * font->ascent / 3, 3665 | width, 1); 3666 | } 3667 | } 3668 | 3669 | void 3670 | wldrawglyph(Glyph g, int x, int y) 3671 | { 3672 | static char buf[UTF_SIZ]; 3673 | size_t len = utf8encode(g.u, buf); 3674 | int width = g.mode & ATTR_WIDE ? 2 : 1; 3675 | 3676 | wldraws(buf, g, x, y, width, len); 3677 | } 3678 | 3679 | void 3680 | wldrawcursor(void) 3681 | { 3682 | static int oldx = 0, oldy = 0; 3683 | int curx; 3684 | Glyph g = {' ', ATTR_NULL, defaultbg, defaultcs}, og; 3685 | int ena_sel = sel.ob.x != -1 && sel.alt == IS_SET(MODE_ALTSCREEN); 3686 | uint32_t drawcol; 3687 | 3688 | LIMIT(oldx, 0, term.col-1); 3689 | LIMIT(oldy, 0, term.row-1); 3690 | 3691 | curx = term.c.x; 3692 | 3693 | /* adjust position if in dummy */ 3694 | if (term.line[oldy][oldx].mode & ATTR_WDUMMY) 3695 | oldx--; 3696 | if (term.line[term.c.y][curx].mode & ATTR_WDUMMY) 3697 | curx--; 3698 | 3699 | /* remove the old cursor */ 3700 | og = term.line[oldy][oldx]; 3701 | if (ena_sel && selected(oldx, oldy)) 3702 | og.mode ^= ATTR_REVERSE; 3703 | wldrawglyph(og, oldx, oldy); 3704 | if (oldx != curx || oldy != term.c.y) { 3705 | wl_surface_damage(wl.surface, borderpx + oldx * wl.cw, 3706 | borderpx + oldy * wl.ch, wl.cw, wl.ch); 3707 | } 3708 | 3709 | g.u = term.line[term.c.y][term.c.x].u; 3710 | 3711 | /* 3712 | * Select the right color for the right mode. 3713 | */ 3714 | if (IS_SET(MODE_REVERSE)) { 3715 | g.mode |= ATTR_REVERSE; 3716 | g.bg = defaultfg; 3717 | if (ena_sel && selected(term.c.x, term.c.y)) { 3718 | drawcol = dc.col[defaultcs]; 3719 | g.fg = defaultrcs; 3720 | } else { 3721 | drawcol = dc.col[defaultrcs]; 3722 | g.fg = defaultcs; 3723 | } 3724 | } else { 3725 | if (ena_sel && selected(term.c.x, term.c.y)) { 3726 | drawcol = dc.col[defaultrcs]; 3727 | g.fg = defaultfg; 3728 | g.bg = defaultrcs; 3729 | } else { 3730 | drawcol = dc.col[defaultcs]; 3731 | } 3732 | } 3733 | 3734 | if (IS_SET(MODE_HIDE)) 3735 | return; 3736 | 3737 | /* draw the new one */ 3738 | if (wl.state & WIN_FOCUSED) { 3739 | switch (wl.cursor) { 3740 | case 7: /* st extension: snowman */ 3741 | utf8decode("☃", &g.u, UTF_SIZ); 3742 | case 0: /* Blinking Block */ 3743 | case 1: /* Blinking Block (Default) */ 3744 | case 2: /* Steady Block */ 3745 | g.mode |= term.line[term.c.y][curx].mode & ATTR_WIDE; 3746 | wldrawglyph(g, term.c.x, term.c.y); 3747 | break; 3748 | case 3: /* Blinking Underline */ 3749 | case 4: /* Steady Underline */ 3750 | wld_fill_rectangle(wld.renderer, drawcol, 3751 | borderpx + curx * wl.cw, 3752 | borderpx + (term.c.y + 1) * wl.ch - \ 3753 | cursorthickness, 3754 | wl.cw, cursorthickness); 3755 | break; 3756 | case 5: /* Blinking bar */ 3757 | case 6: /* Steady bar */ 3758 | wld_fill_rectangle(wld.renderer, drawcol, 3759 | borderpx + curx * wl.cw, 3760 | borderpx + term.c.y * wl.ch, 3761 | cursorthickness, wl.ch); 3762 | break; 3763 | } 3764 | } else { 3765 | wld_fill_rectangle(wld.renderer, drawcol, 3766 | borderpx + curx * wl.cw, 3767 | borderpx + term.c.y * wl.ch, 3768 | wl.cw - 1, 1); 3769 | wld_fill_rectangle(wld.renderer, drawcol, 3770 | borderpx + curx * wl.cw, 3771 | borderpx + term.c.y * wl.ch, 3772 | 1, wl.ch - 1); 3773 | wld_fill_rectangle(wld.renderer, drawcol, 3774 | borderpx + (curx + 1) * wl.cw - 1, 3775 | borderpx + term.c.y * wl.ch, 3776 | 1, wl.ch - 1); 3777 | wld_fill_rectangle(wld.renderer, drawcol, 3778 | borderpx + curx * wl.cw, 3779 | borderpx + (term.c.y + 1) * wl.ch - 1, 3780 | wl.cw, 1); 3781 | } 3782 | wl_surface_damage(wl.surface, borderpx + curx * wl.cw, 3783 | borderpx + term.c.y * wl.ch, wl.cw, wl.ch); 3784 | oldx = curx, oldy = term.c.y; 3785 | } 3786 | 3787 | void 3788 | wlsettitle(char *title) 3789 | { 3790 | xdg_toplevel_set_title(wl.toplevel, title); 3791 | } 3792 | 3793 | void 3794 | wlresettitle(void) 3795 | { 3796 | wlsettitle(opt_title ? opt_title : "st"); 3797 | } 3798 | 3799 | void 3800 | redraw(void) 3801 | { 3802 | tfulldirt(); 3803 | } 3804 | 3805 | void 3806 | draw(void) 3807 | { 3808 | int y, y0; 3809 | 3810 | for (y = 0; y <= term.bot; ++y) { 3811 | if (!term.dirty[y]) 3812 | continue; 3813 | for (y0 = y; y <= term.bot && term.dirty[y]; ++y); 3814 | wl_surface_damage(wl.surface, 0, borderpx + y0 * wl.ch, 3815 | wl.w, (y - y0) * wl.ch); 3816 | } 3817 | 3818 | wld_set_target_buffer(wld.renderer, wld.buffer); 3819 | drawregion(0, 0, term.col, term.row); 3820 | wl.framecb = wl_surface_frame(wl.surface); 3821 | wl_callback_add_listener(wl.framecb, &framelistener, NULL); 3822 | wld_flush(wld.renderer); 3823 | wl_surface_attach(wl.surface, wl.buffer, 0, 0); 3824 | wl_surface_commit(wl.surface); 3825 | /* need to wait to destroy the old buffer until we commit the new 3826 | * buffer */ 3827 | if (wld.oldbuffer) { 3828 | wld_buffer_unreference(wld.oldbuffer); 3829 | wld.oldbuffer = 0; 3830 | } 3831 | needdraw = false; 3832 | } 3833 | 3834 | void 3835 | drawregion(int x1, int y1, int x2, int y2) 3836 | { 3837 | int ic, ib, x, y, ox; 3838 | Glyph base, new; 3839 | char buf[DRAW_BUF_SIZ]; 3840 | int ena_sel = sel.ob.x != -1 && sel.alt == IS_SET(MODE_ALTSCREEN); 3841 | 3842 | for (y = y1; y < y2; y++) { 3843 | if (!term.dirty[y]) 3844 | continue; 3845 | 3846 | term.dirty[y] = 0; 3847 | base = term.line[y][0]; 3848 | ic = ib = ox = 0; 3849 | for (x = x1; x < x2; x++) { 3850 | new = term.line[y][x]; 3851 | if (new.mode == ATTR_WDUMMY) 3852 | continue; 3853 | if (ena_sel && selected(x, y)) 3854 | new.mode ^= ATTR_REVERSE; 3855 | if (ib > 0 && (ATTRCMP(base, new) 3856 | || ib >= DRAW_BUF_SIZ-UTF_SIZ)) { 3857 | wldraws(buf, base, ox, y, ic, ib); 3858 | ic = ib = 0; 3859 | } 3860 | if (ib == 0) { 3861 | ox = x; 3862 | base = new; 3863 | } 3864 | 3865 | ib += utf8encode(new.u, buf+ib); 3866 | ic += (new.mode & ATTR_WIDE)? 2 : 1; 3867 | } 3868 | if (ib > 0) 3869 | wldraws(buf, base, ox, y, ic, ib); 3870 | } 3871 | wldrawcursor(); 3872 | } 3873 | 3874 | void 3875 | wlseturgency(int add) 3876 | { 3877 | /* XXX: no urgency equivalent yet in wayland */ 3878 | } 3879 | 3880 | int 3881 | match(uint mask, uint state) 3882 | { 3883 | return mask == MOD_MASK_ANY || mask == (state & ~(ignoremod)); 3884 | } 3885 | 3886 | void 3887 | numlock(const Arg *dummy) 3888 | { 3889 | term.numlock ^= 1; 3890 | } 3891 | 3892 | char* 3893 | kmap(xkb_keysym_t k, uint state) 3894 | { 3895 | Key *kp; 3896 | int i; 3897 | 3898 | /* Check for mapped keys out of X11 function keys. */ 3899 | for (i = 0; i < LEN(mappedkeys); i++) { 3900 | if (mappedkeys[i] == k) 3901 | break; 3902 | } 3903 | if (i == LEN(mappedkeys)) { 3904 | if ((k & 0xFFFF) < 0xFD00) 3905 | return NULL; 3906 | } 3907 | 3908 | for (kp = key; kp < key + LEN(key); kp++) { 3909 | if (kp->k != k) 3910 | continue; 3911 | 3912 | if (!match(kp->mask, state)) 3913 | continue; 3914 | 3915 | if (IS_SET(MODE_APPKEYPAD) ? kp->appkey < 0 : kp->appkey > 0) 3916 | continue; 3917 | if (term.numlock && kp->appkey == 2) 3918 | continue; 3919 | 3920 | if (IS_SET(MODE_APPCURSOR) ? kp->appcursor < 0 : kp->appcursor > 0) 3921 | continue; 3922 | 3923 | if (IS_SET(MODE_CRLF) ? kp->crlf < 0 : kp->crlf > 0) 3924 | continue; 3925 | 3926 | return kp->s; 3927 | } 3928 | 3929 | return NULL; 3930 | } 3931 | 3932 | void 3933 | cresize(int width, int height) 3934 | { 3935 | int col, row; 3936 | 3937 | if (width != 0) 3938 | wl.w = width; 3939 | if (height != 0) 3940 | wl.h = height; 3941 | 3942 | col = (wl.w - 2 * borderpx) / wl.cw; 3943 | row = (wl.h - 2 * borderpx) / wl.ch; 3944 | 3945 | tresize(col, row); 3946 | wlresize(col, row); 3947 | } 3948 | 3949 | void 3950 | regglobal(void *data, struct wl_registry *registry, uint32_t name, 3951 | const char *interface, uint32_t version) 3952 | { 3953 | if (strcmp(interface, "wl_compositor") == 0) { 3954 | wl.cmp = wl_registry_bind(registry, name, 3955 | &wl_compositor_interface, 3); 3956 | } else if (strcmp(interface, "xdg_wm_base") == 0) { 3957 | wl.wm = wl_registry_bind(registry, name, &xdg_wm_base_interface, 1); 3958 | xdg_wm_base_add_listener(wl.wm, &wmlistener, NULL); 3959 | } else if (strcmp(interface, "wl_shm") == 0) { 3960 | wl.shm = wl_registry_bind(registry, name, &wl_shm_interface, 1); 3961 | } else if (strcmp(interface, "wl_seat") == 0) { 3962 | wl.seat = wl_registry_bind(registry, name, 3963 | &wl_seat_interface, 4); 3964 | } else if (strcmp(interface, "wl_data_device_manager") == 0) { 3965 | wl.datadevmanager = wl_registry_bind(registry, name, 3966 | &wl_data_device_manager_interface, 1); 3967 | } else if (strcmp(interface, "wl_output") == 0) { 3968 | /* bind to outputs so we can get surface enter events */ 3969 | wl_registry_bind(registry, name, &wl_output_interface, 2); 3970 | } 3971 | } 3972 | 3973 | void 3974 | regglobalremove(void *data, struct wl_registry *registry, uint32_t name) 3975 | { 3976 | } 3977 | 3978 | void 3979 | surfenter(void *data, struct wl_surface *surface, struct wl_output *output) 3980 | { 3981 | wl.vis++; 3982 | if (!(wl.state & WIN_VISIBLE)) 3983 | wl.state |= WIN_VISIBLE; 3984 | } 3985 | 3986 | void 3987 | surfleave(void *data, struct wl_surface *surface, struct wl_output *output) 3988 | { 3989 | if (--wl.vis == 0) 3990 | wl.state &= ~WIN_VISIBLE; 3991 | } 3992 | 3993 | void 3994 | framedone(void *data, struct wl_callback *callback, uint32_t msecs) 3995 | { 3996 | wl_callback_destroy(callback); 3997 | wl.framecb = NULL; 3998 | if (needdraw && wl.state & WIN_VISIBLE) { 3999 | draw(); 4000 | } 4001 | } 4002 | 4003 | void 4004 | kbdkeymap(void *data, struct wl_keyboard *keyboard, uint32_t format, int32_t fd, 4005 | uint32_t size) 4006 | { 4007 | char *string; 4008 | 4009 | if (format != WL_KEYBOARD_KEYMAP_FORMAT_XKB_V1) { 4010 | close(fd); 4011 | return; 4012 | } 4013 | 4014 | string = mmap(NULL, size, PROT_READ, MAP_SHARED, fd, 0); 4015 | 4016 | if (string == MAP_FAILED) { 4017 | close(fd); 4018 | return; 4019 | } 4020 | 4021 | wl.xkb.keymap = xkb_keymap_new_from_string(wl.xkb.ctx, string, 4022 | XKB_KEYMAP_FORMAT_TEXT_V1, 0); 4023 | munmap(string, size); 4024 | close(fd); 4025 | wl.xkb.state = xkb_state_new(wl.xkb.keymap); 4026 | 4027 | wl.xkb.ctrl = xkb_keymap_mod_get_index(wl.xkb.keymap, XKB_MOD_NAME_CTRL); 4028 | wl.xkb.alt = xkb_keymap_mod_get_index(wl.xkb.keymap, XKB_MOD_NAME_ALT); 4029 | wl.xkb.shift = xkb_keymap_mod_get_index(wl.xkb.keymap, XKB_MOD_NAME_SHIFT); 4030 | wl.xkb.logo = xkb_keymap_mod_get_index(wl.xkb.keymap, XKB_MOD_NAME_LOGO); 4031 | 4032 | wl.xkb.mods = 0; 4033 | } 4034 | 4035 | void 4036 | kbdenter(void *data, struct wl_keyboard *keyboard, uint32_t serial, 4037 | struct wl_surface *surface, struct wl_array *keys) 4038 | { 4039 | wl.state |= WIN_FOCUSED; 4040 | if (IS_SET(MODE_FOCUS)) 4041 | ttywrite("\033[I", 3); 4042 | /* need to redraw the cursor */ 4043 | needdraw = true; 4044 | } 4045 | 4046 | void 4047 | kbdleave(void *data, struct wl_keyboard *keyboard, uint32_t serial, 4048 | struct wl_surface *surface) 4049 | { 4050 | /* selection offers are invalidated when we lose keyboard focus */ 4051 | wl.seloffer = NULL; 4052 | wl.state &= ~WIN_FOCUSED; 4053 | if (IS_SET(MODE_FOCUS)) 4054 | ttywrite("\033[O", 3); 4055 | /* need to redraw the cursor */ 4056 | needdraw = true; 4057 | /* disable key repeat */ 4058 | repeat.len = 0; 4059 | } 4060 | 4061 | void 4062 | kbdkey(void *data, struct wl_keyboard *keyboard, uint32_t serial, uint32_t time, 4063 | uint32_t key, uint32_t state) 4064 | { 4065 | xkb_keysym_t ksym; 4066 | char buf[32], *str; 4067 | int len; 4068 | Rune c; 4069 | Shortcut *bp; 4070 | 4071 | if (IS_SET(MODE_KBDLOCK)) 4072 | return; 4073 | 4074 | if (state == WL_KEYBOARD_KEY_STATE_RELEASED) { 4075 | if (repeat.key == key) 4076 | repeat.len = 0; 4077 | return; 4078 | } 4079 | 4080 | ksym = xkb_state_key_get_one_sym(wl.xkb.state, key + 8); 4081 | len = xkb_keysym_to_utf8(ksym, buf, sizeof buf); 4082 | if (len > 0) 4083 | --len; 4084 | 4085 | /* 1. shortcuts */ 4086 | for (bp = shortcuts; bp < shortcuts + LEN(shortcuts); bp++) { 4087 | if (ksym == bp->keysym && match(bp->mod, wl.xkb.mods)) { 4088 | bp->func(&(bp->arg)); 4089 | return; 4090 | } 4091 | } 4092 | 4093 | /* 2. custom keys from config.h */ 4094 | if ((str = kmap(ksym, wl.xkb.mods))) { 4095 | len = strlen(str); 4096 | goto send; 4097 | } 4098 | 4099 | /* 3. composed string from input method */ 4100 | if (len == 0) 4101 | return; 4102 | if (len == 1 && wl.xkb.mods & MOD_MASK_ALT) { 4103 | if (IS_SET(MODE_8BIT)) { 4104 | if (*buf < 0177) { 4105 | c = *buf | 0x80; 4106 | len = utf8encode(c, buf); 4107 | } 4108 | } else { 4109 | buf[1] = buf[0]; 4110 | buf[0] = '\033'; 4111 | len = 2; 4112 | } 4113 | } 4114 | /* convert character to control character */ 4115 | else if (len == 1 && wl.xkb.mods & MOD_MASK_CTRL) { 4116 | if ((*buf >= '@' && *buf < '\177') || *buf == ' ') 4117 | *buf &= 0x1F; 4118 | else if (*buf == '2') *buf = '\000'; 4119 | else if (*buf >= '3' && *buf <= '7') 4120 | *buf -= ('3' - '\033'); 4121 | else if (*buf == '8') *buf = '\177'; 4122 | else if (*buf == '/') *buf = '_' & 0x1F; 4123 | } 4124 | 4125 | str = buf; 4126 | 4127 | send: 4128 | memcpy(repeat.str, str, len); 4129 | repeat.key = key; 4130 | repeat.len = len; 4131 | repeat.started = false; 4132 | clock_gettime(CLOCK_MONOTONIC, &repeat.last); 4133 | ttysend(str, len); 4134 | } 4135 | 4136 | void 4137 | kbdmodifiers(void *data, struct wl_keyboard *keyboard, uint32_t serial, 4138 | uint32_t dep, uint32_t lat, uint32_t lck, uint32_t group) 4139 | { 4140 | xkb_mod_mask_t mod_mask; 4141 | 4142 | xkb_state_update_mask(wl.xkb.state, dep, lat, lck, group, 0, 0); 4143 | 4144 | mod_mask = xkb_state_serialize_mods(wl.xkb.state, XKB_STATE_MODS_EFFECTIVE); 4145 | wl.xkb.mods = 0; 4146 | 4147 | if (mod_mask & (1 << wl.xkb.ctrl)) 4148 | wl.xkb.mods |= MOD_MASK_CTRL; 4149 | if (mod_mask & (1 << wl.xkb.alt)) 4150 | wl.xkb.mods |= MOD_MASK_ALT; 4151 | if (mod_mask & (1 << wl.xkb.shift)) 4152 | wl.xkb.mods |= MOD_MASK_SHIFT; 4153 | if (mod_mask & (1 << wl.xkb.logo)) 4154 | wl.xkb.mods |= MOD_MASK_LOGO; 4155 | } 4156 | 4157 | void 4158 | kbdrepeatinfo(void *data, struct wl_keyboard *keyboard, int32_t rate, 4159 | int32_t delay) 4160 | { 4161 | keyrepeatdelay = delay; 4162 | keyrepeatinterval = 1000 / rate; 4163 | } 4164 | 4165 | void 4166 | ptrenter(void *data, struct wl_pointer *pointer, uint32_t serial, 4167 | struct wl_surface *surface, wl_fixed_t x, wl_fixed_t y) 4168 | { 4169 | struct wl_cursor_image *img = cursor.cursor->images[0]; 4170 | struct wl_buffer *buffer; 4171 | 4172 | wl_pointer_set_cursor(pointer, serial, cursor.surface, 4173 | img->hotspot_x, img->hotspot_y); 4174 | buffer = wl_cursor_image_get_buffer(img); 4175 | wl_surface_attach(cursor.surface, buffer, 0, 0); 4176 | wl_surface_damage(cursor.surface, 0, 0, img->width, img->height); 4177 | wl_surface_commit(cursor.surface); 4178 | } 4179 | 4180 | void 4181 | ptrleave(void *data, struct wl_pointer *pointer, uint32_t serial, 4182 | struct wl_surface *surface) 4183 | { 4184 | } 4185 | 4186 | void 4187 | ptrmotion(void *data, struct wl_pointer * pointer, uint32_t serial, 4188 | wl_fixed_t x, wl_fixed_t y) 4189 | { 4190 | int oldey, oldex, oldsby, oldsey; 4191 | 4192 | if (IS_SET(MODE_MOUSE)) { 4193 | wlmousereportmotion(x, y); 4194 | return; 4195 | } 4196 | 4197 | wl.px = wl_fixed_to_int(x); 4198 | wl.py = wl_fixed_to_int(y); 4199 | 4200 | if (!sel.mode) 4201 | return; 4202 | 4203 | sel.mode = SEL_READY; 4204 | oldey = sel.oe.y; 4205 | oldex = sel.oe.x; 4206 | oldsby = sel.nb.y; 4207 | oldsey = sel.ne.y; 4208 | getbuttoninfo(); 4209 | 4210 | if (oldey != sel.oe.y || oldex != sel.oe.x) 4211 | tsetdirt(MIN(sel.nb.y, oldsby), MAX(sel.ne.y, oldsey)); 4212 | } 4213 | 4214 | void 4215 | ptrbutton(void * data, struct wl_pointer * pointer, uint32_t serial, 4216 | uint32_t time, uint32_t button, uint32_t state) 4217 | { 4218 | MouseShortcut *ms; 4219 | 4220 | if (IS_SET(MODE_MOUSE) && !(wl.xkb.mods & forceselmod)) { 4221 | wlmousereportbutton(button, state); 4222 | return; 4223 | } 4224 | 4225 | switch (state) { 4226 | case WL_POINTER_BUTTON_STATE_RELEASED: 4227 | if (button == BTN_MIDDLE) { 4228 | selpaste(NULL); 4229 | } else if (button == BTN_LEFT) { 4230 | if (sel.mode == SEL_READY) { 4231 | getbuttoninfo(); 4232 | selcopy(serial); 4233 | } else 4234 | selclear(); 4235 | sel.mode = SEL_IDLE; 4236 | tsetdirt(sel.nb.y, sel.ne.y); 4237 | } 4238 | break; 4239 | 4240 | case WL_POINTER_BUTTON_STATE_PRESSED: 4241 | for (ms = mshortcuts; ms < mshortcuts + LEN(mshortcuts); ms++) { 4242 | if (button == ms->b && match(ms->mask, wl.xkb.mods)) { 4243 | ttysend(ms->s, strlen(ms->s)); 4244 | return; 4245 | } 4246 | } 4247 | 4248 | if (button == BTN_LEFT) { 4249 | /* Clear previous selection, logically and visually. */ 4250 | selclear(); 4251 | sel.mode = SEL_EMPTY; 4252 | sel.type = SEL_REGULAR; 4253 | sel.oe.x = sel.ob.x = x2col(wl.px); 4254 | sel.oe.y = sel.ob.y = y2row(wl.py); 4255 | 4256 | /* 4257 | * If the user clicks below predefined timeouts 4258 | * specific snapping behaviour is exposed. 4259 | */ 4260 | if (time - sel.tclick2 <= tripleclicktimeout) { 4261 | sel.snap = SNAP_LINE; 4262 | } else if (time - sel.tclick1 <= doubleclicktimeout) { 4263 | sel.snap = SNAP_WORD; 4264 | } else { 4265 | sel.snap = 0; 4266 | } 4267 | selnormalize(); 4268 | 4269 | if (sel.snap != 0) 4270 | sel.mode = SEL_READY; 4271 | tsetdirt(sel.nb.y, sel.ne.y); 4272 | sel.tclick2 = sel.tclick1; 4273 | sel.tclick1 = time; 4274 | } 4275 | break; 4276 | } 4277 | } 4278 | 4279 | void 4280 | ptraxis(void * data, struct wl_pointer * pointer, uint32_t time, uint32_t axis, 4281 | wl_fixed_t value) 4282 | { 4283 | Axiskey *ak; 4284 | int dir = value > 0 ? +1 : -1; 4285 | 4286 | if (IS_SET(MODE_MOUSE) && !(wl.xkb.mods & forceselmod)) { 4287 | wlmousereportaxis(axis, value); 4288 | return; 4289 | } 4290 | 4291 | for (ak = ashortcuts; ak < ashortcuts + LEN(ashortcuts); ak++) { 4292 | if (axis == ak->axis && dir == ak->dir 4293 | && match(ak->mask, wl.xkb.mods)) { 4294 | ttysend(ak->s, strlen(ak->s)); 4295 | return; 4296 | } 4297 | } 4298 | } 4299 | 4300 | void 4301 | wmping(void *data, struct xdg_wm_base *wm, uint32_t serial) 4302 | { 4303 | xdg_wm_base_pong(wm, serial); 4304 | } 4305 | 4306 | void 4307 | xdgsurfconfigure(void *data, struct xdg_surface *surf, uint32_t serial) 4308 | { 4309 | xdg_surface_ack_configure(surf, serial); 4310 | } 4311 | 4312 | void 4313 | toplevelconfigure(void *data, struct xdg_toplevel *toplevel, int32_t w, int32_t h, 4314 | struct wl_array *states) 4315 | { 4316 | if (w == wl.w && h == wl.h) 4317 | return; 4318 | cresize(w, h); 4319 | if (wl.configured) 4320 | ttyresize(); 4321 | else 4322 | wl.configured = true; 4323 | } 4324 | 4325 | void 4326 | toplevelclose(void *data, struct xdg_toplevel *toplevel) 4327 | { 4328 | /* Send SIGHUP to shell */ 4329 | kill(pid, SIGHUP); 4330 | exit(0); 4331 | } 4332 | 4333 | void 4334 | datadevoffer(void *data, struct wl_data_device *datadev, 4335 | struct wl_data_offer *offer) 4336 | { 4337 | wl_data_offer_add_listener(offer, &dataofferlistener, NULL); 4338 | } 4339 | 4340 | void 4341 | datadeventer(void *data, struct wl_data_device *datadev, uint32_t serial, 4342 | struct wl_surface *surf, wl_fixed_t x, wl_fixed_t y, 4343 | struct wl_data_offer *offer) 4344 | { 4345 | } 4346 | 4347 | void 4348 | datadevleave(void *data, struct wl_data_device *datadev) 4349 | { 4350 | } 4351 | 4352 | void 4353 | datadevmotion(void *data, struct wl_data_device *datadev, uint32_t time, 4354 | wl_fixed_t x, wl_fixed_t y) 4355 | { 4356 | } 4357 | 4358 | void 4359 | datadevdrop(void *data, struct wl_data_device *datadev) 4360 | { 4361 | } 4362 | 4363 | void 4364 | datadevselection(void *data, struct wl_data_device *datadev, 4365 | struct wl_data_offer *offer) 4366 | { 4367 | if (offer && (uintptr_t) wl_data_offer_get_user_data(offer) == 1) 4368 | wl.seloffer = offer; 4369 | else 4370 | wl.seloffer = NULL; 4371 | } 4372 | 4373 | void 4374 | dataofferoffer(void *data, struct wl_data_offer *offer, const char *mimetype) 4375 | { 4376 | /* mark the offer as usable if it supports plain text */ 4377 | if (strncmp(mimetype, "text/plain", 10) == 0) 4378 | wl_data_offer_set_user_data(offer, (void *)(uintptr_t) 1); 4379 | } 4380 | 4381 | void 4382 | datasrctarget(void *data, struct wl_data_source *source, const char *mimetype) 4383 | { 4384 | } 4385 | 4386 | void 4387 | datasrcsend(void *data, struct wl_data_source *source, const char *mimetype, 4388 | int32_t fd) 4389 | { 4390 | char *buf = sel.primary; 4391 | int len = strlen(sel.primary); 4392 | ssize_t ret; 4393 | while ((ret = write(fd, buf, MIN(len, BUFSIZ))) > 0) { 4394 | len -= ret; 4395 | buf += ret; 4396 | } 4397 | close(fd); 4398 | } 4399 | 4400 | void 4401 | datasrccancelled(void *data, struct wl_data_source *source) 4402 | { 4403 | if (sel.source == source) { 4404 | sel.source = NULL; 4405 | selclear(); 4406 | } 4407 | wl_data_source_destroy(source); 4408 | } 4409 | 4410 | void 4411 | run(void) 4412 | { 4413 | fd_set rfd; 4414 | int wlfd = wl_display_get_fd(wl.dpy), blinkset = 0; 4415 | struct timespec drawtimeout, *tv = NULL, now, last, lastblink; 4416 | ulong msecs; 4417 | 4418 | /* Look for initial configure. */ 4419 | wl_display_roundtrip(wl.dpy); 4420 | if (!wl.configured) 4421 | cresize(wl.w, wl.h); 4422 | ttynew(); 4423 | ttyresize(); 4424 | draw(); 4425 | 4426 | clock_gettime(CLOCK_MONOTONIC, &last); 4427 | lastblink = last; 4428 | 4429 | for (;;) { 4430 | FD_ZERO(&rfd); 4431 | FD_SET(cmdfd, &rfd); 4432 | FD_SET(wlfd, &rfd); 4433 | 4434 | if (pselect(MAX(wlfd, cmdfd)+1, &rfd, NULL, NULL, tv, NULL) < 0) { 4435 | if (errno == EINTR) 4436 | continue; 4437 | die("select failed: %s\n", strerror(errno)); 4438 | } 4439 | 4440 | if (FD_ISSET(cmdfd, &rfd)) { 4441 | ttyread(); 4442 | if (blinktimeout) { 4443 | blinkset = tattrset(ATTR_BLINK); 4444 | if (!blinkset) 4445 | MODBIT(term.mode, 0, MODE_BLINK); 4446 | } 4447 | } 4448 | 4449 | if (FD_ISSET(wlfd, &rfd)) { 4450 | if (wl_display_dispatch(wl.dpy) == -1) 4451 | die("Connection error\n"); 4452 | } 4453 | 4454 | clock_gettime(CLOCK_MONOTONIC, &now); 4455 | msecs = -1; 4456 | 4457 | if (blinkset && blinktimeout) { 4458 | if (TIMEDIFF(now, lastblink) >= blinktimeout) { 4459 | tsetdirtattr(ATTR_BLINK); 4460 | term.mode ^= MODE_BLINK; 4461 | lastblink = now; 4462 | } else { 4463 | msecs = MIN(msecs, blinktimeout - \ 4464 | TIMEDIFF(now, lastblink)); 4465 | } 4466 | } 4467 | if (repeat.len > 0) { 4468 | if (TIMEDIFF(now, repeat.last) >= \ 4469 | (repeat.started ? keyrepeatinterval : \ 4470 | keyrepeatdelay)) { 4471 | repeat.started = true; 4472 | repeat.last = now; 4473 | ttysend(repeat.str, repeat.len); 4474 | } else { 4475 | msecs = MIN(msecs, (repeat.started ? \ 4476 | keyrepeatinterval : keyrepeatdelay) - \ 4477 | TIMEDIFF(now, repeat.last)); 4478 | } 4479 | } 4480 | 4481 | if (needdraw && wl.state & WIN_VISIBLE) { 4482 | if (!wl.framecb) { 4483 | draw(); 4484 | } 4485 | } 4486 | 4487 | if (msecs == -1) { 4488 | tv = NULL; 4489 | } else { 4490 | drawtimeout.tv_nsec = 1E6 * msecs; 4491 | drawtimeout.tv_sec = 0; 4492 | tv = &drawtimeout; 4493 | } 4494 | 4495 | wl_display_dispatch_pending(wl.dpy); 4496 | wl_display_flush(wl.dpy); 4497 | } 4498 | } 4499 | 4500 | void 4501 | usage(void) 4502 | { 4503 | die("usage: %s [-aiv] [-c class] [-f font] [-g geometry]" 4504 | " [-n name] [-o file]\n" 4505 | " [-T title] [-t title] [-w windowid]" 4506 | " [[-e] command [args ...]]\n" 4507 | " %s [-aiv] [-c class] [-f font] [-g geometry]" 4508 | " [-n name] [-o file]\n" 4509 | " [-T title] [-t title] [-w windowid] -l line" 4510 | " [stty_args ...]\n", argv0, argv0); 4511 | } 4512 | 4513 | int 4514 | main(int argc, char *argv[]) 4515 | { 4516 | wl.cursor = cursorshape; 4517 | 4518 | ARGBEGIN { 4519 | case 'a': 4520 | allowaltscreen = 0; 4521 | break; 4522 | case 'c': 4523 | opt_class = EARGF(usage()); 4524 | break; 4525 | case 'e': 4526 | if (argc > 0) 4527 | --argc, ++argv; 4528 | goto run; 4529 | case 'f': 4530 | opt_font = EARGF(usage()); 4531 | break; 4532 | case 'o': 4533 | opt_io = EARGF(usage()); 4534 | break; 4535 | case 'l': 4536 | opt_line = EARGF(usage()); 4537 | break; 4538 | case 'n': 4539 | opt_name = EARGF(usage()); 4540 | break; 4541 | case 't': 4542 | case 'T': 4543 | opt_title = EARGF(usage()); 4544 | break; 4545 | case 'w': 4546 | opt_embed = EARGF(usage()); 4547 | break; 4548 | case 'v': 4549 | die("%s " VERSION " (c) 2010-2016 st engineers\n", argv0); 4550 | break; 4551 | default: 4552 | usage(); 4553 | } ARGEND; 4554 | 4555 | run: 4556 | if (argc > 0) { 4557 | /* eat all remaining arguments */ 4558 | opt_cmd = argv; 4559 | if (!opt_title && !opt_line) 4560 | opt_title = basename(xstrdup(argv[0])); 4561 | } 4562 | setlocale(LC_CTYPE, ""); 4563 | tnew(MAX(cols, 1), MAX(rows, 1)); 4564 | wlinit(); 4565 | selinit(); 4566 | run(); 4567 | 4568 | return 0; 4569 | } 4570 | 4571 | -------------------------------------------------------------------------------- /st.info: -------------------------------------------------------------------------------- 1 | st| simpleterm, 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#8, 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 | dl=\E[%p1%dM, 29 | dl1=\E[M, 30 | ech=\E[%p1%dX, 31 | ed=\E[J, 32 | el=\E[K, 33 | el1=\E[1K, 34 | enacs=\E)0, 35 | flash=\E[?5h$<80/>\E[?5l, 36 | fsl=^G, 37 | home=\E[H, 38 | hpa=\E[%i%p1%dG, 39 | hs, 40 | ht=^I, 41 | hts=\EH, 42 | ich=\E[%p1%d@, 43 | il1=\E[L, 44 | il=\E[%p1%dL, 45 | ind=^J, 46 | indn=\E[%p1%dS, 47 | invis=\E[8m, 48 | is2=\E[4l\E>\E[?1034l, 49 | it#8, 50 | kel=\E[1;2F, 51 | ked=\E[1;5F, 52 | ka1=\E[1~, 53 | ka3=\E[5~, 54 | kc1=\E[4~, 55 | kc3=\E[6~, 56 | kbs=\177, 57 | kcbt=\E[Z, 58 | kb2=\EOu, 59 | kcub1=\EOD, 60 | kcud1=\EOB, 61 | kcuf1=\EOC, 62 | kcuu1=\EOA, 63 | kDC=\E[3;2~, 64 | kent=\EOM, 65 | kEND=\E[1;2F, 66 | kIC=\E[2;2~, 67 | kNXT=\E[6;2~, 68 | kPRV=\E[5;2~, 69 | kHOM=\E[1;2H, 70 | kLFT=\E[1;2D, 71 | kRIT=\E[1;2C, 72 | kind=\E[1;2B, 73 | kri=\E[1;2A, 74 | kclr=\E[3;5~, 75 | kdl1=\E[3;2~, 76 | kdch1=\E[3~, 77 | kich1=\E[2~, 78 | kend=\E[4~, 79 | kf1=\EOP, 80 | kf2=\EOQ, 81 | kf3=\EOR, 82 | kf4=\EOS, 83 | kf5=\E[15~, 84 | kf6=\E[17~, 85 | kf7=\E[18~, 86 | kf8=\E[19~, 87 | kf9=\E[20~, 88 | kf10=\E[21~, 89 | kf11=\E[23~, 90 | kf12=\E[24~, 91 | kf13=\E[1;2P, 92 | kf14=\E[1;2Q, 93 | kf15=\E[1;2R, 94 | kf16=\E[1;2S, 95 | kf17=\E[15;2~, 96 | kf18=\E[17;2~, 97 | kf19=\E[18;2~, 98 | kf20=\E[19;2~, 99 | kf21=\E[20;2~, 100 | kf22=\E[21;2~, 101 | kf23=\E[23;2~, 102 | kf24=\E[24;2~, 103 | kf25=\E[1;5P, 104 | kf26=\E[1;5Q, 105 | kf27=\E[1;5R, 106 | kf28=\E[1;5S, 107 | kf29=\E[15;5~, 108 | kf30=\E[17;5~, 109 | kf31=\E[18;5~, 110 | kf32=\E[19;5~, 111 | kf33=\E[20;5~, 112 | kf34=\E[21;5~, 113 | kf35=\E[23;5~, 114 | kf36=\E[24;5~, 115 | kf37=\E[1;6P, 116 | kf38=\E[1;6Q, 117 | kf39=\E[1;6R, 118 | kf40=\E[1;6S, 119 | kf41=\E[15;6~, 120 | kf42=\E[17;6~, 121 | kf43=\E[18;6~, 122 | kf44=\E[19;6~, 123 | kf45=\E[20;6~, 124 | kf46=\E[21;6~, 125 | kf47=\E[23;6~, 126 | kf48=\E[24;6~, 127 | kf49=\E[1;3P, 128 | kf50=\E[1;3Q, 129 | kf51=\E[1;3R, 130 | kf52=\E[1;3S, 131 | kf53=\E[15;3~, 132 | kf54=\E[17;3~, 133 | kf55=\E[18;3~, 134 | kf56=\E[19;3~, 135 | kf57=\E[20;3~, 136 | kf58=\E[21;3~, 137 | kf59=\E[23;3~, 138 | kf60=\E[24;3~, 139 | kf61=\E[1;4P, 140 | kf62=\E[1;4Q, 141 | kf63=\E[1;4R, 142 | khome=\E[1~, 143 | kil1=\E[2;5~, 144 | krmir=\E[2;2~, 145 | kich1=\E[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 | ritm=\E[23m, 162 | rmacs=\E(B, 163 | rmcup=\E[?1049l, 164 | rmir=\E[4l, 165 | rmkx=\E[?1l\E>, 166 | rmso=\E[27m, 167 | rmul=\E[24m, 168 | rs1=\Ec, 169 | rs2=\E[4l\E>\E[?1034l, 170 | sc=\E7, 171 | setab=\E[4%p1%dm, 172 | setaf=\E[3%p1%dm, 173 | setb=\E[4%?%p1%{1}%=%t4%e%p1%{3}%=%t6%e%p1%{4}%=%t1%e%p1%{6}%=%t3%e%p1%d%;m, 174 | setf=\E[3%?%p1%{1}%=%t4%e%p1%{3}%=%t6%e%p1%{4}%=%t1%e%p1%{6}%=%t3%e%p1%d%;m, 175 | sgr0=\E[0m, 176 | 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, 177 | sitm=\E[3m, 178 | smacs=\E(0, 179 | smcup=\E[?1049h, 180 | smir=\E[4h, 181 | smkx=\E[?1h\E=, 182 | smso=\E[7m, 183 | smul=\E[4m, 184 | tbc=\E[3g, 185 | tsl=\E]0;, 186 | xenl, 187 | vpa=\E[%i%p1%dd, 188 | # Tmux unofficial extensions, see TERMINFO EXTENSIONS in tmux(1) 189 | Se, 190 | Ss, 191 | Tc, 192 | 193 | st-256color| simpleterm with 256 colors, 194 | use=st, 195 | colors#256, 196 | pairs#32767, 197 | # Nicked from xterm-256color 198 | setab=\E[%?%p1%{8}%<%t4%p1%d%e%p1%{16}%<%t10%p1%{8}%-%d%e48;5;%p1%d%;m, 199 | setaf=\E[%?%p1%{8}%<%t3%p1%d%e%p1%{16}%<%t9%p1%{8}%-%d%e38;5;%p1%d%;m, 200 | 201 | st-meta| simpleterm with meta key, 202 | use=st, 203 | km, 204 | rmm=\E[?1034l, 205 | smm=\E[?1034h, 206 | rs2=\E[4l\E>\E[?1034h, 207 | is2=\E[4l\E>\E[?1034h, 208 | 209 | st-meta-256color| simpleterm with meta key and 256 colors, 210 | use=st-256color, 211 | km, 212 | rmm=\E[?1034l, 213 | smm=\E[?1034h, 214 | rs2=\E[4l\E>\E[?1034h, 215 | is2=\E[4l\E>\E[?1034h, 216 | --------------------------------------------------------------------------------