1*2f106d38Snicm /* $OpenBSD: tty-keys.c,v 1.185 2025/01/02 10:34:45 nicm Exp $ */ 2311827fbSnicm 3311827fbSnicm /* 498ca8272Snicm * Copyright (c) 2007 Nicholas Marriott <nicholas.marriott@gmail.com> 5311827fbSnicm * 6311827fbSnicm * Permission to use, copy, modify, and distribute this software for any 7311827fbSnicm * purpose with or without fee is hereby granted, provided that the above 8311827fbSnicm * copyright notice and this permission notice appear in all copies. 9311827fbSnicm * 10311827fbSnicm * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 11311827fbSnicm * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 12311827fbSnicm * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 13311827fbSnicm * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 14311827fbSnicm * WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER 15311827fbSnicm * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING 16311827fbSnicm * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 17311827fbSnicm */ 18311827fbSnicm 19311827fbSnicm #include <sys/types.h> 20311827fbSnicm #include <sys/time.h> 21311827fbSnicm 2290866723Snicm #include <netinet/in.h> 2390866723Snicm 24f1c69fe1Snicm #include <ctype.h> 255e5e6809Snicm #include <limits.h> 2690866723Snicm #include <resolv.h> 275e5e6809Snicm #include <stdlib.h> 28311827fbSnicm #include <string.h> 294cf01325Snicm #include <termios.h> 304cf01325Snicm #include <unistd.h> 31311827fbSnicm 32311827fbSnicm #include "tmux.h" 33311827fbSnicm 34cc6ff773Snicm /* 359f8fac72Snicm * Handle keys input from the outside terminal. tty_default_*_keys[] are a base 369f8fac72Snicm * table of supported keys which are looked up in terminfo(5) and translated 379f8fac72Snicm * into a ternary tree. 38cc6ff773Snicm */ 39cc6ff773Snicm 404a29328cSnicm static void tty_keys_add1(struct tty_key **, const char *, key_code); 414a29328cSnicm static void tty_keys_add(struct tty *, const char *, key_code); 424a29328cSnicm static void tty_keys_free1(struct tty_key *); 434a29328cSnicm static struct tty_key *tty_keys_find1(struct tty_key *, const char *, size_t, 44885a4698Snicm size_t *); 454a29328cSnicm static struct tty_key *tty_keys_find(struct tty *, const char *, size_t, 464a29328cSnicm size_t *); 474a29328cSnicm static int tty_keys_next1(struct tty *, const char *, size_t, key_code *, 48fa0ecfe4Snicm size_t *, int); 494a29328cSnicm static void tty_keys_callback(int, short, void *); 50f1c69fe1Snicm static int tty_keys_extended_key(struct tty *, const char *, size_t, 51f1c69fe1Snicm size_t *, key_code *); 52a88e9db6Snicm static int tty_keys_mouse(struct tty *, const char *, size_t, size_t *, 53a88e9db6Snicm struct mouse_event *); 5490866723Snicm static int tty_keys_clipboard(struct tty *, const char *, size_t, 5590866723Snicm size_t *); 56f00ac6e6Snicm static int tty_keys_device_attributes(struct tty *, const char *, size_t, 57f00ac6e6Snicm size_t *); 58e0697ebcSnicm static int tty_keys_device_attributes2(struct tty *, const char *, size_t, 59e0697ebcSnicm size_t *); 60ecd9db25Snicm static int tty_keys_extended_device_attributes(struct tty *, const char *, 616110b15cSnicm size_t, size_t *); 62311827fbSnicm 63109d1157Snicm /* A key tree entry. */ 64109d1157Snicm struct tty_key { 65109d1157Snicm char ch; 66109d1157Snicm key_code key; 67109d1157Snicm 68109d1157Snicm struct tty_key *left; 69109d1157Snicm struct tty_key *right; 70109d1157Snicm 71109d1157Snicm struct tty_key *next; 72109d1157Snicm }; 73109d1157Snicm 74f48c717dSnicm /* Default raw keys. */ 75f48c717dSnicm struct tty_default_key_raw { 76311827fbSnicm const char *string; 77885a4698Snicm key_code key; 78311827fbSnicm }; 799883b791Snicm static const struct tty_default_key_raw tty_default_raw_keys[] = { 8016171a6cSnicm /* Application escape. */ 8116171a6cSnicm { "\033O[", '\033' }, 8216171a6cSnicm 83311827fbSnicm /* 844ffe3d28Snicm * Numeric keypad. Just use the vt100 escape sequences here and always 854ffe3d28Snicm * put the terminal into keypad_xmit mode. Translation of numbers 864ffe3d28Snicm * mode/applications mode is done in input-keys.c. 87311827fbSnicm */ 889265d1acSnicm { "\033Oo", KEYC_KP_SLASH|KEYC_KEYPAD }, 899265d1acSnicm { "\033Oj", KEYC_KP_STAR|KEYC_KEYPAD }, 909265d1acSnicm { "\033Om", KEYC_KP_MINUS|KEYC_KEYPAD }, 919265d1acSnicm { "\033Ow", KEYC_KP_SEVEN|KEYC_KEYPAD }, 929265d1acSnicm { "\033Ox", KEYC_KP_EIGHT|KEYC_KEYPAD }, 939265d1acSnicm { "\033Oy", KEYC_KP_NINE|KEYC_KEYPAD }, 949265d1acSnicm { "\033Ok", KEYC_KP_PLUS|KEYC_KEYPAD }, 959265d1acSnicm { "\033Ot", KEYC_KP_FOUR|KEYC_KEYPAD }, 969265d1acSnicm { "\033Ou", KEYC_KP_FIVE|KEYC_KEYPAD }, 979265d1acSnicm { "\033Ov", KEYC_KP_SIX|KEYC_KEYPAD }, 989265d1acSnicm { "\033Oq", KEYC_KP_ONE|KEYC_KEYPAD }, 999265d1acSnicm { "\033Or", KEYC_KP_TWO|KEYC_KEYPAD }, 1009265d1acSnicm { "\033Os", KEYC_KP_THREE|KEYC_KEYPAD }, 1019265d1acSnicm { "\033OM", KEYC_KP_ENTER|KEYC_KEYPAD }, 1029265d1acSnicm { "\033Op", KEYC_KP_ZERO|KEYC_KEYPAD }, 1039265d1acSnicm { "\033On", KEYC_KP_PERIOD|KEYC_KEYPAD }, 10444827713Snicm 1053904313cSnicm /* Arrow keys. */ 1069265d1acSnicm { "\033OA", KEYC_UP|KEYC_CURSOR }, 1079265d1acSnicm { "\033OB", KEYC_DOWN|KEYC_CURSOR }, 1089265d1acSnicm { "\033OC", KEYC_RIGHT|KEYC_CURSOR }, 1099265d1acSnicm { "\033OD", KEYC_LEFT|KEYC_CURSOR }, 1103904313cSnicm 1119265d1acSnicm { "\033[A", KEYC_UP|KEYC_CURSOR }, 1129265d1acSnicm { "\033[B", KEYC_DOWN|KEYC_CURSOR }, 1139265d1acSnicm { "\033[C", KEYC_RIGHT|KEYC_CURSOR }, 1149265d1acSnicm { "\033[D", KEYC_LEFT|KEYC_CURSOR }, 11583980076Snicm 11630233ff7Snicm /* 11730233ff7Snicm * Meta arrow keys. These do not get the IMPLIED_META flag so they 11830233ff7Snicm * don't match the xterm-style meta keys in the output tree - Escape+Up 11930233ff7Snicm * should stay as Escape+Up and not become M-Up. 12030233ff7Snicm */ 12130233ff7Snicm { "\033\033OA", KEYC_UP|KEYC_CURSOR|KEYC_META }, 12230233ff7Snicm { "\033\033OB", KEYC_DOWN|KEYC_CURSOR|KEYC_META }, 12330233ff7Snicm { "\033\033OC", KEYC_RIGHT|KEYC_CURSOR|KEYC_META }, 12430233ff7Snicm { "\033\033OD", KEYC_LEFT|KEYC_CURSOR|KEYC_META }, 12530233ff7Snicm 12630233ff7Snicm { "\033\033[A", KEYC_UP|KEYC_CURSOR|KEYC_META }, 12730233ff7Snicm { "\033\033[B", KEYC_DOWN|KEYC_CURSOR|KEYC_META }, 12830233ff7Snicm { "\033\033[C", KEYC_RIGHT|KEYC_CURSOR|KEYC_META }, 12930233ff7Snicm { "\033\033[D", KEYC_LEFT|KEYC_CURSOR|KEYC_META }, 1307dbc9071Snicm 131c620fc9fSnicm /* Other xterm keys. */ 13299ad9c15Snicm { "\033OH", KEYC_HOME }, 13399ad9c15Snicm { "\033OF", KEYC_END }, 13499ad9c15Snicm 1357dbc9071Snicm { "\033\033OH", KEYC_HOME|KEYC_META|KEYC_IMPLIED_META }, 1367dbc9071Snicm { "\033\033OF", KEYC_END|KEYC_META|KEYC_IMPLIED_META }, 1377dbc9071Snicm 13899ad9c15Snicm { "\033[H", KEYC_HOME }, 13999ad9c15Snicm { "\033[F", KEYC_END }, 14099ad9c15Snicm 1417dbc9071Snicm { "\033\033[H", KEYC_HOME|KEYC_META|KEYC_IMPLIED_META }, 1427dbc9071Snicm { "\033\033[F", KEYC_END|KEYC_META|KEYC_IMPLIED_META }, 1437dbc9071Snicm 144c620fc9fSnicm /* rxvt arrow keys. */ 145f48c717dSnicm { "\033Oa", KEYC_UP|KEYC_CTRL }, 146f48c717dSnicm { "\033Ob", KEYC_DOWN|KEYC_CTRL }, 147f48c717dSnicm { "\033Oc", KEYC_RIGHT|KEYC_CTRL }, 148f48c717dSnicm { "\033Od", KEYC_LEFT|KEYC_CTRL }, 14983980076Snicm 150f48c717dSnicm { "\033[a", KEYC_UP|KEYC_SHIFT }, 151f48c717dSnicm { "\033[b", KEYC_DOWN|KEYC_SHIFT }, 152f48c717dSnicm { "\033[c", KEYC_RIGHT|KEYC_SHIFT }, 153f48c717dSnicm { "\033[d", KEYC_LEFT|KEYC_SHIFT }, 15483980076Snicm 155c620fc9fSnicm /* rxvt function keys. */ 156c620fc9fSnicm { "\033[11~", KEYC_F1 }, 157c620fc9fSnicm { "\033[12~", KEYC_F2 }, 158c620fc9fSnicm { "\033[13~", KEYC_F3 }, 159c620fc9fSnicm { "\033[14~", KEYC_F4 }, 160c620fc9fSnicm { "\033[15~", KEYC_F5 }, 161c620fc9fSnicm { "\033[17~", KEYC_F6 }, 162c620fc9fSnicm { "\033[18~", KEYC_F7 }, 163c620fc9fSnicm { "\033[19~", KEYC_F8 }, 164c620fc9fSnicm { "\033[20~", KEYC_F9 }, 165c620fc9fSnicm { "\033[21~", KEYC_F10 }, 166c620fc9fSnicm 167c620fc9fSnicm { "\033[23~", KEYC_F1|KEYC_SHIFT }, 168c620fc9fSnicm { "\033[24~", KEYC_F2|KEYC_SHIFT }, 169c620fc9fSnicm { "\033[25~", KEYC_F3|KEYC_SHIFT }, 170c620fc9fSnicm { "\033[26~", KEYC_F4|KEYC_SHIFT }, 171c620fc9fSnicm { "\033[28~", KEYC_F5|KEYC_SHIFT }, 172c620fc9fSnicm { "\033[29~", KEYC_F6|KEYC_SHIFT }, 173c620fc9fSnicm { "\033[31~", KEYC_F7|KEYC_SHIFT }, 174c620fc9fSnicm { "\033[32~", KEYC_F8|KEYC_SHIFT }, 175c620fc9fSnicm { "\033[33~", KEYC_F9|KEYC_SHIFT }, 176c620fc9fSnicm { "\033[34~", KEYC_F10|KEYC_SHIFT }, 177c620fc9fSnicm { "\033[23$", KEYC_F11|KEYC_SHIFT }, 178c620fc9fSnicm { "\033[24$", KEYC_F12|KEYC_SHIFT }, 179c620fc9fSnicm 180f48c717dSnicm { "\033[11^", KEYC_F1|KEYC_CTRL }, 181f48c717dSnicm { "\033[12^", KEYC_F2|KEYC_CTRL }, 182f48c717dSnicm { "\033[13^", KEYC_F3|KEYC_CTRL }, 183f48c717dSnicm { "\033[14^", KEYC_F4|KEYC_CTRL }, 184f48c717dSnicm { "\033[15^", KEYC_F5|KEYC_CTRL }, 185f48c717dSnicm { "\033[17^", KEYC_F6|KEYC_CTRL }, 186f48c717dSnicm { "\033[18^", KEYC_F7|KEYC_CTRL }, 187f48c717dSnicm { "\033[19^", KEYC_F8|KEYC_CTRL }, 188f48c717dSnicm { "\033[20^", KEYC_F9|KEYC_CTRL }, 189f48c717dSnicm { "\033[21^", KEYC_F10|KEYC_CTRL }, 190f48c717dSnicm { "\033[23^", KEYC_F11|KEYC_CTRL }, 191f48c717dSnicm { "\033[24^", KEYC_F12|KEYC_CTRL }, 19283980076Snicm 193f48c717dSnicm { "\033[11@", KEYC_F1|KEYC_CTRL|KEYC_SHIFT }, 194f48c717dSnicm { "\033[12@", KEYC_F2|KEYC_CTRL|KEYC_SHIFT }, 195f48c717dSnicm { "\033[13@", KEYC_F3|KEYC_CTRL|KEYC_SHIFT }, 196f48c717dSnicm { "\033[14@", KEYC_F4|KEYC_CTRL|KEYC_SHIFT }, 197f48c717dSnicm { "\033[15@", KEYC_F5|KEYC_CTRL|KEYC_SHIFT }, 198f48c717dSnicm { "\033[17@", KEYC_F6|KEYC_CTRL|KEYC_SHIFT }, 199f48c717dSnicm { "\033[18@", KEYC_F7|KEYC_CTRL|KEYC_SHIFT }, 200f48c717dSnicm { "\033[19@", KEYC_F8|KEYC_CTRL|KEYC_SHIFT }, 201f48c717dSnicm { "\033[20@", KEYC_F9|KEYC_CTRL|KEYC_SHIFT }, 202f48c717dSnicm { "\033[21@", KEYC_F10|KEYC_CTRL|KEYC_SHIFT }, 203f48c717dSnicm { "\033[23@", KEYC_F11|KEYC_CTRL|KEYC_SHIFT }, 204f48c717dSnicm { "\033[24@", KEYC_F12|KEYC_CTRL|KEYC_SHIFT }, 2056ddcc08fSnicm 2066ddcc08fSnicm /* Focus tracking. */ 2076ddcc08fSnicm { "\033[I", KEYC_FOCUS_IN }, 2086ddcc08fSnicm { "\033[O", KEYC_FOCUS_OUT }, 209b99601f2Snicm 210b99601f2Snicm /* Paste keys. */ 211b99601f2Snicm { "\033[200~", KEYC_PASTE_START }, 212b99601f2Snicm { "\033[201~", KEYC_PASTE_END }, 2133c4ca0e6Snicm 2143c4ca0e6Snicm /* Extended keys. */ 2158f86830fSnicm { "\033[1;5Z", '\011'|KEYC_CTRL|KEYC_SHIFT }, 216311827fbSnicm }; 217311827fbSnicm 2189265d1acSnicm /* Default xterm keys. */ 2199265d1acSnicm struct tty_default_key_xterm { 2209265d1acSnicm const char *template; 2219265d1acSnicm key_code key; 2229265d1acSnicm }; 2239265d1acSnicm static const struct tty_default_key_xterm tty_default_xterm_keys[] = { 2249265d1acSnicm { "\033[1;_P", KEYC_F1 }, 2259265d1acSnicm { "\033O1;_P", KEYC_F1 }, 2269265d1acSnicm { "\033O_P", KEYC_F1 }, 2279265d1acSnicm { "\033[1;_Q", KEYC_F2 }, 2289265d1acSnicm { "\033O1;_Q", KEYC_F2 }, 2299265d1acSnicm { "\033O_Q", KEYC_F2 }, 2309265d1acSnicm { "\033[1;_R", KEYC_F3 }, 2319265d1acSnicm { "\033O1;_R", KEYC_F3 }, 2329265d1acSnicm { "\033O_R", KEYC_F3 }, 2339265d1acSnicm { "\033[1;_S", KEYC_F4 }, 2349265d1acSnicm { "\033O1;_S", KEYC_F4 }, 2359265d1acSnicm { "\033O_S", KEYC_F4 }, 2369265d1acSnicm { "\033[15;_~", KEYC_F5 }, 2379265d1acSnicm { "\033[17;_~", KEYC_F6 }, 2389265d1acSnicm { "\033[18;_~", KEYC_F7 }, 2399265d1acSnicm { "\033[19;_~", KEYC_F8 }, 2409265d1acSnicm { "\033[20;_~", KEYC_F9 }, 2419265d1acSnicm { "\033[21;_~", KEYC_F10 }, 2429265d1acSnicm { "\033[23;_~", KEYC_F11 }, 2439265d1acSnicm { "\033[24;_~", KEYC_F12 }, 2449265d1acSnicm { "\033[1;_A", KEYC_UP }, 2459265d1acSnicm { "\033[1;_B", KEYC_DOWN }, 2469265d1acSnicm { "\033[1;_C", KEYC_RIGHT }, 2479265d1acSnicm { "\033[1;_D", KEYC_LEFT }, 2489265d1acSnicm { "\033[1;_H", KEYC_HOME }, 2499265d1acSnicm { "\033[1;_F", KEYC_END }, 2509265d1acSnicm { "\033[5;_~", KEYC_PPAGE }, 2519265d1acSnicm { "\033[6;_~", KEYC_NPAGE }, 2529265d1acSnicm { "\033[2;_~", KEYC_IC }, 2539265d1acSnicm { "\033[3;_~", KEYC_DC }, 2549265d1acSnicm }; 2559265d1acSnicm static const key_code tty_default_xterm_modifiers[] = { 2569265d1acSnicm 0, 2579265d1acSnicm 0, 2585416581eSnicm KEYC_SHIFT, 2595416581eSnicm KEYC_META|KEYC_IMPLIED_META, 2605416581eSnicm KEYC_SHIFT|KEYC_META|KEYC_IMPLIED_META, 2615416581eSnicm KEYC_CTRL, 2625416581eSnicm KEYC_SHIFT|KEYC_CTRL, 2635416581eSnicm KEYC_META|KEYC_IMPLIED_META|KEYC_CTRL, 2649e79a802Snicm KEYC_SHIFT|KEYC_META|KEYC_IMPLIED_META|KEYC_CTRL, 2659e79a802Snicm KEYC_META|KEYC_IMPLIED_META 2669265d1acSnicm }; 2679265d1acSnicm 268fd054becSnicm /* 2699265d1acSnicm * Default terminfo(5) keys. Any keys that have builtin modifiers (that is, 2709265d1acSnicm * where the key itself contains the modifiers) has the KEYC_XTERM flag set so 2719265d1acSnicm * a leading escape is not treated as meta (and probably removed). 272fd054becSnicm */ 273f48c717dSnicm struct tty_default_key_code { 274f48c717dSnicm enum tty_code_code code; 275885a4698Snicm key_code key; 276f48c717dSnicm }; 2779883b791Snicm static const struct tty_default_key_code tty_default_code_keys[] = { 278f48c717dSnicm /* Function keys. */ 279f48c717dSnicm { TTYC_KF1, KEYC_F1 }, 280f48c717dSnicm { TTYC_KF2, KEYC_F2 }, 281f48c717dSnicm { TTYC_KF3, KEYC_F3 }, 282f48c717dSnicm { TTYC_KF4, KEYC_F4 }, 283f48c717dSnicm { TTYC_KF5, KEYC_F5 }, 284f48c717dSnicm { TTYC_KF6, KEYC_F6 }, 285f48c717dSnicm { TTYC_KF7, KEYC_F7 }, 286f48c717dSnicm { TTYC_KF8, KEYC_F8 }, 287f48c717dSnicm { TTYC_KF9, KEYC_F9 }, 288f48c717dSnicm { TTYC_KF10, KEYC_F10 }, 289f48c717dSnicm { TTYC_KF11, KEYC_F11 }, 290f48c717dSnicm { TTYC_KF12, KEYC_F12 }, 291dfe29c0aSnicm 2925416581eSnicm { TTYC_KF13, KEYC_F1|KEYC_SHIFT }, 2935416581eSnicm { TTYC_KF14, KEYC_F2|KEYC_SHIFT }, 2945416581eSnicm { TTYC_KF15, KEYC_F3|KEYC_SHIFT }, 2955416581eSnicm { TTYC_KF16, KEYC_F4|KEYC_SHIFT }, 2965416581eSnicm { TTYC_KF17, KEYC_F5|KEYC_SHIFT }, 2975416581eSnicm { TTYC_KF18, KEYC_F6|KEYC_SHIFT }, 2985416581eSnicm { TTYC_KF19, KEYC_F7|KEYC_SHIFT }, 2995416581eSnicm { TTYC_KF20, KEYC_F8|KEYC_SHIFT }, 3005416581eSnicm { TTYC_KF21, KEYC_F9|KEYC_SHIFT }, 3015416581eSnicm { TTYC_KF22, KEYC_F10|KEYC_SHIFT }, 3025416581eSnicm { TTYC_KF23, KEYC_F11|KEYC_SHIFT }, 3035416581eSnicm { TTYC_KF24, KEYC_F12|KEYC_SHIFT }, 304dfe29c0aSnicm 3055416581eSnicm { TTYC_KF25, KEYC_F1|KEYC_CTRL }, 3065416581eSnicm { TTYC_KF26, KEYC_F2|KEYC_CTRL }, 3075416581eSnicm { TTYC_KF27, KEYC_F3|KEYC_CTRL }, 3085416581eSnicm { TTYC_KF28, KEYC_F4|KEYC_CTRL }, 3095416581eSnicm { TTYC_KF29, KEYC_F5|KEYC_CTRL }, 3105416581eSnicm { TTYC_KF30, KEYC_F6|KEYC_CTRL }, 3115416581eSnicm { TTYC_KF31, KEYC_F7|KEYC_CTRL }, 3125416581eSnicm { TTYC_KF32, KEYC_F8|KEYC_CTRL }, 3135416581eSnicm { TTYC_KF33, KEYC_F9|KEYC_CTRL }, 3145416581eSnicm { TTYC_KF34, KEYC_F10|KEYC_CTRL }, 3155416581eSnicm { TTYC_KF35, KEYC_F11|KEYC_CTRL }, 3165416581eSnicm { TTYC_KF36, KEYC_F12|KEYC_CTRL }, 317dfe29c0aSnicm 3185416581eSnicm { TTYC_KF37, KEYC_F1|KEYC_SHIFT|KEYC_CTRL }, 3195416581eSnicm { TTYC_KF38, KEYC_F2|KEYC_SHIFT|KEYC_CTRL }, 3205416581eSnicm { TTYC_KF39, KEYC_F3|KEYC_SHIFT|KEYC_CTRL }, 3215416581eSnicm { TTYC_KF40, KEYC_F4|KEYC_SHIFT|KEYC_CTRL }, 3225416581eSnicm { TTYC_KF41, KEYC_F5|KEYC_SHIFT|KEYC_CTRL }, 3235416581eSnicm { TTYC_KF42, KEYC_F6|KEYC_SHIFT|KEYC_CTRL }, 3245416581eSnicm { TTYC_KF43, KEYC_F7|KEYC_SHIFT|KEYC_CTRL }, 3255416581eSnicm { TTYC_KF44, KEYC_F8|KEYC_SHIFT|KEYC_CTRL }, 3265416581eSnicm { TTYC_KF45, KEYC_F9|KEYC_SHIFT|KEYC_CTRL }, 3275416581eSnicm { TTYC_KF46, KEYC_F10|KEYC_SHIFT|KEYC_CTRL }, 3285416581eSnicm { TTYC_KF47, KEYC_F11|KEYC_SHIFT|KEYC_CTRL }, 3295416581eSnicm { TTYC_KF48, KEYC_F12|KEYC_SHIFT|KEYC_CTRL }, 330dfe29c0aSnicm 3315416581eSnicm { TTYC_KF49, KEYC_F1|KEYC_META|KEYC_IMPLIED_META }, 3325416581eSnicm { TTYC_KF50, KEYC_F2|KEYC_META|KEYC_IMPLIED_META }, 3335416581eSnicm { TTYC_KF51, KEYC_F3|KEYC_META|KEYC_IMPLIED_META }, 3345416581eSnicm { TTYC_KF52, KEYC_F4|KEYC_META|KEYC_IMPLIED_META }, 3355416581eSnicm { TTYC_KF53, KEYC_F5|KEYC_META|KEYC_IMPLIED_META }, 3365416581eSnicm { TTYC_KF54, KEYC_F6|KEYC_META|KEYC_IMPLIED_META }, 3375416581eSnicm { TTYC_KF55, KEYC_F7|KEYC_META|KEYC_IMPLIED_META }, 3385416581eSnicm { TTYC_KF56, KEYC_F8|KEYC_META|KEYC_IMPLIED_META }, 3395416581eSnicm { TTYC_KF57, KEYC_F9|KEYC_META|KEYC_IMPLIED_META }, 3405416581eSnicm { TTYC_KF58, KEYC_F10|KEYC_META|KEYC_IMPLIED_META }, 3415416581eSnicm { TTYC_KF59, KEYC_F11|KEYC_META|KEYC_IMPLIED_META }, 3425416581eSnicm { TTYC_KF60, KEYC_F12|KEYC_META|KEYC_IMPLIED_META }, 343dfe29c0aSnicm 3445416581eSnicm { TTYC_KF61, KEYC_F1|KEYC_META|KEYC_IMPLIED_META|KEYC_SHIFT }, 3455416581eSnicm { TTYC_KF62, KEYC_F2|KEYC_META|KEYC_IMPLIED_META|KEYC_SHIFT }, 3465416581eSnicm { TTYC_KF63, KEYC_F3|KEYC_META|KEYC_IMPLIED_META|KEYC_SHIFT }, 347dfe29c0aSnicm 348f48c717dSnicm { TTYC_KICH1, KEYC_IC }, 349f48c717dSnicm { TTYC_KDCH1, KEYC_DC }, 350f48c717dSnicm { TTYC_KHOME, KEYC_HOME }, 351f48c717dSnicm { TTYC_KEND, KEYC_END }, 352f48c717dSnicm { TTYC_KNP, KEYC_NPAGE }, 353f48c717dSnicm { TTYC_KPP, KEYC_PPAGE }, 354f48c717dSnicm { TTYC_KCBT, KEYC_BTAB }, 355f48c717dSnicm 356f48c717dSnicm /* Arrow keys from terminfo. */ 3579265d1acSnicm { TTYC_KCUU1, KEYC_UP|KEYC_CURSOR }, 3589265d1acSnicm { TTYC_KCUD1, KEYC_DOWN|KEYC_CURSOR }, 3599265d1acSnicm { TTYC_KCUB1, KEYC_LEFT|KEYC_CURSOR }, 3609265d1acSnicm { TTYC_KCUF1, KEYC_RIGHT|KEYC_CURSOR }, 361f48c717dSnicm 362fd054becSnicm /* Key and modifier capabilities. */ 3635416581eSnicm { TTYC_KDC2, KEYC_DC|KEYC_SHIFT }, 3645416581eSnicm { TTYC_KDC3, KEYC_DC|KEYC_META|KEYC_IMPLIED_META }, 3655416581eSnicm { TTYC_KDC4, KEYC_DC|KEYC_SHIFT|KEYC_META|KEYC_IMPLIED_META }, 3665416581eSnicm { TTYC_KDC5, KEYC_DC|KEYC_CTRL }, 3675416581eSnicm { TTYC_KDC6, KEYC_DC|KEYC_SHIFT|KEYC_CTRL }, 3685416581eSnicm { TTYC_KDC7, KEYC_DC|KEYC_META|KEYC_IMPLIED_META|KEYC_CTRL }, 3695416581eSnicm { TTYC_KIND, KEYC_DOWN|KEYC_SHIFT }, 3705416581eSnicm { TTYC_KDN2, KEYC_DOWN|KEYC_SHIFT }, 3715416581eSnicm { TTYC_KDN3, KEYC_DOWN|KEYC_META|KEYC_IMPLIED_META }, 3725416581eSnicm { TTYC_KDN4, KEYC_DOWN|KEYC_SHIFT|KEYC_META|KEYC_IMPLIED_META }, 3735416581eSnicm { TTYC_KDN5, KEYC_DOWN|KEYC_CTRL }, 3745416581eSnicm { TTYC_KDN6, KEYC_DOWN|KEYC_SHIFT|KEYC_CTRL }, 3755416581eSnicm { TTYC_KDN7, KEYC_DOWN|KEYC_META|KEYC_IMPLIED_META|KEYC_CTRL }, 3765416581eSnicm { TTYC_KEND2, KEYC_END|KEYC_SHIFT }, 3775416581eSnicm { TTYC_KEND3, KEYC_END|KEYC_META|KEYC_IMPLIED_META }, 3785416581eSnicm { TTYC_KEND4, KEYC_END|KEYC_SHIFT|KEYC_META|KEYC_IMPLIED_META }, 3795416581eSnicm { TTYC_KEND5, KEYC_END|KEYC_CTRL }, 3805416581eSnicm { TTYC_KEND6, KEYC_END|KEYC_SHIFT|KEYC_CTRL }, 3815416581eSnicm { TTYC_KEND7, KEYC_END|KEYC_META|KEYC_IMPLIED_META|KEYC_CTRL }, 3825416581eSnicm { TTYC_KHOM2, KEYC_HOME|KEYC_SHIFT }, 3835416581eSnicm { TTYC_KHOM3, KEYC_HOME|KEYC_META|KEYC_IMPLIED_META }, 3845416581eSnicm { TTYC_KHOM4, KEYC_HOME|KEYC_SHIFT|KEYC_META|KEYC_IMPLIED_META }, 3855416581eSnicm { TTYC_KHOM5, KEYC_HOME|KEYC_CTRL }, 3865416581eSnicm { TTYC_KHOM6, KEYC_HOME|KEYC_SHIFT|KEYC_CTRL }, 3875416581eSnicm { TTYC_KHOM7, KEYC_HOME|KEYC_META|KEYC_IMPLIED_META|KEYC_CTRL }, 3885416581eSnicm { TTYC_KIC2, KEYC_IC|KEYC_SHIFT }, 3895416581eSnicm { TTYC_KIC3, KEYC_IC|KEYC_META|KEYC_IMPLIED_META }, 3905416581eSnicm { TTYC_KIC4, KEYC_IC|KEYC_SHIFT|KEYC_META|KEYC_IMPLIED_META }, 3915416581eSnicm { TTYC_KIC5, KEYC_IC|KEYC_CTRL }, 3925416581eSnicm { TTYC_KIC6, KEYC_IC|KEYC_SHIFT|KEYC_CTRL }, 3935416581eSnicm { TTYC_KIC7, KEYC_IC|KEYC_META|KEYC_IMPLIED_META|KEYC_CTRL }, 3945416581eSnicm { TTYC_KLFT2, KEYC_LEFT|KEYC_SHIFT }, 3955416581eSnicm { TTYC_KLFT3, KEYC_LEFT|KEYC_META|KEYC_IMPLIED_META }, 3965416581eSnicm { TTYC_KLFT4, KEYC_LEFT|KEYC_SHIFT|KEYC_META|KEYC_IMPLIED_META }, 3975416581eSnicm { TTYC_KLFT5, KEYC_LEFT|KEYC_CTRL }, 3985416581eSnicm { TTYC_KLFT6, KEYC_LEFT|KEYC_SHIFT|KEYC_CTRL }, 3995416581eSnicm { TTYC_KLFT7, KEYC_LEFT|KEYC_META|KEYC_IMPLIED_META|KEYC_CTRL }, 4005416581eSnicm { TTYC_KNXT2, KEYC_NPAGE|KEYC_SHIFT }, 4015416581eSnicm { TTYC_KNXT3, KEYC_NPAGE|KEYC_META|KEYC_IMPLIED_META }, 4025416581eSnicm { TTYC_KNXT4, KEYC_NPAGE|KEYC_SHIFT|KEYC_META|KEYC_IMPLIED_META }, 4035416581eSnicm { TTYC_KNXT5, KEYC_NPAGE|KEYC_CTRL }, 4045416581eSnicm { TTYC_KNXT6, KEYC_NPAGE|KEYC_SHIFT|KEYC_CTRL }, 4055416581eSnicm { TTYC_KNXT7, KEYC_NPAGE|KEYC_META|KEYC_IMPLIED_META|KEYC_CTRL }, 4065416581eSnicm { TTYC_KPRV2, KEYC_PPAGE|KEYC_SHIFT }, 4075416581eSnicm { TTYC_KPRV3, KEYC_PPAGE|KEYC_META|KEYC_IMPLIED_META }, 4085416581eSnicm { TTYC_KPRV4, KEYC_PPAGE|KEYC_SHIFT|KEYC_META|KEYC_IMPLIED_META }, 4095416581eSnicm { TTYC_KPRV5, KEYC_PPAGE|KEYC_CTRL }, 4105416581eSnicm { TTYC_KPRV6, KEYC_PPAGE|KEYC_SHIFT|KEYC_CTRL }, 4115416581eSnicm { TTYC_KPRV7, KEYC_PPAGE|KEYC_META|KEYC_IMPLIED_META|KEYC_CTRL }, 4125416581eSnicm { TTYC_KRIT2, KEYC_RIGHT|KEYC_SHIFT }, 4135416581eSnicm { TTYC_KRIT3, KEYC_RIGHT|KEYC_META|KEYC_IMPLIED_META }, 4145416581eSnicm { TTYC_KRIT4, KEYC_RIGHT|KEYC_SHIFT|KEYC_META|KEYC_IMPLIED_META }, 4155416581eSnicm { TTYC_KRIT5, KEYC_RIGHT|KEYC_CTRL }, 4165416581eSnicm { TTYC_KRIT6, KEYC_RIGHT|KEYC_SHIFT|KEYC_CTRL }, 4175416581eSnicm { TTYC_KRIT7, KEYC_RIGHT|KEYC_META|KEYC_IMPLIED_META|KEYC_CTRL }, 4185416581eSnicm { TTYC_KRI, KEYC_UP|KEYC_SHIFT }, 4195416581eSnicm { TTYC_KUP2, KEYC_UP|KEYC_SHIFT }, 4205416581eSnicm { TTYC_KUP3, KEYC_UP|KEYC_META|KEYC_IMPLIED_META }, 4215416581eSnicm { TTYC_KUP4, KEYC_UP|KEYC_SHIFT|KEYC_META|KEYC_IMPLIED_META }, 4225416581eSnicm { TTYC_KUP5, KEYC_UP|KEYC_CTRL }, 4235416581eSnicm { TTYC_KUP6, KEYC_UP|KEYC_SHIFT|KEYC_CTRL }, 4245416581eSnicm { TTYC_KUP7, KEYC_UP|KEYC_META|KEYC_IMPLIED_META|KEYC_CTRL }, 425f48c717dSnicm }; 426f48c717dSnicm 427f48c717dSnicm /* Add key to tree. */ 4284a29328cSnicm static void 429885a4698Snicm tty_keys_add(struct tty *tty, const char *s, key_code key) 430311827fbSnicm { 4313904313cSnicm struct tty_key *tk; 432e8bb025fSnicm size_t size; 4333904313cSnicm const char *keystr; 434311827fbSnicm 4355416581eSnicm keystr = key_string_lookup_key(key, 1); 4363904313cSnicm if ((tk = tty_keys_find(tty, s, strlen(s), &size)) == NULL) { 437885a4698Snicm log_debug("new key %s: 0x%llx (%s)", s, key, keystr); 438e8bb025fSnicm tty_keys_add1(&tty->key_tree, s, key); 4393904313cSnicm } else { 440885a4698Snicm log_debug("replacing key %s: 0x%llx (%s)", s, key, keystr); 4413904313cSnicm tk->key = key; 442e8bb025fSnicm } 443e8bb025fSnicm } 444e8bb025fSnicm 445e8bb025fSnicm /* Add next node to the tree. */ 4464a29328cSnicm static void 447885a4698Snicm tty_keys_add1(struct tty_key **tkp, const char *s, key_code key) 448e8bb025fSnicm { 449e8bb025fSnicm struct tty_key *tk; 450e8bb025fSnicm 451e8bb025fSnicm /* Allocate a tree entry if there isn't one already. */ 452e8bb025fSnicm tk = *tkp; 453e8bb025fSnicm if (tk == NULL) { 454e8bb025fSnicm tk = *tkp = xcalloc(1, sizeof *tk); 455e8bb025fSnicm tk->ch = *s; 4560bd9fd8fSnicm tk->key = KEYC_UNKNOWN; 457e8bb025fSnicm } 458e8bb025fSnicm 459e8bb025fSnicm /* Find the next entry. */ 460e8bb025fSnicm if (*s == tk->ch) { 461e8bb025fSnicm /* Move forward in string. */ 462e8bb025fSnicm s++; 463e8bb025fSnicm 464e8bb025fSnicm /* If this is the end of the string, no more is necessary. */ 465e8bb025fSnicm if (*s == '\0') { 466311827fbSnicm tk->key = key; 467311827fbSnicm return; 468311827fbSnicm } 469311827fbSnicm 470e8bb025fSnicm /* Use the child tree for the next character. */ 471e8bb025fSnicm tkp = &tk->next; 472e8bb025fSnicm } else { 473e8bb025fSnicm if (*s < tk->ch) 474e8bb025fSnicm tkp = &tk->left; 475e8bb025fSnicm else if (*s > tk->ch) 476e8bb025fSnicm tkp = &tk->right; 477311827fbSnicm } 478311827fbSnicm 479e8bb025fSnicm /* And recurse to add it. */ 480e8bb025fSnicm tty_keys_add1(tkp, s, key); 481e8bb025fSnicm } 482e8bb025fSnicm 483e8bb025fSnicm /* Initialise a key tree from the table. */ 484311827fbSnicm void 485f48c717dSnicm tty_keys_build(struct tty *tty) 486311827fbSnicm { 487f48c717dSnicm const struct tty_default_key_raw *tdkr; 4889265d1acSnicm const struct tty_default_key_xterm *tdkx; 489f48c717dSnicm const struct tty_default_key_code *tdkc; 4909265d1acSnicm u_int i, j; 49161b6b4dcSnicm const char *s; 492b846cb6cSnicm struct options_entry *o; 49339052edfSnicm struct options_array_item *a; 49461b6b4dcSnicm union options_value *ov; 4959265d1acSnicm char copy[16]; 4969265d1acSnicm key_code key; 497311827fbSnicm 498f48c717dSnicm if (tty->key_tree != NULL) 499998b1aadSnicm tty_keys_free(tty); 500e8bb025fSnicm tty->key_tree = NULL; 501311827fbSnicm 5029265d1acSnicm for (i = 0; i < nitems(tty_default_xterm_keys); i++) { 5039265d1acSnicm tdkx = &tty_default_xterm_keys[i]; 5049265d1acSnicm for (j = 2; j < nitems(tty_default_xterm_modifiers); j++) { 5059265d1acSnicm strlcpy(copy, tdkx->template, sizeof copy); 5069265d1acSnicm copy[strcspn(copy, "_")] = '0' + j; 5079265d1acSnicm 5089265d1acSnicm key = tdkx->key|tty_default_xterm_modifiers[j]; 5099265d1acSnicm tty_keys_add(tty, copy, key); 5109265d1acSnicm } 5119265d1acSnicm } 512f48c717dSnicm for (i = 0; i < nitems(tty_default_raw_keys); i++) { 513f48c717dSnicm tdkr = &tty_default_raw_keys[i]; 514f48c717dSnicm 515f48c717dSnicm s = tdkr->string; 516c740be85Snicm if (*s != '\0') 517c740be85Snicm tty_keys_add(tty, s, tdkr->key); 518e8bb025fSnicm } 519f48c717dSnicm for (i = 0; i < nitems(tty_default_code_keys); i++) { 520f48c717dSnicm tdkc = &tty_default_code_keys[i]; 521311827fbSnicm 522f48c717dSnicm s = tty_term_string(tty->term, tdkc->code); 523c740be85Snicm if (*s != '\0') 524c740be85Snicm tty_keys_add(tty, s, tdkc->key); 525f48c717dSnicm 526311827fbSnicm } 527b846cb6cSnicm 528b846cb6cSnicm o = options_get(global_options, "user-keys"); 52939052edfSnicm if (o != NULL) { 53039052edfSnicm a = options_array_first(o); 53139052edfSnicm while (a != NULL) { 532a35741c0Snicm i = options_array_item_index(a); 53361b6b4dcSnicm ov = options_array_item_value(a); 53461b6b4dcSnicm tty_keys_add(tty, ov->string, KEYC_USER + i); 53539052edfSnicm a = options_array_next(a); 536b846cb6cSnicm } 537b846cb6cSnicm } 538311827fbSnicm } 539311827fbSnicm 540e8bb025fSnicm /* Free the entire key tree. */ 541311827fbSnicm void 542311827fbSnicm tty_keys_free(struct tty *tty) 543311827fbSnicm { 544e8bb025fSnicm tty_keys_free1(tty->key_tree); 545311827fbSnicm } 546311827fbSnicm 547e8bb025fSnicm /* Free a single key. */ 5484a29328cSnicm static void 549e8bb025fSnicm tty_keys_free1(struct tty_key *tk) 550311827fbSnicm { 551e8bb025fSnicm if (tk->next != NULL) 552e8bb025fSnicm tty_keys_free1(tk->next); 553e8bb025fSnicm if (tk->left != NULL) 554e8bb025fSnicm tty_keys_free1(tk->left); 555e8bb025fSnicm if (tk->right != NULL) 556e8bb025fSnicm tty_keys_free1(tk->right); 5577d053cf9Snicm free(tk); 558e8bb025fSnicm } 559e8bb025fSnicm 560e8bb025fSnicm /* Lookup a key in the tree. */ 5614a29328cSnicm static struct tty_key * 562e8bb025fSnicm tty_keys_find(struct tty *tty, const char *buf, size_t len, size_t *size) 563e8bb025fSnicm { 564e8bb025fSnicm *size = 0; 565e8bb025fSnicm return (tty_keys_find1(tty->key_tree, buf, len, size)); 566e8bb025fSnicm } 567e8bb025fSnicm 568e8bb025fSnicm /* Find the next node. */ 5694a29328cSnicm static struct tty_key * 570e8bb025fSnicm tty_keys_find1(struct tty_key *tk, const char *buf, size_t len, size_t *size) 571e8bb025fSnicm { 5727fa57c5bSnicm /* If no data, no match. */ 5737fa57c5bSnicm if (len == 0) 5747fa57c5bSnicm return (NULL); 5757fa57c5bSnicm 576e8bb025fSnicm /* If the node is NULL, this is the end of the tree. No match. */ 577e8bb025fSnicm if (tk == NULL) 578e8bb025fSnicm return (NULL); 579e8bb025fSnicm 580e8bb025fSnicm /* Pick the next in the sequence. */ 581e8bb025fSnicm if (tk->ch == *buf) { 582e8bb025fSnicm /* Move forward in the string. */ 583e8bb025fSnicm buf++; len--; 584e8bb025fSnicm (*size)++; 585e8bb025fSnicm 586e8bb025fSnicm /* At the end of the string, return the current node. */ 5870bd9fd8fSnicm if (len == 0 || (tk->next == NULL && tk->key != KEYC_UNKNOWN)) 588311827fbSnicm return (tk); 589311827fbSnicm 590e8bb025fSnicm /* Move into the next tree for the following character. */ 591e8bb025fSnicm tk = tk->next; 592e8bb025fSnicm } else { 593e8bb025fSnicm if (*buf < tk->ch) 594e8bb025fSnicm tk = tk->left; 595e8bb025fSnicm else if (*buf > tk->ch) 596e8bb025fSnicm tk = tk->right; 597311827fbSnicm } 598311827fbSnicm 599e8bb025fSnicm /* Move to the next in the tree. */ 600e8bb025fSnicm return (tty_keys_find1(tk, buf, len, size)); 601e8bb025fSnicm } 602e8bb025fSnicm 6034a29328cSnicm /* Look up part of the next key. */ 6044a29328cSnicm static int 6054a29328cSnicm tty_keys_next1(struct tty *tty, const char *buf, size_t len, key_code *key, 606fa0ecfe4Snicm size_t *size, int expired) 6074a29328cSnicm { 6081f2e7474Snicm struct client *c = tty->client; 6094a29328cSnicm struct tty_key *tk, *tk1; 6104a29328cSnicm struct utf8_data ud; 6114a29328cSnicm enum utf8_state more; 6126852c63bSnicm utf8_char uc; 6134a29328cSnicm u_int i; 6144a29328cSnicm 6151f2e7474Snicm log_debug("%s: next key is %zu (%.*s) (expired=%d)", c->name, len, 6161f2e7474Snicm (int)len, buf, expired); 6174a29328cSnicm 6184a29328cSnicm /* Is this a known key? */ 6194a29328cSnicm tk = tty_keys_find(tty, buf, len, size); 620fa0ecfe4Snicm if (tk != NULL && tk->key != KEYC_UNKNOWN) { 6214a29328cSnicm tk1 = tk; 6224a29328cSnicm do 6231f2e7474Snicm log_debug("%s: keys in list: %#llx", c->name, tk1->key); 6244a29328cSnicm while ((tk1 = tk1->next) != NULL); 625fa0ecfe4Snicm if (tk->next != NULL && !expired) 626fa0ecfe4Snicm return (1); 6274a29328cSnicm *key = tk->key; 628fa0ecfe4Snicm return (0); 6294a29328cSnicm } 6304a29328cSnicm 6314a29328cSnicm /* Is this valid UTF-8? */ 6324a29328cSnicm more = utf8_open(&ud, (u_char)*buf); 6334a29328cSnicm if (more == UTF8_MORE) { 6344a29328cSnicm *size = ud.size; 635fa0ecfe4Snicm if (len < ud.size) { 636fa0ecfe4Snicm if (!expired) 6374a29328cSnicm return (1); 638fa0ecfe4Snicm return (-1); 639fa0ecfe4Snicm } 6404a29328cSnicm for (i = 1; i < ud.size; i++) 6414a29328cSnicm more = utf8_append(&ud, (u_char)buf[i]); 6424a29328cSnicm if (more != UTF8_DONE) 643fa0ecfe4Snicm return (-1); 6444a29328cSnicm 6456852c63bSnicm if (utf8_from_data(&ud, &uc) != UTF8_DONE) 646fa0ecfe4Snicm return (-1); 6476852c63bSnicm *key = uc; 6484a29328cSnicm 6491f2e7474Snicm log_debug("%s: UTF-8 key %.*s %#llx", c->name, (int)ud.size, 6506852c63bSnicm ud.data, *key); 6514a29328cSnicm return (0); 6524a29328cSnicm } 6534a29328cSnicm 6544a29328cSnicm return (-1); 6554a29328cSnicm } 6564a29328cSnicm 657a1c6091aSnicm /* Process window size change escape sequences. */ 658a1c6091aSnicm static int 659a1c6091aSnicm tty_keys_winsz(struct tty *tty, const char *buf, size_t len, size_t *size) 660a1c6091aSnicm { 661a1c6091aSnicm struct client *c = tty->client; 662a1c6091aSnicm size_t end; 663a1c6091aSnicm char tmp[64]; 664a1c6091aSnicm u_int sx, sy, xpixel, ypixel, char_x, char_y; 665a1c6091aSnicm 666a1c6091aSnicm *size = 0; 667a1c6091aSnicm 668a1c6091aSnicm /* If we did not request this, ignore it. */ 669a1c6091aSnicm if (!(tty->flags & TTY_WINSIZEQUERY)) 670a1c6091aSnicm return (-1); 671a1c6091aSnicm 672a1c6091aSnicm /* First two bytes are always \033[. */ 673a1c6091aSnicm if (buf[0] != '\033') 674a1c6091aSnicm return (-1); 675a1c6091aSnicm if (len == 1) 676a1c6091aSnicm return (1); 677a1c6091aSnicm if (buf[1] != '[') 678a1c6091aSnicm return (-1); 679a1c6091aSnicm if (len == 2) 680a1c6091aSnicm return (1); 681a1c6091aSnicm 682a1c6091aSnicm /* 683a1c6091aSnicm * Stop at either 't' or anything that isn't a 684a1c6091aSnicm * number or ';'. 685a1c6091aSnicm */ 686a1c6091aSnicm for (end = 2; end < len && end != sizeof tmp; end++) { 687a1c6091aSnicm if (buf[end] == 't') 688a1c6091aSnicm break; 689a1c6091aSnicm if (!isdigit((u_char)buf[end]) && buf[end] != ';') 690a1c6091aSnicm break; 691a1c6091aSnicm } 692a1c6091aSnicm if (end == len) 693a1c6091aSnicm return (1); 694a1c6091aSnicm if (end == sizeof tmp || buf[end] != 't') 695a1c6091aSnicm return (-1); 696a1c6091aSnicm 697a1c6091aSnicm /* Copy to the buffer. */ 698a1c6091aSnicm memcpy(tmp, buf + 2, end - 2); 699a1c6091aSnicm tmp[end - 2] = '\0'; 700a1c6091aSnicm 701a1c6091aSnicm /* Try to parse the window size sequence. */ 702a1c6091aSnicm if (sscanf(tmp, "8;%u;%u", &sy, &sx) == 2) { 703a1c6091aSnicm /* Window size in characters. */ 704a1c6091aSnicm tty_set_size(tty, sx, sy, tty->xpixel, tty->ypixel); 705a1c6091aSnicm 706a1c6091aSnicm *size = end + 1; 707a1c6091aSnicm return (0); 708a1c6091aSnicm } else if (sscanf(tmp, "4;%u;%u", &ypixel, &xpixel) == 2) { 709a1c6091aSnicm /* Window size in pixels. */ 710a1c6091aSnicm char_x = (xpixel && tty->sx) ? xpixel / tty->sx : 0; 711a1c6091aSnicm char_y = (ypixel && tty->sy) ? ypixel / tty->sy : 0; 712a1c6091aSnicm tty_set_size(tty, tty->sx, tty->sy, char_x, char_y); 713a1c6091aSnicm tty_invalidate(tty); 714a1c6091aSnicm 715a1c6091aSnicm tty->flags &= ~TTY_WINSIZEQUERY; 716a1c6091aSnicm *size = end + 1; 717a1c6091aSnicm return (0); 718a1c6091aSnicm } 719a1c6091aSnicm 720a1c6091aSnicm log_debug("%s: unrecognized window size sequence: %s", c->name, tmp); 721a1c6091aSnicm return (-1); 722a1c6091aSnicm } 723a1c6091aSnicm 724a1c6091aSnicm 725a88e9db6Snicm /* Process at least one key in the buffer. Return 0 if no keys present. */ 726a88e9db6Snicm int 727a3d8f346Snicm tty_keys_next(struct tty *tty) 728311827fbSnicm { 7291f2e7474Snicm struct client *c = tty->client; 730311827fbSnicm struct timeval tv; 731586d3997Snicm const char *buf; 732311827fbSnicm size_t len, size; 7334cf01325Snicm cc_t bspace; 734fa0ecfe4Snicm int delay, expired = 0, n; 735719f5715Snicm key_code key, onlykey; 736a88e9db6Snicm struct mouse_event m = { 0 }; 737a88e9db6Snicm struct key_event *event; 738a88e9db6Snicm 739c740be85Snicm /* Get key buffer. */ 74099f5cecfSnicm buf = EVBUFFER_DATA(tty->in); 74199f5cecfSnicm len = EVBUFFER_LENGTH(tty->in); 742311827fbSnicm if (len == 0) 743a3d8f346Snicm return (0); 7441f2e7474Snicm log_debug("%s: keys are %zu (%.*s)", c->name, len, (int)len, buf); 745311827fbSnicm 74690866723Snicm /* Is this a clipboard response? */ 74790866723Snicm switch (tty_keys_clipboard(tty, buf, len, &size)) { 74890866723Snicm case 0: /* yes */ 74990866723Snicm key = KEYC_UNKNOWN; 75090866723Snicm goto complete_key; 75190866723Snicm case -1: /* no, or not valid */ 75290866723Snicm break; 75390866723Snicm case 1: /* partial */ 75490866723Snicm goto partial_key; 75590866723Snicm } 75690866723Snicm 757e0697ebcSnicm /* Is this a primary device attributes response? */ 758f00ac6e6Snicm switch (tty_keys_device_attributes(tty, buf, len, &size)) { 759f00ac6e6Snicm case 0: /* yes */ 760f00ac6e6Snicm key = KEYC_UNKNOWN; 761f00ac6e6Snicm goto complete_key; 762f00ac6e6Snicm case -1: /* no, or not valid */ 763f00ac6e6Snicm break; 764f00ac6e6Snicm case 1: /* partial */ 765f00ac6e6Snicm goto partial_key; 766f00ac6e6Snicm } 767f00ac6e6Snicm 768e0697ebcSnicm /* Is this a secondary device attributes response? */ 769e0697ebcSnicm switch (tty_keys_device_attributes2(tty, buf, len, &size)) { 770e0697ebcSnicm case 0: /* yes */ 771e0697ebcSnicm key = KEYC_UNKNOWN; 772e0697ebcSnicm goto complete_key; 773e0697ebcSnicm case -1: /* no, or not valid */ 774e0697ebcSnicm break; 775e0697ebcSnicm case 1: /* partial */ 776e0697ebcSnicm goto partial_key; 777e0697ebcSnicm } 778e0697ebcSnicm 779ecd9db25Snicm /* Is this an extended device attributes response? */ 780ecd9db25Snicm switch (tty_keys_extended_device_attributes(tty, buf, len, &size)) { 7816110b15cSnicm case 0: /* yes */ 7826110b15cSnicm key = KEYC_UNKNOWN; 7836110b15cSnicm goto complete_key; 7846110b15cSnicm case -1: /* no, or not valid */ 7856110b15cSnicm break; 7866110b15cSnicm case 1: /* partial */ 7876110b15cSnicm goto partial_key; 7886110b15cSnicm } 7896110b15cSnicm 790d21788ceSnicm /* Is this a colours response? */ 791891e2565Snicm switch (tty_keys_colours(tty, buf, len, &size, &tty->fg, &tty->bg)) { 792d21788ceSnicm case 0: /* yes */ 793d21788ceSnicm key = KEYC_UNKNOWN; 794d21788ceSnicm goto complete_key; 795d21788ceSnicm case -1: /* no, or not valid */ 796d21788ceSnicm break; 797d21788ceSnicm case 1: /* partial */ 798d21788ceSnicm goto partial_key; 799d21788ceSnicm } 800d21788ceSnicm 80197f93609Snicm /* Is this a mouse key press? */ 802a88e9db6Snicm switch (tty_keys_mouse(tty, buf, len, &size, &m)) { 803888b88f4Snicm case 0: /* yes */ 804888b88f4Snicm key = KEYC_MOUSE; 805c740be85Snicm goto complete_key; 806888b88f4Snicm case -1: /* no, or not valid */ 80789ce6bdbSnicm break; 8089146dae2Snicm case -2: /* yes, but we don't care. */ 809737b0b47Snicm key = KEYC_MOUSE; 8109146dae2Snicm goto discard_key; 811888b88f4Snicm case 1: /* partial */ 812888b88f4Snicm goto partial_key; 813311827fbSnicm } 814311827fbSnicm 815f1c69fe1Snicm /* Is this an extended key press? */ 816f1c69fe1Snicm switch (tty_keys_extended_key(tty, buf, len, &size, &key)) { 817f1c69fe1Snicm case 0: /* yes */ 818f1c69fe1Snicm goto complete_key; 819f1c69fe1Snicm case -1: /* no, or not valid */ 820f1c69fe1Snicm break; 821f1c69fe1Snicm case 1: /* partial */ 822f1c69fe1Snicm goto partial_key; 823f1c69fe1Snicm } 824f1c69fe1Snicm 825a1c6091aSnicm /* Check for window size query */ 826a1c6091aSnicm switch (tty_keys_winsz(tty, buf, len, &size)) { 827a1c6091aSnicm case 0: /* yes */ 828a1c6091aSnicm key = KEYC_UNKNOWN; 829a1c6091aSnicm goto complete_key; 830a1c6091aSnicm case -1: /* no, or not valid */ 831a1c6091aSnicm break; 832a1c6091aSnicm case 1: /* partial */ 833a1c6091aSnicm goto partial_key; 834a1c6091aSnicm } 835a1c6091aSnicm 8364a29328cSnicm first_key: 837b70f7e17Snicm /* Try to lookup complete key. */ 838b70f7e17Snicm n = tty_keys_next1(tty, buf, len, &key, &size, expired); 839b70f7e17Snicm if (n == 0) /* found */ 840b70f7e17Snicm goto complete_key; 841b70f7e17Snicm if (n == 1) 842b70f7e17Snicm goto partial_key; 843b70f7e17Snicm 844b70f7e17Snicm /* 845b70f7e17Snicm * If not a complete key, look for key with an escape prefix (meta 846b70f7e17Snicm * modifier). 847b70f7e17Snicm */ 848ec97f12dSnicm if (*buf == '\033' && len > 1) { 849fa0ecfe4Snicm /* Look for a key without the escape. */ 850fa0ecfe4Snicm n = tty_keys_next1(tty, buf + 1, len - 1, &key, &size, expired); 851fa0ecfe4Snicm if (n == 0) { /* found */ 8525416581eSnicm if (key & KEYC_IMPLIED_META) { 85328dafa00Snicm /* 85428dafa00Snicm * We want the escape key as well as the xterm 85528dafa00Snicm * key, because the xterm sequence implicitly 85628dafa00Snicm * includes the escape (so if we see 85728dafa00Snicm * \033\033[1;3D we know it is an Escape 85828dafa00Snicm * followed by M-Left, not just M-Left). 85928dafa00Snicm */ 86028dafa00Snicm key = '\033'; 86128dafa00Snicm size = 1; 86228dafa00Snicm goto complete_key; 86328dafa00Snicm } 8646a385b80Snicm key |= KEYC_META; 865fa0ecfe4Snicm size++; 86606b16c35Snicm goto complete_key; 867fa0ecfe4Snicm } 868fa0ecfe4Snicm if (n == 1) /* partial */ 8694a29328cSnicm goto partial_key; 8701c8b05a8Snicm } 87106b16c35Snicm 8724a29328cSnicm /* 8731c8b05a8Snicm * At this point, we know the key is not partial (with or without 8741c8b05a8Snicm * escape). So pass it through even if the timer has not expired. 8754a29328cSnicm */ 8761c8b05a8Snicm if (*buf == '\033' && len >= 2) { 8776a385b80Snicm key = (u_char)buf[1] | KEYC_META; 878c740be85Snicm size = 2; 8791c8b05a8Snicm } else { 8801c8b05a8Snicm key = (u_char)buf[0]; 881c740be85Snicm size = 1; 8821c8b05a8Snicm } 883719f5715Snicm 884719f5715Snicm /* C-Space is special. */ 885719f5715Snicm if ((key & KEYC_MASK_KEY) == C0_NUL) 886719f5715Snicm key = ' ' | KEYC_CTRL | (key & KEYC_META); 887719f5715Snicm 888719f5715Snicm /* 889*2f106d38Snicm * Check for backspace key using termios VERASE - the terminfo 890*2f106d38Snicm * kbs entry is extremely unreliable, so cannot be safely 891*2f106d38Snicm * used. termios should have a better idea. 892*2f106d38Snicm */ 893*2f106d38Snicm bspace = tty->tio.c_cc[VERASE]; 894*2f106d38Snicm if (bspace != _POSIX_VDISABLE && key == bspace) { 895*2f106d38Snicm log_debug("%s: key %#llx is backspace", c->name, key); 896*2f106d38Snicm key = KEYC_BSPACE; 897*2f106d38Snicm } 898*2f106d38Snicm 899*2f106d38Snicm /* 900719f5715Snicm * Fix up all C0 control codes that don't have a dedicated key into 901719f5715Snicm * corresponding Ctrl keys. Convert characters in the A-Z range into 902719f5715Snicm * lowercase, so ^A becomes a|CTRL. 903719f5715Snicm */ 904719f5715Snicm onlykey = key & KEYC_MASK_KEY; 9054aaf2c8dSnicm if (onlykey < 0x20 && 9064aaf2c8dSnicm onlykey != C0_HT && 9074aaf2c8dSnicm onlykey != C0_CR && 908719f5715Snicm onlykey != C0_ESC) { 909719f5715Snicm onlykey |= 0x40; 910719f5715Snicm if (onlykey >= 'A' && onlykey <= 'Z') 911719f5715Snicm onlykey |= 0x20; 912719f5715Snicm key = onlykey | KEYC_CTRL | (key & KEYC_META); 913719f5715Snicm } 914719f5715Snicm 915c740be85Snicm goto complete_key; 916e8bb025fSnicm 917e8bb025fSnicm partial_key: 9181f2e7474Snicm log_debug("%s: partial key %.*s", c->name, (int)len, buf); 919c740be85Snicm 920c740be85Snicm /* If timer is going, check for expiration. */ 921c740be85Snicm if (tty->flags & TTY_TIMER) { 922c740be85Snicm if (evtimer_initialized(&tty->key_timer) && 923d9aede6bSnicm !evtimer_pending(&tty->key_timer, NULL)) { 924d9aede6bSnicm expired = 1; 925c740be85Snicm goto first_key; 926d9aede6bSnicm } 927c740be85Snicm return (0); 928a3d8f346Snicm } 929a3d8f346Snicm 930c740be85Snicm /* Get the time period. */ 931d89252e5Snicm delay = options_get_number(global_options, "escape-time"); 932ad7a034aSnicm if (delay == 0) 933ad7a034aSnicm delay = 1; 934298db98fSnicm tv.tv_sec = delay / 1000; 935298db98fSnicm tv.tv_usec = (delay % 1000) * 1000L; 936a3d8f346Snicm 937c740be85Snicm /* Start the timer. */ 938346357b7Snicm if (event_initialized(&tty->key_timer)) 939a3d8f346Snicm evtimer_del(&tty->key_timer); 940a3d8f346Snicm evtimer_set(&tty->key_timer, tty_keys_callback, tty); 941a3d8f346Snicm evtimer_add(&tty->key_timer, &tv); 942a3d8f346Snicm 943c740be85Snicm tty->flags |= TTY_TIMER; 944a3d8f346Snicm return (0); 945311827fbSnicm 946c740be85Snicm complete_key: 9471f2e7474Snicm log_debug("%s: complete key %.*s %#llx", c->name, (int)size, buf, key); 948e8bb025fSnicm 949c740be85Snicm /* Remove key timer. */ 950346357b7Snicm if (event_initialized(&tty->key_timer)) 951a3d8f346Snicm evtimer_del(&tty->key_timer); 952c740be85Snicm tty->flags &= ~TTY_TIMER; 953e8bb025fSnicm 9546ddcc08fSnicm /* Check for focus events. */ 955698bdd54Snicm if (key == KEYC_FOCUS_OUT) { 9561a773291Snicm c->flags &= ~CLIENT_FOCUSED; 9571a773291Snicm window_update_focus(c->session->curw->window); 958698bdd54Snicm notify_client("client-focus-out", c); 959698bdd54Snicm } else if (key == KEYC_FOCUS_IN) { 9601a773291Snicm c->flags |= CLIENT_FOCUSED; 961698bdd54Snicm notify_client("client-focus-in", c); 9621a773291Snicm window_update_focus(c->session->curw->window); 963698bdd54Snicm } 9646ddcc08fSnicm 965c740be85Snicm /* Fire the key. */ 966a88e9db6Snicm if (key != KEYC_UNKNOWN) { 967482624f4Snicm event = xcalloc(1, sizeof *event); 968a88e9db6Snicm event->key = key; 969a88e9db6Snicm memcpy(&event->m, &m, sizeof event->m); 970482624f4Snicm 971482624f4Snicm event->buf = xmalloc(size); 972482624f4Snicm event->len = size; 973482624f4Snicm memcpy (event->buf, buf, event->len); 974482624f4Snicm 975482624f4Snicm if (!server_client_handle_key(c, event)) { 976482624f4Snicm free(event->buf); 9777df29c9dSnicm free(event); 978a88e9db6Snicm } 979482624f4Snicm } 980482624f4Snicm 981482624f4Snicm /* Remove data from buffer. */ 982482624f4Snicm evbuffer_drain(tty->in, size); 983e8bb025fSnicm 984a3d8f346Snicm return (1); 9859146dae2Snicm 9869146dae2Snicm discard_key: 9871f2e7474Snicm log_debug("%s: discard key %.*s %#llx", c->name, (int)size, buf, key); 9889146dae2Snicm 9899146dae2Snicm /* Remove data from buffer. */ 99099f5cecfSnicm evbuffer_drain(tty->in, size); 9919146dae2Snicm 9929146dae2Snicm return (1); 993a3d8f346Snicm } 994a3d8f346Snicm 995e8bb025fSnicm /* Key timer callback. */ 9964a29328cSnicm static void 997d0e2e7f1Snicm tty_keys_callback(__unused int fd, __unused short events, void *data) 998a3d8f346Snicm { 999a3d8f346Snicm struct tty *tty = data; 1000a3d8f346Snicm 1001c740be85Snicm if (tty->flags & TTY_TIMER) { 1002e8bb025fSnicm while (tty_keys_next(tty)) 1003e8bb025fSnicm ; 1004311827fbSnicm } 1005c740be85Snicm } 1006311827fbSnicm 1007888b88f4Snicm /* 1008f1c69fe1Snicm * Handle extended key input. This has two forms: \033[27;m;k~ and \033[k;mu, 1009f1c69fe1Snicm * where k is key as a number and m is a modifier. Returns 0 for success, -1 1010f1c69fe1Snicm * for failure, 1 for partial; 1011f1c69fe1Snicm */ 1012f1c69fe1Snicm static int 1013f1c69fe1Snicm tty_keys_extended_key(struct tty *tty, const char *buf, size_t len, 1014f1c69fe1Snicm size_t *size, key_code *key) 1015f1c69fe1Snicm { 1016f1c69fe1Snicm struct client *c = tty->client; 1017f1c69fe1Snicm size_t end; 1018f1c69fe1Snicm u_int number, modifiers; 1019f1c69fe1Snicm char tmp[64]; 1020188fd23eSnicm cc_t bspace; 10213835afffSnicm key_code nkey, onlykey; 1022b843f94bSnicm struct utf8_data ud; 1023b843f94bSnicm utf8_char uc; 1024f1c69fe1Snicm 1025f1c69fe1Snicm *size = 0; 1026f1c69fe1Snicm 1027f1c69fe1Snicm /* First two bytes are always \033[. */ 1028f1c69fe1Snicm if (buf[0] != '\033') 1029f1c69fe1Snicm return (-1); 1030f1c69fe1Snicm if (len == 1) 1031f1c69fe1Snicm return (1); 1032f1c69fe1Snicm if (buf[1] != '[') 1033f1c69fe1Snicm return (-1); 1034f1c69fe1Snicm if (len == 2) 1035f1c69fe1Snicm return (1); 1036f1c69fe1Snicm 1037f1c69fe1Snicm /* 1038f1c69fe1Snicm * Look for a terminator. Stop at either '~' or anything that isn't a 1039f1c69fe1Snicm * number or ';'. 1040f1c69fe1Snicm */ 1041f1c69fe1Snicm for (end = 2; end < len && end != sizeof tmp; end++) { 1042f1c69fe1Snicm if (buf[end] == '~') 1043f1c69fe1Snicm break; 1044f1c69fe1Snicm if (!isdigit((u_char)buf[end]) && buf[end] != ';') 1045f1c69fe1Snicm break; 1046f1c69fe1Snicm } 1047f1c69fe1Snicm if (end == len) 1048f1c69fe1Snicm return (1); 1049f1c69fe1Snicm if (end == sizeof tmp || (buf[end] != '~' && buf[end] != 'u')) 1050f1c69fe1Snicm return (-1); 1051f1c69fe1Snicm 1052f1c69fe1Snicm /* Copy to the buffer. */ 1053f1c69fe1Snicm memcpy(tmp, buf + 2, end); 1054f1c69fe1Snicm tmp[end] = '\0'; 1055f1c69fe1Snicm 1056f1c69fe1Snicm /* Try to parse either form of key. */ 1057f1c69fe1Snicm if (buf[end] == '~') { 1058f1c69fe1Snicm if (sscanf(tmp, "27;%u;%u", &modifiers, &number) != 2) 1059f1c69fe1Snicm return (-1); 1060f1c69fe1Snicm } else { 1061f1c69fe1Snicm if (sscanf(tmp ,"%u;%u", &number, &modifiers) != 2) 1062f1c69fe1Snicm return (-1); 1063f1c69fe1Snicm } 1064f1c69fe1Snicm *size = end + 1; 1065f1c69fe1Snicm 1066188fd23eSnicm /* Store the key. */ 1067188fd23eSnicm bspace = tty->tio.c_cc[VERASE]; 1068188fd23eSnicm if (bspace != _POSIX_VDISABLE && number == bspace) 1069188fd23eSnicm nkey = KEYC_BSPACE; 1070188fd23eSnicm else 1071188fd23eSnicm nkey = number; 1072188fd23eSnicm 1073b843f94bSnicm /* Convert UTF-32 codepoint into internal representation. */ 10748d6395f7Snicm if (nkey != KEYC_BSPACE && nkey & ~0x7f) { 1075b843f94bSnicm if (utf8_fromwc(nkey, &ud) == UTF8_DONE && 1076b843f94bSnicm utf8_from_data(&ud, &uc) == UTF8_DONE) 1077b843f94bSnicm nkey = uc; 1078b843f94bSnicm else 1079b843f94bSnicm return (-1); 1080b843f94bSnicm } 1081b843f94bSnicm 1082188fd23eSnicm /* Update the modifiers. */ 1083babee3ebSnicm if (modifiers > 0) { 1084babee3ebSnicm modifiers--; 10853835afffSnicm if (modifiers & 1) 1086188fd23eSnicm nkey |= KEYC_SHIFT; 1087babee3ebSnicm if (modifiers & 2) 1088babee3ebSnicm nkey |= (KEYC_META|KEYC_IMPLIED_META); /* Alt */ 1089babee3ebSnicm if (modifiers & 4) 1090188fd23eSnicm nkey |= KEYC_CTRL; 1091babee3ebSnicm if (modifiers & 8) 1092babee3ebSnicm nkey |= (KEYC_META|KEYC_IMPLIED_META); /* Meta */ 1093f1c69fe1Snicm } 1094188fd23eSnicm 1095719f5715Snicm /* Convert S-Tab into Backtab. */ 1096719f5715Snicm if ((nkey & KEYC_MASK_KEY) == '\011' && (nkey & KEYC_SHIFT)) 1097719f5715Snicm nkey = KEYC_BTAB | (nkey & ~KEYC_MASK_KEY & ~KEYC_SHIFT); 1098188fd23eSnicm 10993835afffSnicm /* 11003835afffSnicm * Deal with the Shift modifier when present alone. The problem is that 11013835afffSnicm * in mode 2 some terminals would report shifted keys, like S-a, as 11023835afffSnicm * just A, and some as S-A. 11033835afffSnicm * 11043835afffSnicm * Because we need an unambiguous internal representation, and because 11053835afffSnicm * restoring the Shift modifier when it's missing would require knowing 11063835afffSnicm * the keyboard layout, and because S-A would cause a lot of issues 11073835afffSnicm * downstream, we choose to lose the Shift for all printable 11083835afffSnicm * characters. 11093835afffSnicm * 11103835afffSnicm * That still leaves some ambiguity, such as C-S-A vs. C-A, but that's 11113835afffSnicm * OK, and applications can handle that. 11123835afffSnicm */ 11133835afffSnicm onlykey = nkey & KEYC_MASK_KEY; 11143835afffSnicm if (((onlykey > 0x20 && onlykey < 0x7f) || 11153835afffSnicm KEYC_IS_UNICODE(nkey)) && 11163835afffSnicm (nkey & KEYC_MASK_MODIFIERS) == KEYC_SHIFT) 11173835afffSnicm nkey &= ~KEYC_SHIFT; 11183835afffSnicm 1119f1c69fe1Snicm if (log_get_level() != 0) { 1120f1c69fe1Snicm log_debug("%s: extended key %.*s is %llx (%s)", c->name, 1121188fd23eSnicm (int)*size, buf, nkey, key_string_lookup_key(nkey, 1)); 1122f1c69fe1Snicm } 1123719f5715Snicm 11248f86830fSnicm *key = nkey; 1125f1c69fe1Snicm return (0); 1126f1c69fe1Snicm } 1127f1c69fe1Snicm 1128f1c69fe1Snicm /* 1129888b88f4Snicm * Handle mouse key input. Returns 0 for success, -1 for failure, 1 for partial 113030fb8e30Snicm * (probably a mouse sequence but need more data), -2 if an invalid mouse 113130fb8e30Snicm * sequence. 1132888b88f4Snicm */ 11334a29328cSnicm static int 1134a88e9db6Snicm tty_keys_mouse(struct tty *tty, const char *buf, size_t len, size_t *size, 1135a88e9db6Snicm struct mouse_event *m) 1136311827fbSnicm { 11371f2e7474Snicm struct client *c = tty->client; 11382ac09bbfSnicm u_int i, x, y, b, sgr_b; 11391f2e7474Snicm u_char sgr_type, ch; 1140699995ddSnicm 1141311827fbSnicm /* 1142699995ddSnicm * Standard mouse sequences are \033[M followed by three characters 11436c0777c5Snicm * indicating button, X and Y, all based at 32 with 1,1 top-left. 1144699995ddSnicm * 1145699995ddSnicm * UTF-8 mouse sequences are similar but the three are expressed as 1146699995ddSnicm * UTF-8 characters. 11476c0777c5Snicm * 11486c0777c5Snicm * SGR extended mouse sequences are \033[< followed by three numbers in 11496c0777c5Snicm * decimal and separated by semicolons indicating button, X and Y. A 11506c0777c5Snicm * trailing 'M' is click or scroll and trailing 'm' release. All are 11516c0777c5Snicm * based at 0 with 1,1 top-left. 1152311827fbSnicm */ 1153311827fbSnicm 1154888b88f4Snicm *size = 0; 1155e048bb79Snicm x = y = b = sgr_b = 0; 1156e048bb79Snicm sgr_type = ' '; 1157888b88f4Snicm 11586c0777c5Snicm /* First two bytes are always \033[. */ 1159888b88f4Snicm if (buf[0] != '\033') 1160888b88f4Snicm return (-1); 1161888b88f4Snicm if (len == 1) 1162888b88f4Snicm return (1); 1163888b88f4Snicm if (buf[1] != '[') 1164888b88f4Snicm return (-1); 1165888b88f4Snicm if (len == 2) 1166888b88f4Snicm return (1); 1167888b88f4Snicm 11686c0777c5Snicm /* 11692ac09bbfSnicm * Third byte is M in old standard (and UTF-8 extension which we do not 11702ac09bbfSnicm * support), < in SGR extension. 11716c0777c5Snicm */ 11726c0777c5Snicm if (buf[2] == 'M') { 1173699995ddSnicm /* Read the three inputs. */ 1174699995ddSnicm *size = 3; 1175699995ddSnicm for (i = 0; i < 3; i++) { 11766c0777c5Snicm if (len <= *size) 1177888b88f4Snicm return (1); 11781f2e7474Snicm ch = (u_char)buf[(*size)++]; 1179699995ddSnicm if (i == 0) 11801f2e7474Snicm b = ch; 1181699995ddSnicm else if (i == 1) 11821f2e7474Snicm x = ch; 1183699995ddSnicm else 11841f2e7474Snicm y = ch; 1185699995ddSnicm } 11861f2e7474Snicm log_debug("%s: mouse input: %.*s", c->name, (int)*size, buf); 1187699995ddSnicm 1188699995ddSnicm /* Check and return the mouse input. */ 118981e6b289Snicm if (b < MOUSE_PARAM_BTN_OFF || 119081e6b289Snicm x < MOUSE_PARAM_POS_OFF || 119181e6b289Snicm y < MOUSE_PARAM_POS_OFF) 119230fb8e30Snicm return (-2); 119381e6b289Snicm b -= MOUSE_PARAM_BTN_OFF; 119481e6b289Snicm x -= MOUSE_PARAM_POS_OFF; 119581e6b289Snicm y -= MOUSE_PARAM_POS_OFF; 11966c0777c5Snicm } else if (buf[2] == '<') { 11976c0777c5Snicm /* Read the three inputs. */ 11986c0777c5Snicm *size = 3; 11996c0777c5Snicm while (1) { 12006c0777c5Snicm if (len <= *size) 12016c0777c5Snicm return (1); 12021f2e7474Snicm ch = (u_char)buf[(*size)++]; 12031f2e7474Snicm if (ch == ';') 12046c0777c5Snicm break; 12051f2e7474Snicm if (ch < '0' || ch > '9') 12066c0777c5Snicm return (-1); 12071f2e7474Snicm sgr_b = 10 * sgr_b + (ch - '0'); 12086c0777c5Snicm } 12096c0777c5Snicm while (1) { 12106c0777c5Snicm if (len <= *size) 12116c0777c5Snicm return (1); 12121f2e7474Snicm ch = (u_char)buf[(*size)++]; 12131f2e7474Snicm if (ch == ';') 12146c0777c5Snicm break; 12151f2e7474Snicm if (ch < '0' || ch > '9') 12166c0777c5Snicm return (-1); 12171f2e7474Snicm x = 10 * x + (ch - '0'); 12186c0777c5Snicm } 12196c0777c5Snicm while (1) { 12206c0777c5Snicm if (len <= *size) 12216c0777c5Snicm return (1); 12221f2e7474Snicm ch = (u_char)buf[(*size)++]; 12231f2e7474Snicm if (ch == 'M' || ch == 'm') 12246c0777c5Snicm break; 12251f2e7474Snicm if (ch < '0' || ch > '9') 12266c0777c5Snicm return (-1); 12271f2e7474Snicm y = 10 * y + (ch - '0'); 12286c0777c5Snicm } 12291f2e7474Snicm log_debug("%s: mouse input (SGR): %.*s", c->name, (int)*size, 12301f2e7474Snicm buf); 12316c0777c5Snicm 12326c0777c5Snicm /* Check and return the mouse input. */ 12336c0777c5Snicm if (x < 1 || y < 1) 123430fb8e30Snicm return (-2); 12356c0777c5Snicm x--; 12366c0777c5Snicm y--; 1237e048bb79Snicm b = sgr_b; 1238e048bb79Snicm 1239e048bb79Snicm /* Type is M for press, m for release. */ 12401f2e7474Snicm sgr_type = ch; 1241e048bb79Snicm if (sgr_type == 'm') 12424e5846a2Snicm b = 3; 12436c0777c5Snicm 12449146dae2Snicm /* 12459146dae2Snicm * Some terminals (like PuTTY 0.63) mistakenly send 12469146dae2Snicm * button-release events for scroll-wheel button-press event. 12479146dae2Snicm * Discard it before it reaches any program running inside 12489146dae2Snicm * tmux. 12499146dae2Snicm */ 12504e5846a2Snicm if (sgr_type == 'm' && MOUSE_WHEEL(sgr_b)) 12519146dae2Snicm return (-2); 12526c0777c5Snicm } else 12536c0777c5Snicm return (-1); 12546fb64d94Snicm 1255e048bb79Snicm /* Fill mouse event. */ 1256a88e9db6Snicm m->lx = tty->mouse_last_x; 125785539586Snicm m->x = x; 1258a88e9db6Snicm m->ly = tty->mouse_last_y; 125985539586Snicm m->y = y; 1260bdacbbeeSnicm m->lb = tty->mouse_last_b; 1261e048bb79Snicm m->b = b; 1262e048bb79Snicm m->sgr_type = sgr_type; 1263e048bb79Snicm m->sgr_b = sgr_b; 12646fb64d94Snicm 1265a88e9db6Snicm /* Update last mouse state. */ 1266a88e9db6Snicm tty->mouse_last_x = x; 1267a88e9db6Snicm tty->mouse_last_y = y; 1268bdacbbeeSnicm tty->mouse_last_b = b; 1269a88e9db6Snicm 1270888b88f4Snicm return (0); 1271311827fbSnicm } 1272f00ac6e6Snicm 1273f00ac6e6Snicm /* 127490866723Snicm * Handle OSC 52 clipboard input. Returns 0 for success, -1 for failure, 1 for 127590866723Snicm * partial. 127690866723Snicm */ 127790866723Snicm static int 1278f9bfc3f8Snicm tty_keys_clipboard(struct tty *tty, const char *buf, size_t len, size_t *size) 127990866723Snicm { 1280f9bfc3f8Snicm struct client *c = tty->client; 1281f9bfc3f8Snicm struct window_pane *wp; 1282e3eeb0f8Snicm size_t end, terminator = 0, needed; 128390866723Snicm char *copy, *out; 128490866723Snicm int outlen; 1285f9bfc3f8Snicm u_int i; 128690866723Snicm 128790866723Snicm *size = 0; 128890866723Snicm 1289ecd9db25Snicm /* First five bytes are always \033]52;. */ 129090866723Snicm if (buf[0] != '\033') 129190866723Snicm return (-1); 129290866723Snicm if (len == 1) 129390866723Snicm return (1); 129490866723Snicm if (buf[1] != ']') 129590866723Snicm return (-1); 129690866723Snicm if (len == 2) 129790866723Snicm return (1); 129890866723Snicm if (buf[2] != '5') 129990866723Snicm return (-1); 130090866723Snicm if (len == 3) 130190866723Snicm return (1); 130290866723Snicm if (buf[3] != '2') 130390866723Snicm return (-1); 130490866723Snicm if (len == 4) 130590866723Snicm return (1); 130690866723Snicm if (buf[4] != ';') 130790866723Snicm return (-1); 130890866723Snicm if (len == 5) 130990866723Snicm return (1); 131090866723Snicm 131190866723Snicm /* Find the terminator if any. */ 131290866723Snicm for (end = 5; end < len; end++) { 131390866723Snicm if (buf[end] == '\007') { 131490866723Snicm terminator = 1; 131590866723Snicm break; 131690866723Snicm } 131790866723Snicm if (end > 5 && buf[end - 1] == '\033' && buf[end] == '\\') { 131890866723Snicm terminator = 2; 131990866723Snicm break; 132090866723Snicm } 132190866723Snicm } 132290866723Snicm if (end == len) 132390866723Snicm return (1); 13243d0c1d47Snicm *size = end + 1; 132590866723Snicm 132690866723Snicm /* Skip the initial part. */ 132790866723Snicm buf += 5; 132890866723Snicm end -= 5; 132990866723Snicm 13309bd2c8e2Snicm /* Adjust end so that it points to the start of the terminator. */ 13319bd2c8e2Snicm end -= terminator - 1; 13329bd2c8e2Snicm 133390866723Snicm /* Get the second argument. */ 133490866723Snicm while (end != 0 && *buf != ';') { 133590866723Snicm buf++; 133690866723Snicm end--; 133790866723Snicm } 133890866723Snicm if (end == 0 || end == 1) 133990866723Snicm return (0); 134090866723Snicm buf++; 134190866723Snicm end--; 134290866723Snicm 1343b0386005Snicm /* If we did not request this, ignore it. */ 1344b0386005Snicm if (~tty->flags & TTY_OSC52QUERY) 1345b0386005Snicm return (0); 1346b0386005Snicm tty->flags &= ~TTY_OSC52QUERY; 1347f9bfc3f8Snicm evtimer_del(&tty->clipboard_timer); 1348b0386005Snicm 134990866723Snicm /* It has to be a string so copy it. */ 135090866723Snicm copy = xmalloc(end + 1); 135190866723Snicm memcpy(copy, buf, end); 135290866723Snicm copy[end] = '\0'; 135390866723Snicm 135490866723Snicm /* Convert from base64. */ 135590866723Snicm needed = (end / 4) * 3; 135690866723Snicm out = xmalloc(needed); 135790866723Snicm if ((outlen = b64_pton(copy, out, len)) == -1) { 135890866723Snicm free(out); 135990866723Snicm free(copy); 136090866723Snicm return (0); 136190866723Snicm } 136290866723Snicm free(copy); 136390866723Snicm 1364f9bfc3f8Snicm /* Create a new paste buffer and forward to panes. */ 136590866723Snicm log_debug("%s: %.*s", __func__, outlen, out); 1366f9bfc3f8Snicm if (c->flags & CLIENT_CLIPBOARDBUFFER) { 13674aafca52Snicm paste_add(NULL, out, outlen); 1368f9bfc3f8Snicm c->flags &= ~CLIENT_CLIPBOARDBUFFER; 1369f9bfc3f8Snicm } 1370f9bfc3f8Snicm for (i = 0; i < c->clipboard_npanes; i++) { 1371f9bfc3f8Snicm wp = window_pane_find_by_id(c->clipboard_panes[i]); 1372f9bfc3f8Snicm if (wp != NULL) 1373f9bfc3f8Snicm input_reply_clipboard(wp->event, out, outlen, "\033\\"); 1374f9bfc3f8Snicm } 1375f9bfc3f8Snicm free(c->clipboard_panes); 1376f9bfc3f8Snicm c->clipboard_panes = NULL; 1377f9bfc3f8Snicm c->clipboard_npanes = 0; 137890866723Snicm 137990866723Snicm return (0); 138090866723Snicm } 138190866723Snicm 138290866723Snicm /* 1383e0697ebcSnicm * Handle primary device attributes input. Returns 0 for success, -1 for 138425e90a40Snicm * failure, 1 for partial. 1385f00ac6e6Snicm */ 1386f00ac6e6Snicm static int 1387f00ac6e6Snicm tty_keys_device_attributes(struct tty *tty, const char *buf, size_t len, 1388f00ac6e6Snicm size_t *size) 1389f00ac6e6Snicm { 13901f2e7474Snicm struct client *c = tty->client; 1391f4ec6bcaSnicm int *features = &c->term_features; 13922afb856aSnicm u_int i, n = 0; 1393e0697ebcSnicm char tmp[128], *endptr, p[32] = { 0 }, *cp, *next; 1394f00ac6e6Snicm 1395f00ac6e6Snicm *size = 0; 13966648e05cSnicm if (tty->flags & TTY_HAVEDA) 13976648e05cSnicm return (-1); 1398f00ac6e6Snicm 1399e0697ebcSnicm /* First three bytes are always \033[?. */ 1400f00ac6e6Snicm if (buf[0] != '\033') 1401f00ac6e6Snicm return (-1); 1402f00ac6e6Snicm if (len == 1) 1403f00ac6e6Snicm return (1); 1404f00ac6e6Snicm if (buf[1] != '[') 1405f00ac6e6Snicm return (-1); 1406f00ac6e6Snicm if (len == 2) 1407f00ac6e6Snicm return (1); 1408e0697ebcSnicm if (buf[2] != '?') 1409f00ac6e6Snicm return (-1); 1410f00ac6e6Snicm if (len == 3) 1411f00ac6e6Snicm return (1); 1412f00ac6e6Snicm 1413d21788ceSnicm /* Copy the rest up to a c. */ 1414e0697ebcSnicm for (i = 0; i < (sizeof tmp); i++) { 1415f00ac6e6Snicm if (3 + i == len) 1416f00ac6e6Snicm return (1); 1417ecd9db25Snicm if (buf[3 + i] == 'c') 1418ecd9db25Snicm break; 1419f00ac6e6Snicm tmp[i] = buf[3 + i]; 1420f00ac6e6Snicm } 1421e0697ebcSnicm if (i == (sizeof tmp)) 1422f00ac6e6Snicm return (-1); 1423f00ac6e6Snicm tmp[i] = '\0'; 1424f00ac6e6Snicm *size = 4 + i; 1425f00ac6e6Snicm 142625e90a40Snicm /* Convert all arguments to numbers. */ 14272afb856aSnicm cp = tmp; 14282afb856aSnicm while ((next = strsep(&cp, ";")) != NULL) { 14292afb856aSnicm p[n] = strtoul(next, &endptr, 10); 1430be3ccdcbSnicm if (*endptr != '\0') 14312afb856aSnicm p[n] = 0; 1432e0697ebcSnicm if (++n == nitems(p)) 1433e0697ebcSnicm break; 1434e0697ebcSnicm } 1435e0697ebcSnicm 143627676e5cSnicm /* Add terminal features. */ 1437e0697ebcSnicm switch (p[0]) { 143827676e5cSnicm case 61: /* level 1 */ 143957c744a4Snicm case 62: /* level 2 */ 144057c744a4Snicm case 63: /* level 3 */ 144127676e5cSnicm case 64: /* level 4 */ 144257c744a4Snicm case 65: /* level 5 */ 1443e0697ebcSnicm for (i = 1; i < n; i++) { 1444e0697ebcSnicm log_debug("%s: DA feature: %d", c->name, p[i]); 1445e0697ebcSnicm if (p[i] == 4) 1446f4ec6bcaSnicm tty_add_features(features, "sixel", ","); 144727676e5cSnicm if (p[i] == 21) 144827676e5cSnicm tty_add_features(features, "margins", ","); 144927676e5cSnicm if (p[i] == 28) 145027676e5cSnicm tty_add_features(features, "rectfill", ","); 1451e0697ebcSnicm } 1452e0697ebcSnicm break; 1453e0697ebcSnicm } 1454e0697ebcSnicm log_debug("%s: received primary DA %.*s", c->name, (int)*size, buf); 1455e0697ebcSnicm 1456e0697ebcSnicm tty_update_features(tty); 1457e0697ebcSnicm tty->flags |= TTY_HAVEDA; 1458e0697ebcSnicm 1459e0697ebcSnicm return (0); 1460e0697ebcSnicm } 1461e0697ebcSnicm 1462e0697ebcSnicm /* 1463e0697ebcSnicm * Handle secondary device attributes input. Returns 0 for success, -1 for 1464e0697ebcSnicm * failure, 1 for partial. 1465e0697ebcSnicm */ 1466e0697ebcSnicm static int 1467e0697ebcSnicm tty_keys_device_attributes2(struct tty *tty, const char *buf, size_t len, 1468e0697ebcSnicm size_t *size) 1469e0697ebcSnicm { 1470e0697ebcSnicm struct client *c = tty->client; 1471f4ec6bcaSnicm int *features = &c->term_features; 1472e0697ebcSnicm u_int i, n = 0; 1473e0697ebcSnicm char tmp[128], *endptr, p[32] = { 0 }, *cp, *next; 1474e0697ebcSnicm 1475e0697ebcSnicm *size = 0; 1476e0697ebcSnicm if (tty->flags & TTY_HAVEDA2) 1477e0697ebcSnicm return (-1); 1478e0697ebcSnicm 1479e0697ebcSnicm /* First three bytes are always \033[>. */ 1480e0697ebcSnicm if (buf[0] != '\033') 1481e0697ebcSnicm return (-1); 1482e0697ebcSnicm if (len == 1) 1483e0697ebcSnicm return (1); 1484e0697ebcSnicm if (buf[1] != '[') 1485e0697ebcSnicm return (-1); 1486e0697ebcSnicm if (len == 2) 1487e0697ebcSnicm return (1); 1488e0697ebcSnicm if (buf[2] != '>') 1489e0697ebcSnicm return (-1); 1490e0697ebcSnicm if (len == 3) 1491e0697ebcSnicm return (1); 1492e0697ebcSnicm 1493d21788ceSnicm /* Copy the rest up to a c. */ 1494e0697ebcSnicm for (i = 0; i < (sizeof tmp); i++) { 1495e0697ebcSnicm if (3 + i == len) 1496e0697ebcSnicm return (1); 1497e0697ebcSnicm if (buf[3 + i] == 'c') 1498e0697ebcSnicm break; 1499e0697ebcSnicm tmp[i] = buf[3 + i]; 1500e0697ebcSnicm } 1501e0697ebcSnicm if (i == (sizeof tmp)) 1502e0697ebcSnicm return (-1); 1503e0697ebcSnicm tmp[i] = '\0'; 1504e0697ebcSnicm *size = 4 + i; 1505e0697ebcSnicm 1506e0697ebcSnicm /* Convert all arguments to numbers. */ 1507e0697ebcSnicm cp = tmp; 1508e0697ebcSnicm while ((next = strsep(&cp, ";")) != NULL) { 1509e0697ebcSnicm p[n] = strtoul(next, &endptr, 10); 1510e0697ebcSnicm if (*endptr != '\0') 1511e0697ebcSnicm p[n] = 0; 1512e0697ebcSnicm if (++n == nitems(p)) 1513e0697ebcSnicm break; 15142afb856aSnicm } 1515f00ac6e6Snicm 1516da4edc7fSnicm /* 1517da4edc7fSnicm * Add terminal features. We add DECSLRM and DECFRA for some 1518da4edc7fSnicm * identification codes here, notably 64 will catch VT520, even though 1519da4edc7fSnicm * we can't use level 5 from DA because of VTE. 1520da4edc7fSnicm */ 15212afb856aSnicm switch (p[0]) { 152225e90a40Snicm case 'M': /* mintty */ 1523f4ec6bcaSnicm tty_default_features(features, "mintty", 0); 152425e90a40Snicm break; 15255a160f88Snicm case 'T': /* tmux */ 1526f4ec6bcaSnicm tty_default_features(features, "tmux", 0); 152725e90a40Snicm break; 152825e90a40Snicm case 'U': /* rxvt-unicode */ 1529f4ec6bcaSnicm tty_default_features(features, "rxvt-unicode", 0); 153025e90a40Snicm break; 1531f00ac6e6Snicm } 153225e90a40Snicm log_debug("%s: received secondary DA %.*s", c->name, (int)*size, buf); 15336648e05cSnicm 15345a160f88Snicm tty_update_features(tty); 1535e0697ebcSnicm tty->flags |= TTY_HAVEDA2; 15366648e05cSnicm 1537f00ac6e6Snicm return (0); 1538f00ac6e6Snicm } 15396110b15cSnicm 15406110b15cSnicm /* 1541ecd9db25Snicm * Handle extended device attributes input. Returns 0 for success, -1 for 1542ecd9db25Snicm * failure, 1 for partial. 15436110b15cSnicm */ 15446110b15cSnicm static int 1545ecd9db25Snicm tty_keys_extended_device_attributes(struct tty *tty, const char *buf, 1546ecd9db25Snicm size_t len, size_t *size) 15476110b15cSnicm { 15486110b15cSnicm struct client *c = tty->client; 1549f4ec6bcaSnicm int *features = &c->term_features; 15506110b15cSnicm u_int i; 15518942cc58Snicm char tmp[128]; 15526110b15cSnicm 15536110b15cSnicm *size = 0; 1554ecd9db25Snicm if (tty->flags & TTY_HAVEXDA) 15556648e05cSnicm return (-1); 15566110b15cSnicm 1557ecd9db25Snicm /* First four bytes are always \033P>|. */ 15586110b15cSnicm if (buf[0] != '\033') 15596110b15cSnicm return (-1); 15606110b15cSnicm if (len == 1) 15616110b15cSnicm return (1); 1562ecd9db25Snicm if (buf[1] != 'P') 15636110b15cSnicm return (-1); 15646110b15cSnicm if (len == 2) 15656110b15cSnicm return (1); 1566ecd9db25Snicm if (buf[2] != '>') 15676648e05cSnicm return (-1); 15686648e05cSnicm if (len == 3) 15696648e05cSnicm return (1); 1570ecd9db25Snicm if (buf[3] != '|') 1571ecd9db25Snicm return (-1); 1572ecd9db25Snicm if (len == 4) 15736110b15cSnicm return (1); 1574ecd9db25Snicm 1575d21788ceSnicm /* Copy the rest up to \033\. */ 1576ecd9db25Snicm for (i = 0; i < (sizeof tmp) - 1; i++) { 1577ecd9db25Snicm if (4 + i == len) 1578ecd9db25Snicm return (1); 1579ecd9db25Snicm if (buf[4 + i - 1] == '\033' && buf[4 + i] == '\\') 1580ecd9db25Snicm break; 1581ecd9db25Snicm tmp[i] = buf[4 + i]; 15826110b15cSnicm } 15836110b15cSnicm if (i == (sizeof tmp) - 1) 15846110b15cSnicm return (-1); 1585ecd9db25Snicm tmp[i - 1] = '\0'; 1586ecd9db25Snicm *size = 5 + i; 15876110b15cSnicm 15885a160f88Snicm /* Add terminal features. */ 15898942cc58Snicm if (strncmp(tmp, "iTerm2 ", 7) == 0) 1590f4ec6bcaSnicm tty_default_features(features, "iTerm2", 0); 15918942cc58Snicm else if (strncmp(tmp, "tmux ", 5) == 0) 1592f4ec6bcaSnicm tty_default_features(features, "tmux", 0); 15938942cc58Snicm else if (strncmp(tmp, "XTerm(", 6) == 0) 1594f4ec6bcaSnicm tty_default_features(features, "XTerm", 0); 15958942cc58Snicm else if (strncmp(tmp, "mintty ", 7) == 0) 1596f4ec6bcaSnicm tty_default_features(features, "mintty", 0); 1597a65ab485Snicm else if (strncmp(tmp, "foot(", 5) == 0) 1598a65ab485Snicm tty_default_features(features, "foot", 0); 1599ecd9db25Snicm log_debug("%s: received extended DA %.*s", c->name, (int)*size, buf); 16006648e05cSnicm 16018942cc58Snicm free(c->term_type); 16028942cc58Snicm c->term_type = xstrdup(tmp); 16038942cc58Snicm 16045a160f88Snicm tty_update_features(tty); 1605ecd9db25Snicm tty->flags |= TTY_HAVEXDA; 16066648e05cSnicm 16076110b15cSnicm return (0); 16086110b15cSnicm } 1609d21788ceSnicm 1610d21788ceSnicm /* 1611d21788ceSnicm * Handle foreground or background input. Returns 0 for success, -1 for 1612d21788ceSnicm * failure, 1 for partial. 1613d21788ceSnicm */ 1614891e2565Snicm int 1615891e2565Snicm tty_keys_colours(struct tty *tty, const char *buf, size_t len, size_t *size, 1616891e2565Snicm int *fg, int *bg) 1617d21788ceSnicm { 1618d21788ceSnicm struct client *c = tty->client; 1619d21788ceSnicm u_int i; 1620d21788ceSnicm char tmp[128]; 1621d21788ceSnicm int n; 1622d21788ceSnicm 1623d21788ceSnicm *size = 0; 1624d21788ceSnicm 1625d21788ceSnicm /* First four bytes are always \033]1 and 0 or 1 and ;. */ 1626d21788ceSnicm if (buf[0] != '\033') 1627d21788ceSnicm return (-1); 1628d21788ceSnicm if (len == 1) 1629d21788ceSnicm return (1); 1630d21788ceSnicm if (buf[1] != ']') 1631d21788ceSnicm return (-1); 1632d21788ceSnicm if (len == 2) 1633d21788ceSnicm return (1); 1634d21788ceSnicm if (buf[2] != '1') 1635d21788ceSnicm return (-1); 1636d21788ceSnicm if (len == 3) 1637d21788ceSnicm return (1); 1638d21788ceSnicm if (buf[3] != '0' && buf[3] != '1') 1639d21788ceSnicm return (-1); 1640d21788ceSnicm if (len == 4) 1641d21788ceSnicm return (1); 1642d21788ceSnicm if (buf[4] != ';') 1643d21788ceSnicm return (-1); 1644d21788ceSnicm if (len == 5) 1645d21788ceSnicm return (1); 1646d21788ceSnicm 1647ce2a7883Snicm /* Copy the rest up to \033\ or \007. */ 1648d21788ceSnicm for (i = 0; i < (sizeof tmp) - 1; i++) { 1649d21788ceSnicm if (5 + i == len) 1650d21788ceSnicm return (1); 1651d21788ceSnicm if (buf[5 + i - 1] == '\033' && buf[5 + i] == '\\') 1652d21788ceSnicm break; 1653ce2a7883Snicm if (buf[5 + i] == '\007') 1654ce2a7883Snicm break; 1655d21788ceSnicm tmp[i] = buf[5 + i]; 1656d21788ceSnicm } 1657d21788ceSnicm if (i == (sizeof tmp) - 1) 1658d21788ceSnicm return (-1); 1659affa3703Snicm if (tmp[i - 1] == '\033') 1660ce2a7883Snicm tmp[i - 1] = '\0'; 1661affa3703Snicm else 1662affa3703Snicm tmp[i] = '\0'; 1663affa3703Snicm *size = 6 + i; 1664d21788ceSnicm 1665d21788ceSnicm n = colour_parseX11(tmp); 1666d21788ceSnicm if (n != -1 && buf[3] == '0') { 1667891e2565Snicm if (c != NULL) 1668891e2565Snicm log_debug("%s fg is %s", c->name, colour_tostring(n)); 1669891e2565Snicm else 1670891e2565Snicm log_debug("fg is %s", colour_tostring(n)); 1671891e2565Snicm *fg = n; 1672d21788ceSnicm } else if (n != -1) { 1673891e2565Snicm if (c != NULL) 1674891e2565Snicm log_debug("%s bg is %s", c->name, colour_tostring(n)); 1675891e2565Snicm else 1676891e2565Snicm log_debug("bg is %s", colour_tostring(n)); 1677891e2565Snicm *bg = n; 1678d21788ceSnicm } 1679d21788ceSnicm 1680d21788ceSnicm return (0); 1681d21788ceSnicm } 1682