File nst_config.hxx of Package nst

#pragma once

// C++
#include <array>
#include <chrono>
#include <set>
#include <string_view>

// xpp
#include "xpp/keyboard.hxx"
#include "xpp/types.hxx"
#include "xpp/XCursor.hxx"

// nst
#include "fwd.hxx"
#include "Selection.hxx"
#include "themes.hxx"
#include "types.hxx"

namespace nst::config {

/// Default character font to use.
/**
 * \see http://freedesktop.org/software/fontconfig/fontconfig-user.html
 **/
constexpr std::string_view FONT{"Liberation Mono:pixelsize=12:antialias=true:autohint=true"};
/// Font default pixel size to use, if not specified in FONT
constexpr double FONT_DEFAULT_SIZE_PX = 12;

/// Word delimiter string for expanding selection upon double/triple clicking
/**
 * More advanced example: L" `'\"()[]{}"
 **/
inline constexpr const std::wstring_view WORD_DELIMITERS{L" \"<>()[]'{}:/"};

/// How stty will be invoked when operating on real TTY lines.
constexpr std::array<std::string_view, 8> STTY_ARGS{{
	"stty", "raw", "pass8", "nl", "-echo", "-iexten", "-cstopb", "38400"
}};

/// The default command to invoke for when the "open_buffer_in_editor" keyboard shortcut is executed.
const std::vector<std::string> EXTERNAL_PIPE_CMDLINE{"gvim", "--not-a-term", "-"};

/*
 * What program is executed by nst depends on these precedence rules:
 * 1: program passed with -e
 * 2: scroll and/or utmp
 * 3: SHELL environment variable
 * 4: value of shell in /etc/passwd
 * 5: value of SHELL in here
 */

/// Shell to execute if none found in environment or on command line
constexpr std::string_view SHELL{"/bin/sh"};
constexpr std::string_view UTMP{};
/// Scroll program: to enable use a string like "scroll"
constexpr std::string_view SCROLL{};
/// Default TERM value
constexpr std::string_view TERM_NAME{"nst-256color"};

/// Identification sequence returned in DA and DECID escape sequences
constexpr std::string_view VT_IDENT{"\033[?6c"};

/// Allow certain non-interactive (insecure) window operations such as setting the clipboard text
constexpr bool ALLOW_WINDOW_OPS = false;

/// Spaces per tab.
/**
 * When you are changing this value, don't forget to adapt the »it« value in
 * the nst.info and appropriately install the nst.info in the environment where
 * you use this nst version.
 *
 *	it#$TABSPACES,
 *
 * Secondly make sure your kernel is not expanding tabs. When running `stty
 * -a` »tab0« should appear. You can tell the terminal to not expand tabs by
 *  running following command:
 *
 *	stty tabs
 */
constexpr int TABSPACES = 8;

/// Allow alternative screen usage
constexpr bool ALLOW_ALTSCREEN = true;

/// Number of border pixels between window frame and actual terminal characters
constexpr int BORDERPX = 2;

/* Kerning / character bounding-box multipliers */
constexpr float CW_SCALE = 1.0;
constexpr float CH_SCALE = 1.0;

/// Double click selection timeout.
constexpr std::chrono::milliseconds DOUBLE_CLICK_TIMEOUT{300};

/// Automatically clear selection when selection ownership is lost.
/**
 * Set this to true if you want the selection to disappear when you select
 * something different in another window.
 **/
constexpr bool SEL_CLEAR = false;

/// Keep trailing newlines in Selection::Flag::LINES style selections.
/**
 * When selecting a range of full lines, keep trailing newlines. Upon
 * pasting this causes a final newline to be pasted, as well. This can be
 * undesirable when pasting e.g. a single line in a shell prompt (which you
 * want to edit, before executing it).
 *
 * If disabled then trailing newline characters will be skipped during
 * pasting.
 **/
constexpr bool LINE_PASTE_KEEP_NEWLINE = false;

/// Minimum draw latency.
/*
 * Time from new content/keypress/etc until drawing.
 *
 * Within this range, nst draws when content stops arriving (idle). Mostly
 * it's near MINLATENCY, but it waits longer for slow updates to avoid partial
 * draw. Low MINLATENCY Will tear/flicker more, as it can "detect" idle too
 * early.
 */
constexpr std::chrono::milliseconds MIN_LATENCY{8};
/// Maximum draw latency.
/**
 * \see MIN_LATENCY
 **/
constexpr std::chrono::milliseconds MAX_LATENCY{33};

/// Blinking timeout.
/**
 * Set to 0 to disable blinking. This is used for the terminal blinking
 * attribute.
 **/
constexpr std::chrono::milliseconds BLINK_TIMEOUT{800};

/// Thickness of bar style cursors.
constexpr int CURSOR_THICKNESS = 2;

/// Terminal bell volume.
/**
 * It must be a value between -100 and 100. Use 0 for disabling it.
 **/
constexpr xpp::BellVolume BELL_VOLUME{0};

/// Default colors to use, see themes.hxx for available themes.
const auto THEME = DEFAULT_THEME;

/// Default shape of cursor
constexpr CursorStyle CURSORSHAPE = CursorStyle::STEADY_BLOCK;

/// Default number of columns.
constexpr unsigned int COLS = 80;
/// Default number of rows.
constexpr unsigned int ROWS = 24;
/// Number of lines kept in the scrollback buffer.
constexpr size_t HISTORY_LEN = 10000;
/// Whether nst should keep a selected scrollback position even when new TTY data comes in.
/**
 * When the terminal history is displayed then the question arises what to do
 * when new TTY data comes in from the shell or another running process. If
 * this is set to `true` then nst will keep the scrolled-to position until
 * either
 *
 * - an interactive keyboard / mouse event occurs (except for scrollback
 *   control).
 * - the history buffer is full and overwritten by new data, in which case nst
 *   will scroll to the oldest available history data.
 *
 * If set to `false` then nst will jump back to the current screen in these
 * cases. Like this also includes window resize events, if the active program
 * (e.g. the shell) reacts to the event and changes terminal content. You can
 * still stop the terminal process via Ctrl + S to prevent any new output to
 * appear.
 **/
constexpr bool KEEP_SCROLL_POSITION = true;

/// Whether nst should offer a UNIX domain socket IPC for the nst-msg tool.
/**
 * If enabled then a UNIX domain listen socket is created for each nst
 * instance. The `nst-msg` utility can be used to talk to the terminal for
 * obtaining terminal history contents e.g. for grepping on it.
 **/
constexpr bool ENABLE_IPC = true;

/// Default shape of the mouse cursor.
constexpr xpp::CursorFont MOUSE_SHAPE{xpp::CursorFont::XTERM};
/// Default foreground color of the mouse cursor.
constexpr ColorIndex MOUSE_FG{7};
/// Default background color of the mouse cursor.
constexpr ColorIndex MOUSE_BG{0};
/// If set then the mouse cursor in will be hidden from the terminal window when typing.
constexpr bool HIDE_MOUSE_CURSOR = true;

/// Fallback color to use if no matching font is found.
/**
 * Color used to display font attributes when fontconfig selected a font which
 * doesn't match the ones requested.
 **/
constexpr ColorIndex DEFAULT_ATTR{11};

/// Input modifier mask which forces mouse select/shortcuts in WinMode::MOUSE.
/**
 * Force mouse select/shortcuts while mask is active (when WinMode::MOUSE is
 * set). Some terminal applications may interpret mouse events and prevent you
 * from performing select operations on nst level. For still being able to
 * perform nst operations, this modifier can be pressed to bypass the
 * application.
 *
 * Note that if this modifier will have priority over the same modifiers used
 * in SEL_MASKS. The latter will become unusable, if the modifiers conflict.
 **/
constexpr xpp::InputMask FORCE_MOUSE_MOD{xpp::InputModifier::SHIFT};

// shorthands for shorter definitions below
using KeyID     = xpp::KeySymID;
using Mod       = xpp::InputModifier;
using Mask      = xpp::InputMask;
using AppKey    = Key::AppKeypad;
using AppCursor = Key::AppCursor;

/// Modifier state to ignore when matching key or button events.
/**
 * By default, numlock (MOD2) and keyboard layout (XKB_GROUP_INDEX) are
 * ignored. The latter is an implementation detail when using multiple
 * keyboard (layouts).
 **/
inline constexpr xpp::InputMask IGNORE_MOD{Mod::MOD2, Mod::XKB_GROUP_INDEX};

/*
 * Special keys (change & recompile nst.info accordingly)
 *
 * Mask value:
 * * Use Mod::ANY to match the key no matter modifiers state
 * * Use Mod::NONE to match the key alone (no modifiers)
 * appkeypad value: matches the current terminal's application keypad state
 * appcursor: matches the current terminal's application cursor state
 *
 * Be careful with the order of the definitions because nst searches in
 * this table sequentially, so any Mod::ANY must be in the last
 * position for a key.
 */

/// Additionally mapped keys.
/**
 * If you want keys other than the X11 function keys (0xFD00 - 0xFFFF)
 * to work, add them to this array. This set only determines
 * whether the given keys will be processed at all. Individual key
 * mappings still need to be entered below into KEYS.
 **/
inline const std::set<xpp::KeySymID> MAPPED_KEYS{};

/// List of special function key definitions.
/**
 * This is the huge list of keys which defines all compatibility to the Linux
 * world. Please decide about changes wisely.
 *
 * We use a multiset for the key definitions. The KeyID is the comparison
 * key, so we don't have to iterate over the complete list of keys linearly,
 * but only over a small list of key combinations that share the same KeyID.
 **/
inline const std::multiset<Key> KEYS{{
	// keysym               mask                              string          appkey             appcursor
	{ KeyID::KP_HOME,       Mask{Mod::SHIFT},                 "\033[2J",      AppKey::IGNORE,    AppCursor::DISABLED},
	{ KeyID::KP_HOME,       Mask{Mod::SHIFT},                 "\033[1;2H",    AppKey::IGNORE,    AppCursor::ENABLED},
	{ KeyID::KP_HOME,       Mask{Mod::ANY},                   "\033[H",       AppKey::IGNORE,    AppCursor::DISABLED},
	{ KeyID::KP_HOME,       Mask{Mod::ANY},                   "\033[1~",      AppKey::IGNORE,    AppCursor::ENABLED},
	{ KeyID::KP_UP,         Mask{Mod::ANY},                   "\033Ox",       AppKey::ENABLED,   AppCursor::IGNORE},
	{ KeyID::KP_UP,         Mask{Mod::ANY},                   "\033[A",       AppKey::IGNORE,    AppCursor::DISABLED},
	{ KeyID::KP_UP,         Mask{Mod::ANY},                   "\033OA",       AppKey::IGNORE,    AppCursor::ENABLED},
	{ KeyID::KP_DOWN,       Mask{Mod::ANY},                   "\033Or",       AppKey::ENABLED,   AppCursor::IGNORE},
	{ KeyID::KP_DOWN,       Mask{Mod::ANY},                   "\033[B",       AppKey::IGNORE,    AppCursor::DISABLED},
	{ KeyID::KP_DOWN,       Mask{Mod::ANY},                   "\033OB",       AppKey::IGNORE,    AppCursor::ENABLED},
	{ KeyID::KP_LEFT,       Mask{Mod::ANY},                   "\033Ot",       AppKey::ENABLED,   AppCursor::IGNORE},
	{ KeyID::KP_LEFT,       Mask{Mod::ANY},                   "\033[D",       AppKey::IGNORE,    AppCursor::DISABLED},
	{ KeyID::KP_LEFT,       Mask{Mod::ANY},                   "\033OD",       AppKey::IGNORE,    AppCursor::ENABLED},
	{ KeyID::KP_RIGHT,      Mask{Mod::ANY},                   "\033Ov",       AppKey::ENABLED,   AppCursor::IGNORE},
	{ KeyID::KP_RIGHT,      Mask{Mod::ANY},                   "\033[C",       AppKey::IGNORE,    AppCursor::DISABLED},
	{ KeyID::KP_RIGHT,      Mask{Mod::ANY},                   "\033OC",       AppKey::IGNORE,    AppCursor::ENABLED},
	{ KeyID::KP_PRIOR,      Mask{Mod::SHIFT},                 "\033[5;2~",    AppKey::IGNORE,    AppCursor::IGNORE},
	{ KeyID::KP_PRIOR,      Mask{Mod::ANY},                   "\033[5~",      AppKey::IGNORE,    AppCursor::IGNORE},
	{ KeyID::KP_BEGIN,      Mask{Mod::ANY},                   "\033[E",       AppKey::IGNORE,    AppCursor::IGNORE},
	{ KeyID::KP_END,        Mask{Mod::CONTROL},               "\033[J",       AppKey::DISABLED,  AppCursor::IGNORE},
	{ KeyID::KP_END,        Mask{Mod::CONTROL},               "\033[1;5F",    AppKey::ENABLED,   AppCursor::IGNORE},
	{ KeyID::KP_END,        Mask{Mod::SHIFT},                 "\033[K",       AppKey::DISABLED,  AppCursor::IGNORE},
	{ KeyID::KP_END,        Mask{Mod::SHIFT},                 "\033[1;2F",    AppKey::ENABLED,   AppCursor::IGNORE},
	{ KeyID::KP_END,        Mask{Mod::ANY},                   "\033[4~",      AppKey::IGNORE,    AppCursor::IGNORE},
	{ KeyID::KP_NEXT,       Mask{Mod::SHIFT},                 "\033[6;2~",    AppKey::IGNORE,    AppCursor::IGNORE},
	{ KeyID::KP_NEXT,       Mask{Mod::ANY},                   "\033[6~",      AppKey::IGNORE,    AppCursor::IGNORE},
	{ KeyID::KP_INSERT,     Mask{Mod::SHIFT},                 "\033[2;2~",    AppKey::ENABLED,   AppCursor::IGNORE},
	{ KeyID::KP_INSERT,     Mask{Mod::SHIFT},                 "\033[4l",      AppKey::DISABLED,  AppCursor::IGNORE},
	{ KeyID::KP_INSERT,     Mask{Mod::CONTROL},               "\033[L",       AppKey::DISABLED,  AppCursor::IGNORE},
	{ KeyID::KP_INSERT,     Mask{Mod::CONTROL},               "\033[2;5~",    AppKey::ENABLED,   AppCursor::IGNORE},
	{ KeyID::KP_INSERT,     Mask{Mod::ANY},                   "\033[4h",      AppKey::DISABLED,  AppCursor::IGNORE},
	{ KeyID::KP_INSERT,     Mask{Mod::ANY},                   "\033[2~",      AppKey::ENABLED,   AppCursor::IGNORE},
	{ KeyID::KP_DELETE,     Mask{Mod::CONTROL},               "\033[M",       AppKey::DISABLED,  AppCursor::IGNORE},
	{ KeyID::KP_DELETE,     Mask{Mod::CONTROL},               "\033[3;5~",    AppKey::ENABLED,   AppCursor::IGNORE},
	{ KeyID::KP_DELETE,     Mask{Mod::SHIFT},                 "\033[2K",      AppKey::DISABLED,  AppCursor::IGNORE},
	{ KeyID::KP_DELETE,     Mask{Mod::SHIFT},                 "\033[3;2~",    AppKey::ENABLED,   AppCursor::IGNORE},
	{ KeyID::KP_DELETE,     Mask{Mod::ANY},                   "\033[P",       AppKey::DISABLED,  AppCursor::IGNORE},
	{ KeyID::KP_DELETE,     Mask{Mod::ANY},                   "\033[3~",      AppKey::ENABLED,   AppCursor::IGNORE},
	{ KeyID::KP_MULTIPLY,   Mask{Mod::ANY},                   "\033Oj",       AppKey::NO_NUMLOCK,AppCursor::IGNORE},
	{ KeyID::KP_ADD,        Mask{Mod::ANY},                   "\033Ok",       AppKey::NO_NUMLOCK,AppCursor::IGNORE},
	{ KeyID::KP_ENTER,      Mask{Mod::ANY},                   "\033OM",       AppKey::NO_NUMLOCK,AppCursor::IGNORE},
	{ KeyID::KP_ENTER,      Mask{Mod::ANY},                   "\r",           AppKey::DISABLED,  AppCursor::IGNORE},
	{ KeyID::KP_SUBTRACT,   Mask{Mod::ANY},                   "\033Om",       AppKey::NO_NUMLOCK,AppCursor::IGNORE},
	{ KeyID::KP_DECIMAL,    Mask{Mod::ANY},                   "\033On",       AppKey::NO_NUMLOCK,AppCursor::IGNORE},
	{ KeyID::KP_DIVIDE,     Mask{Mod::ANY},                   "\033Oo",       AppKey::NO_NUMLOCK,AppCursor::IGNORE},
	{ KeyID::KP_0,          Mask{Mod::ANY},                   "\033Op",       AppKey::NO_NUMLOCK,AppCursor::IGNORE},
	{ KeyID::KP_1,          Mask{Mod::ANY},                   "\033Oq",       AppKey::NO_NUMLOCK,AppCursor::IGNORE},
	{ KeyID::KP_2,          Mask{Mod::ANY},                   "\033Or",       AppKey::NO_NUMLOCK,AppCursor::IGNORE},
	{ KeyID::KP_3,          Mask{Mod::ANY},                   "\033Os",       AppKey::NO_NUMLOCK,AppCursor::IGNORE},
	{ KeyID::KP_4,          Mask{Mod::ANY},                   "\033Ot",       AppKey::NO_NUMLOCK,AppCursor::IGNORE},
	{ KeyID::KP_5,          Mask{Mod::ANY},                   "\033Ou",       AppKey::NO_NUMLOCK,AppCursor::IGNORE},
	{ KeyID::KP_6,          Mask{Mod::ANY},                   "\033Ov",       AppKey::NO_NUMLOCK,AppCursor::IGNORE},
	{ KeyID::KP_7,          Mask{Mod::ANY},                   "\033Ow",       AppKey::NO_NUMLOCK,AppCursor::IGNORE},
	{ KeyID::KP_8,          Mask{Mod::ANY},                   "\033Ox",       AppKey::NO_NUMLOCK,AppCursor::IGNORE},
	{ KeyID::KP_9,          Mask{Mod::ANY},                   "\033Oy",       AppKey::NO_NUMLOCK,AppCursor::IGNORE},
	{ KeyID::UP,            Mask{Mod::SHIFT},                 "\033[1;2A",    AppKey::IGNORE,    AppCursor::IGNORE},
	{ KeyID::UP,            Mask{Mod::MOD1},                  "\033[1;3A",    AppKey::IGNORE,    AppCursor::IGNORE},
	{ KeyID::UP,            Mask{Mod::SHIFT, Mod::MOD1},      "\033[1;4A",    AppKey::IGNORE,    AppCursor::IGNORE},
	{ KeyID::UP,            Mask{Mod::CONTROL},               "\033[1;5A",    AppKey::IGNORE,    AppCursor::IGNORE},
	{ KeyID::UP,            Mask{Mod::SHIFT, Mod::CONTROL},   "\033[1;6A",    AppKey::IGNORE,    AppCursor::IGNORE},
	{ KeyID::UP,            Mask{Mod::CONTROL, Mod::MOD1},    "\033[1;7A",    AppKey::IGNORE,    AppCursor::IGNORE},
	{ KeyID::UP,            Mask{Mod::SHIFT, Mod::CONTROL, Mod::MOD1},
		                                                  "\033[1;8A",    AppKey::IGNORE,    AppCursor::IGNORE},
	{ KeyID::UP,            Mask{Mod::ANY},                   "\033[A",       AppKey::IGNORE,    AppCursor::DISABLED},
	{ KeyID::UP,            Mask{Mod::ANY},                   "\033OA",       AppKey::IGNORE,    AppCursor::ENABLED},
	{ KeyID::DOWN,          Mask{Mod::SHIFT},                 "\033[1;2B",    AppKey::IGNORE,    AppCursor::IGNORE},
	{ KeyID::DOWN,          Mask{Mod::MOD1},                  "\033[1;3B",    AppKey::IGNORE,    AppCursor::IGNORE},
	{ KeyID::DOWN,          Mask{Mod::SHIFT, Mod::MOD1},      "\033[1;4B",    AppKey::IGNORE,    AppCursor::IGNORE},
	{ KeyID::DOWN,          Mask{Mod::CONTROL},               "\033[1;5B",    AppKey::IGNORE,    AppCursor::IGNORE},
	{ KeyID::DOWN,          Mask{Mod::SHIFT, Mod::CONTROL},   "\033[1;6B",    AppKey::IGNORE,    AppCursor::IGNORE},
	{ KeyID::DOWN,          Mask{Mod::CONTROL, Mod::MOD1},    "\033[1;7B",    AppKey::IGNORE,    AppCursor::IGNORE},
	{ KeyID::DOWN,          Mask{Mod::SHIFT, Mod::CONTROL, Mod::MOD1},
		                                                  "\033[1;8B",    AppKey::IGNORE,    AppCursor::IGNORE},
	{ KeyID::DOWN,          Mask{Mod::ANY},                   "\033[B",       AppKey::IGNORE,    AppCursor::DISABLED},
	{ KeyID::DOWN,          Mask{Mod::ANY},                   "\033OB",       AppKey::IGNORE,    AppCursor::ENABLED},
	{ KeyID::LEFT,          Mask{Mod::SHIFT},                 "\033[1;2D",    AppKey::IGNORE,    AppCursor::IGNORE},
	{ KeyID::LEFT,          Mask{Mod::MOD1},                  "\033[1;3D",    AppKey::IGNORE,    AppCursor::IGNORE},
	{ KeyID::LEFT,          Mask{Mod::SHIFT, Mod::MOD1},      "\033[1;4D",    AppKey::IGNORE,    AppCursor::IGNORE},
	{ KeyID::LEFT,          Mask{Mod::CONTROL},               "\033[1;5D",    AppKey::IGNORE,    AppCursor::IGNORE},
	{ KeyID::LEFT,          Mask{Mod::SHIFT, Mod::CONTROL},   "\033[1;6D",    AppKey::IGNORE,    AppCursor::IGNORE},
	{ KeyID::LEFT,          Mask{Mod::CONTROL,Mod::MOD1},     "\033[1;7D",    AppKey::IGNORE,    AppCursor::IGNORE},
	{ KeyID::LEFT,          Mask{Mod::SHIFT,Mod::CONTROL,Mod::MOD1},
		                                                  "\033[1;8D",    AppKey::IGNORE,    AppCursor::IGNORE},
	{ KeyID::LEFT,          Mask{Mod::ANY},                   "\033[D",       AppKey::IGNORE,    AppCursor::DISABLED},
	{ KeyID::LEFT,          Mask{Mod::ANY},                   "\033OD",       AppKey::IGNORE,    AppCursor::ENABLED},
	{ KeyID::RIGHT,         Mask{Mod::SHIFT},                 "\033[1;2C",    AppKey::IGNORE,    AppCursor::IGNORE},
	{ KeyID::RIGHT,         Mask{Mod::MOD1},                  "\033[1;3C",    AppKey::IGNORE,    AppCursor::IGNORE},
	{ KeyID::RIGHT,         Mask{Mod::SHIFT,Mod::MOD1},       "\033[1;4C",    AppKey::IGNORE,    AppCursor::IGNORE},
	{ KeyID::RIGHT,         Mask{Mod::CONTROL},               "\033[1;5C",    AppKey::IGNORE,    AppCursor::IGNORE},
	{ KeyID::RIGHT,         Mask{Mod::SHIFT,Mod::CONTROL},    "\033[1;6C",    AppKey::IGNORE,    AppCursor::IGNORE},
	{ KeyID::RIGHT,         Mask{Mod::CONTROL,Mod::MOD1},     "\033[1;7C",    AppKey::IGNORE,    AppCursor::IGNORE},
	{ KeyID::RIGHT,         Mask{Mod::SHIFT,Mod::CONTROL,Mod::MOD1},
		                                                  "\033[1;8C",    AppKey::IGNORE,    AppCursor::IGNORE},
	{ KeyID::RIGHT,         Mask{Mod::ANY},                   "\033[C",       AppKey::IGNORE,    AppCursor::DISABLED},
	{ KeyID::RIGHT,         Mask{Mod::ANY},                   "\033OC",       AppKey::IGNORE,    AppCursor::ENABLED},
	{ KeyID::ISO_LEFT_TAB,  Mask{Mod::SHIFT},                 "\033[Z",       AppKey::IGNORE,    AppCursor::IGNORE},
	{ KeyID::RETURN,        Mask{Mod::MOD1},                  "\033\r",       AppKey::IGNORE,    AppCursor::IGNORE},
	{ KeyID::RETURN,        Mask{Mod::ANY},                   "\r",           AppKey::IGNORE,    AppCursor::IGNORE},
	{ KeyID::INSERT,        Mask{Mod::SHIFT},                 "\033[4l",      AppKey::DISABLED,  AppCursor::IGNORE},
	{ KeyID::INSERT,        Mask{Mod::SHIFT},                 "\033[2;2~",    AppKey::ENABLED,   AppCursor::IGNORE},
	{ KeyID::INSERT,        Mask{Mod::CONTROL},               "\033[L",       AppKey::DISABLED,  AppCursor::IGNORE},
	{ KeyID::INSERT,        Mask{Mod::CONTROL},               "\033[2;5~",    AppKey::ENABLED,   AppCursor::IGNORE},
	{ KeyID::INSERT,        Mask{Mod::ANY},                   "\033[4h",      AppKey::DISABLED,  AppCursor::IGNORE},
	{ KeyID::INSERT,        Mask{Mod::ANY},                   "\033[2~",      AppKey::ENABLED,   AppCursor::IGNORE},
	{ KeyID::DELETE,        Mask{Mod::CONTROL},               "\033[M",       AppKey::DISABLED,  AppCursor::IGNORE},
	{ KeyID::DELETE,        Mask{Mod::CONTROL},               "\033[3;5~",    AppKey::ENABLED,   AppCursor::IGNORE},
	{ KeyID::DELETE,        Mask{Mod::SHIFT},                 "\033[2K",      AppKey::DISABLED,  AppCursor::IGNORE},
	{ KeyID::DELETE,        Mask{Mod::SHIFT},                 "\033[3;2~",    AppKey::ENABLED,   AppCursor::IGNORE},
	{ KeyID::DELETE,        Mask{Mod::ANY},                   "\033[P",       AppKey::DISABLED,  AppCursor::IGNORE},
	{ KeyID::DELETE,        Mask{Mod::ANY},                   "\033[3~",      AppKey::ENABLED,   AppCursor::IGNORE},
	{ KeyID::BACKSPACE,     Mask{Mod::NONE},                  "\177",         AppKey::IGNORE,    AppCursor::IGNORE},
	{ KeyID::BACKSPACE,     Mask{Mod::MOD1},                  "\033\177",     AppKey::IGNORE,    AppCursor::IGNORE},
	{ KeyID::HOME,          Mask{Mod::SHIFT},                 "\033[2J",      AppKey::IGNORE,    AppCursor::DISABLED},
	{ KeyID::HOME,          Mask{Mod::SHIFT},                 "\033[1;2H",    AppKey::IGNORE,    AppCursor::ENABLED},
	{ KeyID::HOME,          Mask{Mod::ANY},                   "\033[H",       AppKey::IGNORE,    AppCursor::DISABLED},
	{ KeyID::HOME,          Mask{Mod::ANY},                   "\033[1~",      AppKey::IGNORE,    AppCursor::ENABLED},
	{ KeyID::END,           Mask{Mod::CONTROL},               "\033[J",       AppKey::DISABLED,  AppCursor::IGNORE},
	{ KeyID::END,           Mask{Mod::CONTROL},               "\033[1;5F",    AppKey::ENABLED,   AppCursor::IGNORE},
	{ KeyID::END,           Mask{Mod::SHIFT},                 "\033[K",       AppKey::DISABLED,  AppCursor::IGNORE},
	{ KeyID::END,           Mask{Mod::SHIFT},                 "\033[1;2F",    AppKey::ENABLED,   AppCursor::IGNORE},
	{ KeyID::END,           Mask{Mod::ANY},                   "\033[4~",      AppKey::IGNORE,    AppCursor::IGNORE},
	{ KeyID::PRIOR,         Mask{Mod::CONTROL},               "\033[5;5~",    AppKey::IGNORE,    AppCursor::IGNORE},
	{ KeyID::PRIOR,         Mask{Mod::SHIFT},                 "\033[5;2~",    AppKey::IGNORE,    AppCursor::IGNORE},
	{ KeyID::PRIOR,         Mask{Mod::ANY},                   "\033[5~",      AppKey::IGNORE,    AppCursor::IGNORE},
	{ KeyID::NEXT,          Mask{Mod::CONTROL},               "\033[6;5~",    AppKey::IGNORE,    AppCursor::IGNORE},
	{ KeyID::NEXT,          Mask{Mod::SHIFT},                 "\033[6;2~",    AppKey::IGNORE,    AppCursor::IGNORE},
	{ KeyID::NEXT,          Mask{Mod::ANY},                   "\033[6~",      AppKey::IGNORE,    AppCursor::IGNORE},
	{ KeyID::F1,            Mask{Mod::NONE},                  "\033OP" ,      AppKey::IGNORE,    AppCursor::IGNORE},
	{ KeyID::F1, /* F13 */  Mask{Mod::SHIFT},                 "\033[1;2P",    AppKey::IGNORE,    AppCursor::IGNORE},
	{ KeyID::F1, /* F25 */  Mask{Mod::CONTROL},               "\033[1;5P",    AppKey::IGNORE,    AppCursor::IGNORE},
	{ KeyID::F1, /* F37 */  Mask{Mod::MOD4},                  "\033[1;6P",    AppKey::IGNORE,    AppCursor::IGNORE},
	{ KeyID::F1, /* F49 */  Mask{Mod::MOD1},                  "\033[1;3P",    AppKey::IGNORE,    AppCursor::IGNORE},
	{ KeyID::F1, /* F61 */  Mask{Mod::MOD3},                  "\033[1;4P",    AppKey::IGNORE,    AppCursor::IGNORE},
	{ KeyID::F2,            Mask{Mod::NONE},                  "\033OQ" ,      AppKey::IGNORE,    AppCursor::IGNORE},
	{ KeyID::F2, /* F14 */  Mask{Mod::SHIFT},                 "\033[1;2Q",    AppKey::IGNORE,    AppCursor::IGNORE},
	{ KeyID::F2, /* F26 */  Mask{Mod::CONTROL},               "\033[1;5Q",    AppKey::IGNORE,    AppCursor::IGNORE},
	{ KeyID::F2, /* F38 */  Mask{Mod::MOD4},                  "\033[1;6Q",    AppKey::IGNORE,    AppCursor::IGNORE},
	{ KeyID::F2, /* F50 */  Mask{Mod::MOD1},                  "\033[1;3Q",    AppKey::IGNORE,    AppCursor::IGNORE},
	{ KeyID::F2, /* F62 */  Mask{Mod::MOD3},                  "\033[1;4Q",    AppKey::IGNORE,    AppCursor::IGNORE},
	{ KeyID::F3,            Mask{Mod::NONE},                  "\033OR" ,      AppKey::IGNORE,    AppCursor::IGNORE},
	{ KeyID::F3, /* F15 */  Mask{Mod::SHIFT},                 "\033[1;2R",    AppKey::IGNORE,    AppCursor::IGNORE},
	{ KeyID::F3, /* F27 */  Mask{Mod::CONTROL},               "\033[1;5R",    AppKey::IGNORE,    AppCursor::IGNORE},
	{ KeyID::F3, /* F39 */  Mask{Mod::MOD4},                  "\033[1;6R",    AppKey::IGNORE,    AppCursor::IGNORE},
	{ KeyID::F3, /* F51 */  Mask{Mod::MOD1},                  "\033[1;3R",    AppKey::IGNORE,    AppCursor::IGNORE},
	{ KeyID::F3, /* F63 */  Mask{Mod::MOD3},                  "\033[1;4R",    AppKey::IGNORE,    AppCursor::IGNORE},
	{ KeyID::F4,            Mask{Mod::NONE},                  "\033OS" ,      AppKey::IGNORE,    AppCursor::IGNORE},
	{ KeyID::F4, /* F16 */  Mask{Mod::SHIFT},                 "\033[1;2S",    AppKey::IGNORE,    AppCursor::IGNORE},
	{ KeyID::F4, /* F28 */  Mask{Mod::CONTROL},               "\033[1;5S",    AppKey::IGNORE,    AppCursor::IGNORE},
	{ KeyID::F4, /* F40 */  Mask{Mod::MOD4},                  "\033[1;6S",    AppKey::IGNORE,    AppCursor::IGNORE},
	{ KeyID::F4, /* F52 */  Mask{Mod::MOD1},                  "\033[1;3S",    AppKey::IGNORE,    AppCursor::IGNORE},
	{ KeyID::F5,            Mask{Mod::NONE},                  "\033[15~",     AppKey::IGNORE,    AppCursor::IGNORE},
	{ KeyID::F5, /* F17 */  Mask{Mod::SHIFT},                 "\033[15;2~",   AppKey::IGNORE,    AppCursor::IGNORE},
	{ KeyID::F5, /* F29 */  Mask{Mod::CONTROL},               "\033[15;5~",   AppKey::IGNORE,    AppCursor::IGNORE},
	{ KeyID::F5, /* F41 */  Mask{Mod::MOD4},                  "\033[15;6~",   AppKey::IGNORE,    AppCursor::IGNORE},
	{ KeyID::F5, /* F53 */  Mask{Mod::MOD1},                  "\033[15;3~",   AppKey::IGNORE,    AppCursor::IGNORE},
	{ KeyID::F6,            Mask{Mod::NONE},                  "\033[17~",     AppKey::IGNORE,    AppCursor::IGNORE},
	{ KeyID::F6, /* F18 */  Mask{Mod::SHIFT},                 "\033[17;2~",   AppKey::IGNORE,    AppCursor::IGNORE},
	{ KeyID::F6, /* F30 */  Mask{Mod::CONTROL},               "\033[17;5~",   AppKey::IGNORE,    AppCursor::IGNORE},
	{ KeyID::F6, /* F42 */  Mask{Mod::MOD4},                  "\033[17;6~",   AppKey::IGNORE,    AppCursor::IGNORE},
	{ KeyID::F6, /* F54 */  Mask{Mod::MOD1},                  "\033[17;3~",   AppKey::IGNORE,    AppCursor::IGNORE},
	{ KeyID::F7,            Mask{Mod::NONE},                  "\033[18~",     AppKey::IGNORE,    AppCursor::IGNORE},
	{ KeyID::F7, /* F19 */  Mask{Mod::SHIFT},                 "\033[18;2~",   AppKey::IGNORE,    AppCursor::IGNORE},
	{ KeyID::F7, /* F31 */  Mask{Mod::CONTROL},               "\033[18;5~",   AppKey::IGNORE,    AppCursor::IGNORE},
	{ KeyID::F7, /* F43 */  Mask{Mod::MOD4},                  "\033[18;6~",   AppKey::IGNORE,    AppCursor::IGNORE},
	{ KeyID::F7, /* F55 */  Mask{Mod::MOD1},                  "\033[18;3~",   AppKey::IGNORE,    AppCursor::IGNORE},
	{ KeyID::F8,            Mask{Mod::NONE},                  "\033[19~",     AppKey::IGNORE,    AppCursor::IGNORE},
	{ KeyID::F8, /* F20 */  Mask{Mod::SHIFT},                 "\033[19;2~",   AppKey::IGNORE,    AppCursor::IGNORE},
	{ KeyID::F8, /* F32 */  Mask{Mod::CONTROL},               "\033[19;5~",   AppKey::IGNORE,    AppCursor::IGNORE},
	{ KeyID::F8, /* F44 */  Mask{Mod::MOD4},                  "\033[19;6~",   AppKey::IGNORE,    AppCursor::IGNORE},
	{ KeyID::F8, /* F56 */  Mask{Mod::MOD1},                  "\033[19;3~",   AppKey::IGNORE,    AppCursor::IGNORE},
	{ KeyID::F9,            Mask{Mod::NONE},                  "\033[20~",     AppKey::IGNORE,    AppCursor::IGNORE},
	{ KeyID::F9, /* F21 */  Mask{Mod::SHIFT},                 "\033[20;2~",   AppKey::IGNORE,    AppCursor::IGNORE},
	{ KeyID::F9, /* F33 */  Mask{Mod::CONTROL},               "\033[20;5~",   AppKey::IGNORE,    AppCursor::IGNORE},
	{ KeyID::F9, /* F45 */  Mask{Mod::MOD4},                  "\033[20;6~",   AppKey::IGNORE,    AppCursor::IGNORE},
	{ KeyID::F9, /* F57 */  Mask{Mod::MOD1},                  "\033[20;3~",   AppKey::IGNORE,    AppCursor::IGNORE},
	{ KeyID::F10,           Mask{Mod::NONE},                  "\033[21~",     AppKey::IGNORE,    AppCursor::IGNORE},
	{ KeyID::F10, /* F22 */ Mask{Mod::SHIFT},                 "\033[21;2~",   AppKey::IGNORE,    AppCursor::IGNORE},
	{ KeyID::F10, /* F34 */ Mask{Mod::CONTROL},               "\033[21;5~",   AppKey::IGNORE,    AppCursor::IGNORE},
	{ KeyID::F10, /* F46 */ Mask{Mod::MOD4},                  "\033[21;6~",   AppKey::IGNORE,    AppCursor::IGNORE},
	{ KeyID::F10, /* F58 */ Mask{Mod::MOD1},                  "\033[21;3~",   AppKey::IGNORE,    AppCursor::IGNORE},
	{ KeyID::F11,           Mask{Mod::NONE},                  "\033[23~",     AppKey::IGNORE,    AppCursor::IGNORE},
	{ KeyID::F11, /* F23 */ Mask{Mod::SHIFT},                 "\033[23;2~",   AppKey::IGNORE,    AppCursor::IGNORE},
	{ KeyID::F11, /* F35 */ Mask{Mod::CONTROL},               "\033[23;5~",   AppKey::IGNORE,    AppCursor::IGNORE},
	{ KeyID::F11, /* F47 */ Mask{Mod::MOD4},                  "\033[23;6~",   AppKey::IGNORE,    AppCursor::IGNORE},
	{ KeyID::F11, /* F59 */ Mask{Mod::MOD1},                  "\033[23;3~",   AppKey::IGNORE,    AppCursor::IGNORE},
	{ KeyID::F12,           Mask{Mod::NONE},                  "\033[24~",     AppKey::IGNORE,    AppCursor::IGNORE},
	{ KeyID::F12, /* F24 */ Mask{Mod::SHIFT},                 "\033[24;2~",   AppKey::IGNORE,    AppCursor::IGNORE},
	{ KeyID::F12, /* F36 */ Mask{Mod::CONTROL},               "\033[24;5~",   AppKey::IGNORE,    AppCursor::IGNORE},
	{ KeyID::F12, /* F48 */ Mask{Mod::MOD4},                  "\033[24;6~",   AppKey::IGNORE,    AppCursor::IGNORE},
	{ KeyID::F12, /* F60 */ Mask{Mod::MOD1},                  "\033[24;3~",   AppKey::IGNORE,    AppCursor::IGNORE},
	{ KeyID::F13,           Mask{Mod::NONE},                  "\033[1;2P",    AppKey::IGNORE,    AppCursor::IGNORE},
	{ KeyID::F14,           Mask{Mod::NONE},                  "\033[1;2Q",    AppKey::IGNORE,    AppCursor::IGNORE},
	{ KeyID::F15,           Mask{Mod::NONE},                  "\033[1;2R",    AppKey::IGNORE,    AppCursor::IGNORE},
	{ KeyID::F16,           Mask{Mod::NONE},                  "\033[1;2S",    AppKey::IGNORE,    AppCursor::IGNORE},
	{ KeyID::F17,           Mask{Mod::NONE},                  "\033[15;2~",   AppKey::IGNORE,    AppCursor::IGNORE},
	{ KeyID::F18,           Mask{Mod::NONE},                  "\033[17;2~",   AppKey::IGNORE,    AppCursor::IGNORE},
	{ KeyID::F19,           Mask{Mod::NONE},                  "\033[18;2~",   AppKey::IGNORE,    AppCursor::IGNORE},
	{ KeyID::F20,           Mask{Mod::NONE},                  "\033[19;2~",   AppKey::IGNORE,    AppCursor::IGNORE},
	{ KeyID::F21,           Mask{Mod::NONE},                  "\033[20;2~",   AppKey::IGNORE,    AppCursor::IGNORE},
	{ KeyID::F22,           Mask{Mod::NONE},                  "\033[21;2~",   AppKey::IGNORE,    AppCursor::IGNORE},
	{ KeyID::F23,           Mask{Mod::NONE},                  "\033[23;2~",   AppKey::IGNORE,    AppCursor::IGNORE},
	{ KeyID::F24,           Mask{Mod::NONE},                  "\033[24;2~",   AppKey::IGNORE,    AppCursor::IGNORE},
	{ KeyID::F25,           Mask{Mod::NONE},                  "\033[1;5P",    AppKey::IGNORE,    AppCursor::IGNORE},
	{ KeyID::F26,           Mask{Mod::NONE},                  "\033[1;5Q",    AppKey::IGNORE,    AppCursor::IGNORE},
	{ KeyID::F27,           Mask{Mod::NONE},                  "\033[1;5R",    AppKey::IGNORE,    AppCursor::IGNORE},
	{ KeyID::F28,           Mask{Mod::NONE},                  "\033[1;5S",    AppKey::IGNORE,    AppCursor::IGNORE},
	{ KeyID::F29,           Mask{Mod::NONE},                  "\033[15;5~",   AppKey::IGNORE,    AppCursor::IGNORE},
	{ KeyID::F30,           Mask{Mod::NONE},                  "\033[17;5~",   AppKey::IGNORE,    AppCursor::IGNORE},
	{ KeyID::F31,           Mask{Mod::NONE},                  "\033[18;5~",   AppKey::IGNORE,    AppCursor::IGNORE},
	{ KeyID::F32,           Mask{Mod::NONE},                  "\033[19;5~",   AppKey::IGNORE,    AppCursor::IGNORE},
	{ KeyID::F33,           Mask{Mod::NONE},                  "\033[20;5~",   AppKey::IGNORE,    AppCursor::IGNORE},
	{ KeyID::F34,           Mask{Mod::NONE},                  "\033[21;5~",   AppKey::IGNORE,    AppCursor::IGNORE},
	{ KeyID::F35,           Mask{Mod::NONE},                  "\033[23;5~",   AppKey::IGNORE,    AppCursor::IGNORE},
}};

/// List of printable ASCII characters.
/**
 * This is used to estimate the advance width of single wide characters.
 **/
inline constexpr std::string_view ASCII_PRINTABLE{
	" !\"#$%&'()*+,-./0123456789:;<=>?"
	"@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_"
	"`abcdefghijklmnopqrstuvwxyz{|}~"
};

/// Input modifiers to influence the selection type.
/**
 * Use the same masks as usual.
 * Button1Mask is always unset, to make masks match between ButtonPress.
 * ButtonRelease and MotionNotify.
 * If no match is found, regular selection is used.
 **/
constexpr std::array<std::pair<Selection::Mode, xpp::InputMask>, 2> SEL_MASKS = {
	std::pair{Selection::Mode::RECT_RANGE, xpp::InputMask{xpp::InputModifier::CONTROL}},
	std::pair{Selection::Mode::LINE_RANGE, xpp::InputMask{xpp::InputModifier::CONTROL, xpp::InputModifier::MOD1}}
};

/// Modifier used for alternative selection behaviour.
/**
 * This modifier is currently used for two things:
 *
 * - a new selection is started and a double-click on a word delimiter with
 *   the modifier pressed occurs. Instead of regular word extension, the
 *   selection will be expanded forward until the very same word separator is
 *   encountered. Further single clicks while holding the modified will extend
 *   the selection by one field until the next very same word separator is
 *   found.
 * - a word-extended selection exists and a single-click with the modifier
 *   pressed occurs. The word selection will be further extended using
 *   WORD_DELIMITERS. If you click on the selection itself then extension is
 *   performed in both directions until another delimiter is found. If you
 *   click left/above the current selection then the selection will be
 *   extended to the left. If you click right/below the current selection
 *   then the selection will be extended to the right.
 **/
constexpr xpp::InputMask SEL_ALT_MOD{xpp::InputModifier::MOD1};

/// A list of URL schemes that will be identified as URLs and expanded in the context of word snap expansion.
/**
 * When double-clicking on such a URI scheme, then the complete URL will be
 * expanded, if possible.
 **/
inline constexpr std::string_view SEL_URI_SCHEMES[] = {"http", "https", "ftp", "git", "socks"};

// see implementation file
std::vector<MouseShortcut> get_mouse_shortcuts(Nst &nst);

// see implementation file
std::vector<KbdShortcut> get_kbd_shortcuts(Nst &nst);

} // end ns
openSUSE Build Service is sponsored by